aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-04-07 22:29:28 -0700
committerGitHub <noreply@github.com>2021-04-07 22:29:28 -0700
commitd4f61f9842da9025a4eb57e7a0fbb3298a4c01f6 (patch)
treecf9bdfa6cf51b7446df1da43bb37b76728ced769 /src
parent341dc03b638bc75bb8215dd2ad22231ebe106139 (diff)
parent759591577518fcaf03fb90efca67d896d0806458 (diff)
downloadzig-d4f61f9842da9025a4eb57e7a0fbb3298a4c01f6.tar.gz
zig-d4f61f9842da9025a4eb57e7a0fbb3298a4c01f6.zip
Merge pull request #8449 from ziglang/stage2-enums
stage2: implement simple enums
Diffstat (limited to 'src')
-rw-r--r--src/AstGen.zig303
-rw-r--r--src/BuiltinFn.zig2
-rw-r--r--src/Compilation.zig55
-rw-r--r--src/Module.zig242
-rw-r--r--src/Sema.zig589
-rw-r--r--src/codegen/c.zig60
-rw-r--r--src/type.zig1939
-rw-r--r--src/value.zig837
-rw-r--r--src/zir.zig95
9 files changed, 1587 insertions, 2535 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig
index a059ab04cb..8ee27b1658 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -28,8 +28,6 @@ const BuiltinFn = @import("BuiltinFn.zig");
instructions: std.MultiArrayList(zir.Inst) = .{},
string_bytes: ArrayListUnmanaged(u8) = .{},
extra: ArrayListUnmanaged(u32) = .{},
-decl_map: std.StringArrayHashMapUnmanaged(void) = .{},
-decls: ArrayListUnmanaged(*Decl) = .{},
/// The end of special indexes. `zir.Inst.Ref` subtracts against this number to convert
/// to `zir.Inst.Index`. The default here is correct if there are 0 parameters.
ref_start_index: u32 = zir.Inst.Ref.typed_value_map.len,
@@ -110,8 +108,6 @@ pub fn deinit(astgen: *AstGen) void {
astgen.instructions.deinit(gpa);
astgen.extra.deinit(gpa);
astgen.string_bytes.deinit(gpa);
- astgen.decl_map.deinit(gpa);
- astgen.decls.deinit(gpa);
}
pub const ResultLoc = union(enum) {
@@ -1183,13 +1179,6 @@ fn blockExprStmts(
// in the above while loop.
const zir_tags = gz.astgen.instructions.items(.tag);
switch (zir_tags[inst]) {
- .@"const" => {
- const tv = gz.astgen.instructions.items(.data)[inst].@"const";
- break :b switch (tv.ty.zigTypeTag()) {
- .NoReturn, .Void => true,
- else => false,
- };
- },
// For some instructions, swap in a slightly different ZIR tag
// so we can avoid a separate ensure_result_used instruction.
.call_none_chkused => unreachable,
@@ -1257,6 +1246,8 @@ fn blockExprStmts(
.fn_type_cc,
.fn_type_cc_var_args,
.int,
+ .float,
+ .float128,
.intcast,
.int_type,
.is_non_null,
@@ -1334,7 +1325,10 @@ fn blockExprStmts(
.struct_decl_extern,
.union_decl,
.enum_decl,
+ .enum_decl_nonexhaustive,
.opaque_decl,
+ .int_to_enum,
+ .enum_to_int,
=> break :b false,
// ZIR instructions that are always either `noreturn` or `void`.
@@ -1490,7 +1484,7 @@ fn varDecl(
init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node);
init_scope.rl_ty_inst = type_inst;
} else {
- const alloc = try init_scope.addUnNode(.alloc_inferred, undefined, node);
+ const alloc = try init_scope.addNode(.alloc_inferred, node);
resolve_inferred_alloc = alloc;
init_scope.rl_ptr = alloc;
}
@@ -1565,7 +1559,7 @@ fn varDecl(
const alloc = try gz.addUnNode(.alloc_mut, type_inst, node);
break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
} else a: {
- const alloc = try gz.addUnNode(.alloc_inferred_mut, undefined, node);
+ const alloc = try gz.addNode(.alloc_inferred_mut, node);
resolve_inferred_alloc = alloc;
break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } };
};
@@ -1823,15 +1817,18 @@ fn containerDecl(
defer bit_bag.deinit(gpa);
var cur_bit_bag: u32 = 0;
- var member_index: usize = 0;
- while (true) {
- const member_node = container_decl.ast.members[member_index];
+ var field_index: usize = 0;
+ for (container_decl.ast.members) |member_node| {
const member = switch (node_tags[member_node]) {
.container_field_init => tree.containerFieldInit(member_node),
.container_field_align => tree.containerFieldAlign(member_node),
.container_field => tree.containerField(member_node),
- else => unreachable,
+ else => continue,
};
+ if (field_index % 16 == 0 and field_index != 0) {
+ try bit_bag.append(gpa, cur_bit_bag);
+ cur_bit_bag = 0;
+ }
if (member.comptime_token) |comptime_token| {
return mod.failTok(scope, comptime_token, "TODO implement comptime struct fields", .{});
}
@@ -1858,17 +1855,9 @@ fn containerDecl(
fields_data.appendAssumeCapacity(@enumToInt(default_inst));
}
- member_index += 1;
- if (member_index < container_decl.ast.members.len) {
- if (member_index % 16 == 0) {
- try bit_bag.append(gpa, cur_bit_bag);
- cur_bit_bag = 0;
- }
- } else {
- break;
- }
+ field_index += 1;
}
- const empty_slot_count = 16 - ((member_index - 1) % 16);
+ const empty_slot_count = 16 - (field_index % 16);
cur_bit_bag >>= @intCast(u5, empty_slot_count * 2);
const result = try gz.addPlNode(tag, node, zir.Inst.StructDecl{
@@ -1885,7 +1874,172 @@ fn containerDecl(
return mod.failTok(scope, container_decl.ast.main_token, "TODO AstGen for union decl", .{});
},
.keyword_enum => {
- return mod.failTok(scope, container_decl.ast.main_token, "TODO AstGen for enum decl", .{});
+ if (container_decl.layout_token) |t| {
+ return mod.failTok(scope, t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{});
+ }
+ // Count total fields as well as how many have explicitly provided tag values.
+ const counts = blk: {
+ var values: usize = 0;
+ var total_fields: usize = 0;
+ var decls: usize = 0;
+ var nonexhaustive_node: ast.Node.Index = 0;
+ for (container_decl.ast.members) |member_node| {
+ const member = switch (node_tags[member_node]) {
+ .container_field_init => tree.containerFieldInit(member_node),
+ .container_field_align => tree.containerFieldAlign(member_node),
+ .container_field => tree.containerField(member_node),
+ else => {
+ decls += 1;
+ continue;
+ },
+ };
+ if (member.comptime_token) |comptime_token| {
+ return mod.failTok(scope, comptime_token, "enum fields cannot be marked comptime", .{});
+ }
+ if (member.ast.type_expr != 0) {
+ return mod.failNode(scope, member.ast.type_expr, "enum fields do not have types", .{});
+ }
+ // Alignment expressions in enums are caught by the parser.
+ assert(member.ast.align_expr == 0);
+
+ const name_token = member.ast.name_token;
+ if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
+ if (nonexhaustive_node != 0) {
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ scope,
+ gz.nodeSrcLoc(member_node),
+ "redundant non-exhaustive enum mark",
+ .{},
+ );
+ errdefer msg.destroy(gpa);
+ const other_src = gz.nodeSrcLoc(nonexhaustive_node);
+ try mod.errNote(scope, other_src, msg, "other mark here", .{});
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(scope, msg);
+ }
+ nonexhaustive_node = member_node;
+ if (member.ast.value_expr != 0) {
+ return mod.failNode(scope, member.ast.value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{});
+ }
+ continue;
+ }
+ total_fields += 1;
+ if (member.ast.value_expr != 0) {
+ values += 1;
+ }
+ }
+ break :blk .{
+ .total_fields = total_fields,
+ .values = values,
+ .decls = decls,
+ .nonexhaustive_node = nonexhaustive_node,
+ };
+ };
+ if (counts.total_fields == 0) {
+ // One can construct an enum with no tags, and it functions the same as `noreturn`. But
+ // this is only useful for generic code; when explicitly using `enum {}` syntax, there
+ // must be at least one tag.
+ return mod.failNode(scope, node, "enum declarations must have at least one tag", .{});
+ }
+ if (counts.nonexhaustive_node != 0 and arg_inst == .none) {
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ scope,
+ gz.nodeSrcLoc(node),
+ "non-exhaustive enum missing integer tag type",
+ .{},
+ );
+ errdefer msg.destroy(gpa);
+ const other_src = gz.nodeSrcLoc(counts.nonexhaustive_node);
+ try mod.errNote(scope, other_src, msg, "marked non-exhaustive here", .{});
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(scope, msg);
+ }
+ if (counts.values == 0 and counts.decls == 0 and arg_inst == .none) {
+ // No explicitly provided tag values and no top level declarations! In this case,
+ // we can construct the enum type in AstGen and it will be correctly shared by all
+ // generic function instantiations and comptime function calls.
+ var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
+ errdefer new_decl_arena.deinit();
+ const arena = &new_decl_arena.allocator;
+
+ var fields_map: std.StringArrayHashMapUnmanaged(void) = .{};
+ try fields_map.ensureCapacity(arena, counts.total_fields);
+ for (container_decl.ast.members) |member_node| {
+ if (member_node == counts.nonexhaustive_node)
+ continue;
+ const member = switch (node_tags[member_node]) {
+ .container_field_init => tree.containerFieldInit(member_node),
+ .container_field_align => tree.containerFieldAlign(member_node),
+ .container_field => tree.containerField(member_node),
+ else => unreachable, // We checked earlier.
+ };
+ const name_token = member.ast.name_token;
+ const tag_name = try mod.identifierTokenStringTreeArena(
+ scope,
+ name_token,
+ tree,
+ arena,
+ );
+ const gop = fields_map.getOrPutAssumeCapacity(tag_name);
+ if (gop.found_existing) {
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ scope,
+ gz.tokSrcLoc(name_token),
+ "duplicate enum tag",
+ .{},
+ );
+ errdefer msg.destroy(gpa);
+ // Iterate to find the other tag. We don't eagerly store it in a hash
+ // map because in the hot path there will be no compile error and we
+ // don't need to waste time with a hash map.
+ const bad_node = for (container_decl.ast.members) |other_member_node| {
+ const other_member = switch (node_tags[other_member_node]) {
+ .container_field_init => tree.containerFieldInit(other_member_node),
+ .container_field_align => tree.containerFieldAlign(other_member_node),
+ .container_field => tree.containerField(other_member_node),
+ else => unreachable, // We checked earlier.
+ };
+ const other_tag_name = try mod.identifierTokenStringTreeArena(
+ scope,
+ other_member.ast.name_token,
+ tree,
+ arena,
+ );
+ if (mem.eql(u8, tag_name, other_tag_name))
+ break other_member_node;
+ } else unreachable;
+ const other_src = gz.nodeSrcLoc(bad_node);
+ try mod.errNote(scope, other_src, msg, "other tag here", .{});
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(scope, msg);
+ }
+ }
+ const enum_simple = try arena.create(Module.EnumSimple);
+ enum_simple.* = .{
+ .owner_decl = astgen.decl,
+ .node_offset = astgen.decl.nodeIndexToRelative(node),
+ .fields = fields_map,
+ };
+ const enum_ty = try Type.Tag.enum_simple.create(arena, enum_simple);
+ const enum_val = try Value.Tag.ty.create(arena, enum_ty);
+ const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
+ .ty = Type.initTag(.type),
+ .val = enum_val,
+ });
+ const decl_index = try mod.declareDeclDependency(astgen.decl, new_decl);
+ const result = try gz.addDecl(.decl_val, decl_index, node);
+ return rvalue(gz, scope, rl, result, node);
+ }
+ // In this case we must generate ZIR code for the tag values, similar to
+ // how structs are handled above. The new anonymous Decl will be created in
+ // Sema, not AstGen.
+ return mod.failNode(scope, node, "TODO AstGen for enum decl with decls or explicitly provided field values", .{});
},
.keyword_opaque => {
const result = try gz.addNode(.opaque_decl, node);
@@ -1901,11 +2055,11 @@ fn errorSetDecl(
rl: ResultLoc,
node: ast.Node.Index,
) InnerError!zir.Inst.Ref {
- const mod = gz.astgen.mod;
+ const astgen = gz.astgen;
+ const mod = astgen.mod;
const tree = gz.tree();
const main_tokens = tree.nodes.items(.main_token);
const token_tags = tree.tokens.items(.tag);
- const arena = gz.astgen.arena;
// Count how many fields there are.
const error_token = main_tokens[node];
@@ -1922,6 +2076,11 @@ fn errorSetDecl(
} else unreachable; // TODO should not need else unreachable here
};
+ const gpa = mod.gpa;
+ var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
+ errdefer new_decl_arena.deinit();
+ const arena = &new_decl_arena.allocator;
+
const fields = try arena.alloc([]const u8, count);
{
var tok_i = error_token + 2;
@@ -1930,7 +2089,7 @@ fn errorSetDecl(
switch (token_tags[tok_i]) {
.doc_comment, .comma => {},
.identifier => {
- fields[field_i] = try mod.identifierTokenString(scope, tok_i);
+ fields[field_i] = try mod.identifierTokenStringTreeArena(scope, tok_i, tree, arena);
field_i += 1;
},
.r_brace => break,
@@ -1940,18 +2099,19 @@ fn errorSetDecl(
}
const error_set = try arena.create(Module.ErrorSet);
error_set.* = .{
- .owner_decl = gz.astgen.decl,
- .node_offset = gz.astgen.decl.nodeIndexToRelative(node),
+ .owner_decl = astgen.decl,
+ .node_offset = astgen.decl.nodeIndexToRelative(node),
.names_ptr = fields.ptr,
.names_len = @intCast(u32, fields.len),
};
const error_set_ty = try Type.Tag.error_set.create(arena, error_set);
- const typed_value = try arena.create(TypedValue);
- typed_value.* = .{
+ const error_set_val = try Value.Tag.ty.create(arena, error_set_ty);
+ const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{
.ty = Type.initTag(.type),
- .val = try Value.Tag.ty.create(arena, error_set_ty),
- };
- const result = try gz.addConst(typed_value);
+ .val = error_set_val,
+ });
+ const decl_index = try mod.declareDeclDependency(astgen.decl, new_decl);
+ const result = try gz.addDecl(.decl_val, decl_index, node);
return rvalue(gz, scope, rl, result, node);
}
@@ -3196,8 +3356,13 @@ fn switchExpr(
switch (strat.tag) {
.break_operand => {
// Switch expressions return `true` for `nodeMayNeedMemoryLocation` thus
- // this is always true.
- assert(strat.elide_store_to_block_ptr_instructions);
+ // `elide_store_to_block_ptr_instructions` will either be true,
+ // or all prongs are noreturn.
+ if (!strat.elide_store_to_block_ptr_instructions) {
+ astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
+ astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
+ return astgen.indexToRef(switch_block);
+ }
// There will necessarily be a store_to_block_ptr for
// all prongs, except for prongs that ended with a noreturn instruction.
@@ -3426,7 +3591,8 @@ fn identifier(
const tracy = trace(@src());
defer tracy.end();
- const mod = gz.astgen.mod;
+ const astgen = gz.astgen;
+ const mod = astgen.mod;
const tree = gz.tree();
const main_tokens = tree.nodes.items(.main_token);
@@ -3459,7 +3625,7 @@ fn identifier(
const result = try gz.add(.{
.tag = .int_type,
.data = .{ .int_type = .{
- .src_node = gz.astgen.decl.nodeIndexToRelative(ident),
+ .src_node = astgen.decl.nodeIndexToRelative(ident),
.signedness = signedness,
.bit_count = bit_count,
} },
@@ -3497,13 +3663,13 @@ fn identifier(
};
}
- const gop = try gz.astgen.decl_map.getOrPut(mod.gpa, ident_name);
- if (!gop.found_existing) {
- const decl = mod.lookupDeclName(scope, ident_name) orelse
- return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name});
- try gz.astgen.decls.append(mod.gpa, decl);
- }
- const decl_index = @intCast(u32, gop.index);
+ const decl = mod.lookupDeclName(scope, ident_name) orelse {
+ // TODO insert a "dependency on the non-existence of a decl" here to make this
+ // compile error go away when the decl is introduced. This data should be in a global
+ // sparse map since it is only relevant when a compile error occurs.
+ return mod.failNode(scope, ident, "use of undeclared identifier '{s}'", .{ident_name});
+ };
+ const decl_index = try mod.declareDeclDependency(astgen.decl, decl);
switch (rl) {
.ref, .none_or_ref => return gz.addDecl(.decl_ref, decl_index, ident),
else => return rvalue(gz, scope, rl, try gz.addDecl(.decl_val, decl_index, ident), ident),
@@ -3638,12 +3804,23 @@ fn floatLiteral(
const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
error.InvalidCharacter => unreachable, // validated by tokenizer
};
- const typed_value = try arena.create(TypedValue);
- typed_value.* = .{
- .ty = Type.initTag(.comptime_float),
- .val = try Value.Tag.float_128.create(arena, float_number),
- };
- const result = try gz.addConst(typed_value);
+ // If the value fits into a f32 without losing any precision, store it that way.
+ @setFloatMode(.Strict);
+ const smaller_float = @floatCast(f32, float_number);
+ const bigger_again: f128 = smaller_float;
+ if (bigger_again == float_number) {
+ const result = try gz.addFloat(smaller_float, node);
+ return rvalue(gz, scope, rl, result, node);
+ }
+ // We need to use 128 bits. Break the float into 4 u32 values so we can
+ // put it into the `extra` array.
+ const int_bits = @bitCast(u128, float_number);
+ const result = try gz.addPlNode(.float128, node, zir.Inst.Float128{
+ .piece0 = @truncate(u32, int_bits),
+ .piece1 = @truncate(u32, int_bits >> 32),
+ .piece2 = @truncate(u32, int_bits >> 64),
+ .piece3 = @truncate(u32, int_bits >> 96),
+ });
return rvalue(gz, scope, rl, result, node);
}
@@ -3955,6 +4132,20 @@ fn builtinCall(
.bit_cast => return bitCast(gz, scope, rl, node, params[0], params[1]),
.TypeOf => return typeOf(gz, scope, rl, node, params),
+ .int_to_enum => {
+ const result = try gz.addPlNode(.int_to_enum, node, zir.Inst.Bin{
+ .lhs = try typeExpr(gz, scope, params[0]),
+ .rhs = try expr(gz, scope, .none, params[1]),
+ });
+ return rvalue(gz, scope, rl, result, node);
+ },
+
+ .enum_to_int => {
+ const operand = try expr(gz, scope, .none, params[0]);
+ const result = try gz.addUnNode(.enum_to_int, operand, node);
+ return rvalue(gz, scope, rl, result, node);
+ },
+
.add_with_overflow,
.align_cast,
.align_of,
@@ -3981,7 +4172,6 @@ fn builtinCall(
.div_floor,
.div_trunc,
.embed_file,
- .enum_to_int,
.error_name,
.error_return_trace,
.err_set_cast,
@@ -3991,7 +4181,6 @@ fn builtinCall(
.float_to_int,
.has_decl,
.has_field,
- .int_to_enum,
.int_to_float,
.int_to_ptr,
.memcpy,
diff --git a/src/BuiltinFn.zig b/src/BuiltinFn.zig
index 1710169dc7..a5d15554af 100644
--- a/src/BuiltinFn.zig
+++ b/src/BuiltinFn.zig
@@ -484,7 +484,7 @@ pub const list = list: {
"@intToEnum",
.{
.tag = .int_to_enum,
- .param_count = 1,
+ .param_count = 2,
},
},
.{
diff --git a/src/Compilation.zig b/src/Compilation.zig
index b086e513b9..080d4bddaa 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -1346,9 +1346,9 @@ pub fn update(self: *Compilation) !void {
module.generation += 1;
// TODO Detect which source files changed.
- // Until then we simulate a full cache miss. Source files could have been loaded for any reason;
- // to force a refresh we unload now.
- module.root_scope.unload(module.gpa);
+ // Until then we simulate a full cache miss. Source files could have been loaded
+ // for any reason; to force a refresh we unload now.
+ module.unloadFile(module.root_scope);
module.failed_root_src_file = null;
module.analyzeContainer(&module.root_scope.root_container) catch |err| switch (err) {
error.AnalysisFail => {
@@ -1362,7 +1362,7 @@ pub fn update(self: *Compilation) !void {
// TODO only analyze imports if they are still referenced
for (module.import_table.items()) |entry| {
- entry.value.unload(module.gpa);
+ module.unloadFile(entry.value);
module.analyzeContainer(&entry.value.root_container) catch |err| switch (err) {
error.AnalysisFail => {
assert(self.totalErrorCount() != 0);
@@ -1377,14 +1377,17 @@ pub fn update(self: *Compilation) !void {
if (!use_stage1) {
if (self.bin_file.options.module) |module| {
- // Process the deletion set.
- while (module.deletion_set.popOrNull()) |decl| {
- if (decl.dependants.items().len != 0) {
- decl.deletion_flag = false;
- continue;
- }
- try module.deleteDecl(decl);
+ // Process the deletion set. We use a while loop here because the
+ // deletion set may grow as we call `deleteDecl` within this loop,
+ // and more unreferenced Decls are revealed.
+ var entry_i: usize = 0;
+ while (entry_i < module.deletion_set.entries.items.len) : (entry_i += 1) {
+ const decl = module.deletion_set.entries.items[entry_i].key;
+ assert(decl.deletion_flag);
+ assert(decl.dependants.items().len == 0);
+ try module.deleteDecl(decl, null);
}
+ module.deletion_set.shrinkRetainingCapacity(0);
}
}
@@ -1429,11 +1432,25 @@ pub fn totalErrorCount(self: *Compilation) usize {
var total: usize = self.failed_c_objects.items().len;
if (self.bin_file.options.module) |module| {
- total += module.failed_decls.count() +
- module.emit_h_failed_decls.count() +
- module.failed_exports.items().len +
+ total += module.failed_exports.items().len +
module.failed_files.items().len +
@boolToInt(module.failed_root_src_file != null);
+ // Skip errors for Decls within files that failed parsing.
+ // When a parse error is introduced, we keep all the semantic analysis for
+ // the previous parse success, including compile errors, but we cannot
+ // emit them until the file succeeds parsing.
+ for (module.failed_decls.items()) |entry| {
+ if (entry.key.container.file_scope.status == .unloaded_parse_failure) {
+ continue;
+ }
+ total += 1;
+ }
+ for (module.emit_h_failed_decls.items()) |entry| {
+ if (entry.key.container.file_scope.status == .unloaded_parse_failure) {
+ continue;
+ }
+ total += 1;
+ }
}
// The "no entry point found" error only counts if there are no other errors.
@@ -1480,9 +1497,19 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
try AllErrors.add(module, &arena, &errors, entry.value.*);
}
for (module.failed_decls.items()) |entry| {
+ if (entry.key.container.file_scope.status == .unloaded_parse_failure) {
+ // Skip errors for Decls within files that had a parse failure.
+ // We'll try again once parsing succeeds.
+ continue;
+ }
try AllErrors.add(module, &arena, &errors, entry.value.*);
}
for (module.emit_h_failed_decls.items()) |entry| {
+ if (entry.key.container.file_scope.status == .unloaded_parse_failure) {
+ // Skip errors for Decls within files that had a parse failure.
+ // We'll try again once parsing succeeds.
+ continue;
+ }
try AllErrors.add(module, &arena, &errors, entry.value.*);
}
for (module.failed_exports.items()) |entry| {
diff --git a/src/Module.zig b/src/Module.zig
index bab61730e5..933917d948 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -65,8 +65,8 @@ emit_h_failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
/// Keep track of one `@compileLog` callsite per owner Decl.
compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, SrcLoc) = .{},
/// Using a map here for consistency with the other fields here.
-/// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator.
-failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *ErrorMsg) = .{},
+/// The ErrorMsg memory is owned by the `Scope.File`, using Module's general purpose allocator.
+failed_files: std.AutoArrayHashMapUnmanaged(*Scope.File, *ErrorMsg) = .{},
/// Using a map here for consistency with the other fields here.
/// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.
failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{},
@@ -75,7 +75,7 @@ next_anon_name_index: usize = 0,
/// Candidates for deletion. After a semantic analysis update completes, this list
/// contains Decls that need to be deleted if they end up having no references to them.
-deletion_set: ArrayListUnmanaged(*Decl) = .{},
+deletion_set: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
/// Error tags and their values, tag names are duped with mod.gpa.
/// Corresponds with `error_name_list`.
@@ -192,7 +192,7 @@ pub const Decl = struct {
/// to require re-analysis.
outdated,
},
- /// This flag is set when this Decl is added to a check_for_deletion set, and cleared
+ /// This flag is set when this Decl is added to `Module.deletion_set`, and cleared
/// when removed.
deletion_flag: bool,
/// Whether the corresponding AST decl has a `pub` keyword.
@@ -290,6 +290,18 @@ pub const Decl = struct {
return decl.container.fullyQualifiedNameHash(mem.spanZ(decl.name));
}
+ pub fn renderFullyQualifiedName(decl: Decl, writer: anytype) !void {
+ const unqualified_name = mem.spanZ(decl.name);
+ return decl.container.renderFullyQualifiedName(unqualified_name, writer);
+ }
+
+ pub fn getFullyQualifiedName(decl: Decl, gpa: *Allocator) ![]u8 {
+ var buffer = std.ArrayList(u8).init(gpa);
+ defer buffer.deinit();
+ try decl.renderFullyQualifiedName(buffer.writer());
+ return buffer.toOwnedSlice();
+ }
+
pub fn typedValue(decl: *Decl) error{AnalysisFail}!TypedValue {
const tvm = decl.typedValueManaged() orelse return error.AnalysisFail;
return tvm.typed_value;
@@ -354,6 +366,13 @@ pub const ErrorSet = struct {
/// The string bytes are stored in the owner Decl arena.
/// They are in the same order they appear in the AST.
names_ptr: [*]const []const u8,
+
+ pub fn srcLoc(self: ErrorSet) SrcLoc {
+ return .{
+ .container = .{ .decl = self.owner_decl },
+ .lazy = .{ .node_offset = self.node_offset },
+ };
+ }
};
/// Represents the data that a struct declaration provides.
@@ -375,8 +394,7 @@ pub const Struct = struct {
};
pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![]u8 {
- // TODO this should return e.g. "std.fs.Dir.OpenOptions"
- return gpa.dupe(u8, mem.spanZ(s.owner_decl.name));
+ return s.owner_decl.getFullyQualifiedName(gpa);
}
pub fn srcLoc(s: Struct) SrcLoc {
@@ -387,6 +405,53 @@ pub const Struct = struct {
}
};
+/// Represents the data that an enum declaration provides, when the fields
+/// are auto-numbered, and there are no declarations. The integer tag type
+/// is inferred to be the smallest power of two unsigned int that fits
+/// the number of fields.
+pub const EnumSimple = struct {
+ owner_decl: *Decl,
+ /// Set of field names in declaration order.
+ fields: std.StringArrayHashMapUnmanaged(void),
+ /// Offset from `owner_decl`, points to the enum decl AST node.
+ node_offset: i32,
+
+ pub fn srcLoc(self: EnumSimple) SrcLoc {
+ return .{
+ .container = .{ .decl = self.owner_decl },
+ .lazy = .{ .node_offset = self.node_offset },
+ };
+ }
+};
+
+/// Represents the data that an enum declaration provides, when there is
+/// at least one tag value explicitly specified, or at least one declaration.
+pub const EnumFull = struct {
+ owner_decl: *Decl,
+ /// An integer type which is used for the numerical value of the enum.
+ /// Whether zig chooses this type or the user specifies it, it is stored here.
+ tag_ty: Type,
+ /// Set of field names in declaration order.
+ fields: std.StringArrayHashMapUnmanaged(void),
+ /// Maps integer tag value to field index.
+ /// Entries are in declaration order, same as `fields`.
+ /// If this hash map is empty, it means the enum tags are auto-numbered.
+ values: ValueMap,
+ /// Represents the declarations inside this struct.
+ container: Scope.Container,
+ /// Offset from `owner_decl`, points to the enum decl AST node.
+ node_offset: i32,
+
+ pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.hash_u32, Value.eql, false);
+
+ pub fn srcLoc(self: EnumFull) SrcLoc {
+ return .{
+ .container = .{ .decl = self.owner_decl },
+ .lazy = .{ .node_offset = self.node_offset },
+ };
+ }
+};
+
/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
/// Extern functions do not have this data structure; they are represented by
/// the `Decl` only, with a `Value` tag of `extern_fn`.
@@ -634,6 +699,11 @@ pub const Scope = struct {
// TODO container scope qualified names.
return std.zig.hashSrc(name);
}
+
+ pub fn renderFullyQualifiedName(cont: Container, name: []const u8, writer: anytype) !void {
+ // TODO this should render e.g. "std.fs.Dir.OpenOptions"
+ return writer.writeAll(name);
+ }
};
pub const File = struct {
@@ -662,10 +732,12 @@ pub const Scope = struct {
pub fn unload(file: *File, gpa: *Allocator) void {
switch (file.status) {
- .never_loaded,
.unloaded_parse_failure,
+ .never_loaded,
.unloaded_success,
- => {},
+ => {
+ file.status = .unloaded_success;
+ },
.loaded_success => {
file.tree.deinit(gpa);
@@ -1030,7 +1102,6 @@ pub const Scope = struct {
.instructions = gz.astgen.instructions.toOwnedSlice(),
.string_bytes = gz.astgen.string_bytes.toOwnedSlice(gpa),
.extra = gz.astgen.extra.toOwnedSlice(gpa),
- .decls = gz.astgen.decls.toOwnedSlice(gpa),
};
}
@@ -1242,6 +1313,16 @@ pub const Scope = struct {
});
}
+ pub fn addFloat(gz: *GenZir, number: f32, src_node: ast.Node.Index) !zir.Inst.Ref {
+ return gz.add(.{
+ .tag = .float,
+ .data = .{ .float = .{
+ .src_node = gz.astgen.decl.nodeIndexToRelative(src_node),
+ .number = number,
+ } },
+ });
+ }
+
pub fn addUnNode(
gz: *GenZir,
tag: zir.Inst.Tag,
@@ -1450,13 +1531,6 @@ pub const Scope = struct {
return new_index;
}
- pub fn addConst(gz: *GenZir, typed_value: *TypedValue) !zir.Inst.Ref {
- return gz.add(.{
- .tag = .@"const",
- .data = .{ .@"const" = typed_value },
- });
- }
-
pub fn add(gz: *GenZir, inst: zir.Inst) !zir.Inst.Ref {
return gz.astgen.indexToRef(try gz.addAsIndex(inst));
}
@@ -2321,7 +2395,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
// We don't perform a deletion here, because this Decl or another one
// may end up referencing it before the update is complete.
dep.deletion_flag = true;
- try mod.deletion_set.append(mod.gpa, dep);
+ try mod.deletion_set.put(mod.gpa, dep, {});
}
}
decl.dependencies.clearRetainingCapacity();
@@ -3120,12 +3194,19 @@ fn astgenAndSemaVarDecl(
return type_changed;
}
-pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !void {
- try depender.dependencies.ensureCapacity(mod.gpa, depender.dependencies.items().len + 1);
- try dependee.dependants.ensureCapacity(mod.gpa, dependee.dependants.items().len + 1);
+/// Returns the depender's index of the dependee.
+pub fn declareDeclDependency(mod: *Module, depender: *Decl, dependee: *Decl) !u32 {
+ try depender.dependencies.ensureCapacity(mod.gpa, depender.dependencies.count() + 1);
+ try dependee.dependants.ensureCapacity(mod.gpa, dependee.dependants.count() + 1);
+
+ if (dependee.deletion_flag) {
+ dependee.deletion_flag = false;
+ mod.deletion_set.removeAssertDiscard(dependee);
+ }
- depender.dependencies.putAssumeCapacity(dependee, {});
dependee.dependants.putAssumeCapacity(depender, {});
+ const gop = depender.dependencies.getOrPutAssumeCapacity(dependee);
+ return @intCast(u32, gop.index);
}
pub fn getAstTree(mod: *Module, root_scope: *Scope.File) !*const ast.Tree {
@@ -3150,17 +3231,19 @@ pub fn getAstTree(mod: *Module, root_scope: *Scope.File) !*const ast.Tree {
var msg = std.ArrayList(u8).init(mod.gpa);
defer msg.deinit();
+ const token_starts = tree.tokens.items(.start);
+
try tree.renderError(parse_err, msg.writer());
const err_msg = try mod.gpa.create(ErrorMsg);
err_msg.* = .{
.src_loc = .{
.container = .{ .file_scope = root_scope },
- .lazy = .{ .token_abs = parse_err.token },
+ .lazy = .{ .byte_abs = token_starts[parse_err.token] },
},
.msg = msg.toOwnedSlice(),
};
- mod.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
+ mod.failed_files.putAssumeCapacityNoClobber(root_scope, err_msg);
root_scope.status = .unloaded_parse_failure;
return error.AnalysisFail;
}
@@ -3200,6 +3283,14 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
deleted_decls.putAssumeCapacityNoClobber(entry.key, {});
}
+ // Keep track of decls that are invalidated from the update. Ultimately,
+ // the goal is to queue up `analyze_decl` tasks in the work queue for
+ // the outdated decls, but we cannot queue up the tasks until after
+ // we find out which ones have been deleted, otherwise there would be
+ // deleted Decl pointers in the work queue.
+ var outdated_decls = std.AutoArrayHashMap(*Decl, void).init(mod.gpa);
+ defer outdated_decls.deinit();
+
for (decls) |decl_node, decl_i| switch (node_tags[decl_node]) {
.fn_decl => {
const fn_proto = node_datas[decl_node].lhs;
@@ -3210,6 +3301,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
try mod.semaContainerFn(
container_scope,
&deleted_decls,
+ &outdated_decls,
decl_node,
decl_i,
tree.*,
@@ -3220,6 +3312,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
.fn_proto_multi => try mod.semaContainerFn(
container_scope,
&deleted_decls,
+ &outdated_decls,
decl_node,
decl_i,
tree.*,
@@ -3231,6 +3324,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
try mod.semaContainerFn(
container_scope,
&deleted_decls,
+ &outdated_decls,
decl_node,
decl_i,
tree.*,
@@ -3241,6 +3335,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
.fn_proto => try mod.semaContainerFn(
container_scope,
&deleted_decls,
+ &outdated_decls,
decl_node,
decl_i,
tree.*,
@@ -3255,6 +3350,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
try mod.semaContainerFn(
container_scope,
&deleted_decls,
+ &outdated_decls,
decl_node,
decl_i,
tree.*,
@@ -3265,6 +3361,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
.fn_proto_multi => try mod.semaContainerFn(
container_scope,
&deleted_decls,
+ &outdated_decls,
decl_node,
decl_i,
tree.*,
@@ -3276,6 +3373,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
try mod.semaContainerFn(
container_scope,
&deleted_decls,
+ &outdated_decls,
decl_node,
decl_i,
tree.*,
@@ -3286,6 +3384,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
.fn_proto => try mod.semaContainerFn(
container_scope,
&deleted_decls,
+ &outdated_decls,
decl_node,
decl_i,
tree.*,
@@ -3296,6 +3395,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
.global_var_decl => try mod.semaContainerVar(
container_scope,
&deleted_decls,
+ &outdated_decls,
decl_node,
decl_i,
tree.*,
@@ -3304,6 +3404,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
.local_var_decl => try mod.semaContainerVar(
container_scope,
&deleted_decls,
+ &outdated_decls,
decl_node,
decl_i,
tree.*,
@@ -3312,6 +3413,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
.simple_var_decl => try mod.semaContainerVar(
container_scope,
&deleted_decls,
+ &outdated_decls,
decl_node,
decl_i,
tree.*,
@@ -3320,6 +3422,7 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
.aligned_var_decl => try mod.semaContainerVar(
container_scope,
&deleted_decls,
+ &outdated_decls,
decl_node,
decl_i,
tree.*,
@@ -3372,11 +3475,27 @@ pub fn analyzeContainer(mod: *Module, container_scope: *Scope.Container) !void {
},
else => unreachable,
};
- // Handle explicitly deleted decls from the source code. Not to be confused
- // with when we delete decls because they are no longer referenced.
+ // Handle explicitly deleted decls from the source code. This is one of two
+ // places that Decl deletions happen. The other is in `Compilation`, after
+ // `performAllTheWork`, where we iterate over `Module.deletion_set` and
+ // delete Decls which are no longer referenced.
+ // If a Decl is explicitly deleted from source, and also no longer referenced,
+ // it may be both in this `deleted_decls` set, as well as in the
+ // `Module.deletion_set`. To avoid deleting it twice, we remove it from the
+ // deletion set at this time.
for (deleted_decls.items()) |entry| {
- log.debug("noticed '{s}' deleted from source", .{entry.key.name});
- try mod.deleteDecl(entry.key);
+ const decl = entry.key;
+ log.debug("'{s}' deleted from source", .{decl.name});
+ if (decl.deletion_flag) {
+ log.debug("'{s}' redundantly in deletion set; removing", .{decl.name});
+ mod.deletion_set.removeAssertDiscard(decl);
+ }
+ try mod.deleteDecl(decl, &outdated_decls);
+ }
+ // Finally we can queue up re-analysis tasks after we have processed
+ // the deleted decls.
+ for (outdated_decls.items()) |entry| {
+ try mod.markOutdatedDecl(entry.key);
}
}
@@ -3384,6 +3503,7 @@ fn semaContainerFn(
mod: *Module,
container_scope: *Scope.Container,
deleted_decls: *std.AutoArrayHashMap(*Decl, void),
+ outdated_decls: *std.AutoArrayHashMap(*Decl, void),
decl_node: ast.Node.Index,
decl_i: usize,
tree: ast.Tree,
@@ -3415,7 +3535,7 @@ fn semaContainerFn(
try mod.failed_decls.putNoClobber(mod.gpa, decl, msg);
} else {
if (!srcHashEql(decl.contents_hash, contents_hash)) {
- try mod.markOutdatedDecl(decl);
+ try outdated_decls.put(decl, {});
decl.contents_hash = contents_hash;
} else switch (mod.comp.bin_file.tag) {
.coff => {
@@ -3450,6 +3570,7 @@ fn semaContainerVar(
mod: *Module,
container_scope: *Scope.Container,
deleted_decls: *std.AutoArrayHashMap(*Decl, void),
+ outdated_decls: *std.AutoArrayHashMap(*Decl, void),
decl_node: ast.Node.Index,
decl_i: usize,
tree: ast.Tree,
@@ -3475,7 +3596,7 @@ fn semaContainerVar(
errdefer err_msg.destroy(mod.gpa);
try mod.failed_decls.putNoClobber(mod.gpa, decl, err_msg);
} else if (!srcHashEql(decl.contents_hash, contents_hash)) {
- try mod.markOutdatedDecl(decl);
+ try outdated_decls.put(decl, {});
decl.contents_hash = contents_hash;
}
} else {
@@ -3505,17 +3626,27 @@ fn semaContainerField(
log.err("TODO: analyze container field", .{});
}
-pub fn deleteDecl(mod: *Module, decl: *Decl) !void {
+pub fn deleteDecl(
+ mod: *Module,
+ decl: *Decl,
+ outdated_decls: ?*std.AutoArrayHashMap(*Decl, void),
+) !void {
const tracy = trace(@src());
defer tracy.end();
- try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.items.len + decl.dependencies.items().len);
+ log.debug("deleting decl '{s}'", .{decl.name});
+
+ if (outdated_decls) |map| {
+ _ = map.swapRemove(decl);
+ try map.ensureCapacity(map.count() + decl.dependants.count());
+ }
+ try mod.deletion_set.ensureCapacity(mod.gpa, mod.deletion_set.count() +
+ decl.dependencies.count());
// Remove from the namespace it resides in. In the case of an anonymous Decl it will
// not be present in the set, and this does nothing.
decl.container.removeDecl(decl);
- log.debug("deleting decl '{s}'", .{decl.name});
const name_hash = decl.fullyQualifiedNameHash();
mod.decl_table.removeAssertDiscard(name_hash);
// Remove itself from its dependencies, because we are about to destroy the decl pointer.
@@ -3526,16 +3657,22 @@ pub fn deleteDecl(mod: *Module, decl: *Decl) !void {
// We don't recursively perform a deletion here, because during the update,
// another reference to it may turn up.
dep.deletion_flag = true;
- mod.deletion_set.appendAssumeCapacity(dep);
+ mod.deletion_set.putAssumeCapacity(dep, {});
}
}
- // Anything that depends on this deleted decl certainly needs to be re-analyzed.
+ // Anything that depends on this deleted decl needs to be re-analyzed.
for (decl.dependants.items()) |entry| {
const dep = entry.key;
dep.removeDependency(decl);
- if (dep.analysis != .outdated) {
- // TODO Move this failure possibility to the top of the function.
- try mod.markOutdatedDecl(dep);
+ if (outdated_decls) |map| {
+ map.putAssumeCapacity(dep, {});
+ } else if (std.debug.runtime_safety) {
+ // If `outdated_decls` is `null`, it means we're being called from
+ // `Compilation` after `performAllTheWork` and we cannot queue up any
+ // more work. `dep` must necessarily be another Decl that is no longer
+ // being referenced, and will be in the `deletion_set`. Otherwise,
+ // something has gone wrong.
+ assert(mod.deletion_set.contains(dep));
}
}
if (mod.failed_decls.swapRemove(decl)) |entry| {
@@ -4455,7 +4592,29 @@ pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex)
var buf: ArrayListUnmanaged(u8) = .{};
defer buf.deinit(mod.gpa);
try parseStrLit(mod, scope, token, &buf, ident_name, 1);
- return buf.toOwnedSlice(mod.gpa);
+ const duped = try scope.arena().dupe(u8, buf.items);
+ return duped;
+}
+
+/// `scope` is only used for error reporting.
+/// The string is stored in `arena` regardless of whether it uses @"" syntax.
+pub fn identifierTokenStringTreeArena(
+ mod: *Module,
+ scope: *Scope,
+ token: ast.TokenIndex,
+ tree: *const ast.Tree,
+ arena: *Allocator,
+) InnerError![]u8 {
+ const token_tags = tree.tokens.items(.tag);
+ assert(token_tags[token] == .identifier);
+ const ident_name = tree.tokenSlice(token);
+ if (!mem.startsWith(u8, ident_name, "@")) {
+ return arena.dupe(u8, ident_name);
+ }
+ var buf: ArrayListUnmanaged(u8) = .{};
+ defer buf.deinit(mod.gpa);
+ try parseStrLit(mod, scope, token, &buf, ident_name, 1);
+ return arena.dupe(u8, buf.items);
}
/// Given an identifier token, obtain the string for it (possibly parsing as a string
@@ -4545,3 +4704,10 @@ pub fn parseStrLit(
},
}
}
+
+pub fn unloadFile(mod: *Module, file_scope: *Scope.File) void {
+ if (file_scope.status == .unloaded_parse_failure) {
+ mod.failed_files.swapRemove(file_scope).?.value.destroy(mod.gpa);
+ }
+ file_scope.unload(mod.gpa);
+}
diff --git a/src/Sema.zig b/src/Sema.zig
index 0b5a21ce42..519d5df401 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -168,7 +168,6 @@ pub fn analyzeBody(
.cmp_lte => try sema.zirCmp(block, inst, .lte),
.cmp_neq => try sema.zirCmp(block, inst, .neq),
.coerce_result_ptr => try sema.zirCoerceResultPtr(block, inst),
- .@"const" => try sema.zirConst(block, inst),
.decl_ref => try sema.zirDeclRef(block, inst),
.decl_val => try sema.zirDeclVal(block, inst),
.load => try sema.zirLoad(block, inst),
@@ -179,6 +178,8 @@ pub fn analyzeBody(
.elem_val_node => try sema.zirElemValNode(block, inst),
.enum_literal => try sema.zirEnumLiteral(block, inst),
.enum_literal_small => try sema.zirEnumLiteralSmall(block, inst),
+ .enum_to_int => try sema.zirEnumToInt(block, inst),
+ .int_to_enum => try sema.zirIntToEnum(block, inst),
.err_union_code => try sema.zirErrUnionCode(block, inst),
.err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst),
.err_union_payload_safe => try sema.zirErrUnionPayload(block, inst, true),
@@ -201,6 +202,8 @@ pub fn analyzeBody(
.import => try sema.zirImport(block, inst),
.indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst),
.int => try sema.zirInt(block, inst),
+ .float => try sema.zirFloat(block, inst),
+ .float128 => try sema.zirFloat128(block, inst),
.int_type => try sema.zirIntType(block, inst),
.intcast => try sema.zirIntcast(block, inst),
.is_err => try sema.zirIsErr(block, inst),
@@ -264,7 +267,8 @@ pub fn analyzeBody(
.struct_decl => try sema.zirStructDecl(block, inst, .Auto),
.struct_decl_packed => try sema.zirStructDecl(block, inst, .Packed),
.struct_decl_extern => try sema.zirStructDecl(block, inst, .Extern),
- .enum_decl => try sema.zirEnumDecl(block, inst),
+ .enum_decl => try sema.zirEnumDecl(block, inst, false),
+ .enum_decl_nonexhaustive => try sema.zirEnumDecl(block, inst, true),
.union_decl => try sema.zirUnionDecl(block, inst),
.opaque_decl => try sema.zirOpaqueDecl(block, inst),
@@ -498,18 +502,6 @@ fn resolveInstConst(
};
}
-fn zirConst(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
- const tracy = trace(@src());
- defer tracy.end();
-
- const tv_ptr = sema.code.instructions.items(.data)[inst].@"const";
- // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions
- // after analysis. This happens, for example, with variable declaration initialization
- // expressions.
- const typed_value_copy = try tv_ptr.copy(sema.arena);
- return sema.mod.constInst(sema.arena, .unneeded, typed_value_copy);
-}
-
fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
@@ -617,7 +609,12 @@ fn zirStructDecl(
return sema.analyzeDeclVal(block, src, new_decl);
}
-fn zirEnumDecl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirEnumDecl(
+ sema: *Sema,
+ block: *Scope.Block,
+ inst: zir.Inst.Index,
+ nonexhaustive: bool,
+) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
@@ -788,8 +785,8 @@ fn zirAllocInferred(
const tracy = trace(@src());
defer tracy.end();
- const inst_data = sema.code.instructions.items(.data)[inst].un_node;
- const src = inst_data.src();
+ const src_node = sema.code.instructions.items(.data)[inst].node;
+ const src: LazySrcLoc = .{ .node_offset = src_node };
const val_payload = try sema.arena.create(Value.Payload.InferredAlloc);
val_payload.* = .{
@@ -900,7 +897,7 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Ind
try mod.errNoteNonLazy(
struct_obj.srcLoc(),
msg,
- "'{s}' declared here",
+ "struct '{s}' declared here",
.{fqn},
);
return mod.failWithOwnedErrorMsg(&block.base, msg);
@@ -928,7 +925,7 @@ fn failWithBadFieldAccess(
.{ field_name, fqn },
);
errdefer msg.destroy(gpa);
- try mod.errNoteNonLazy(struct_obj.srcLoc(), msg, "'{s}' declared here", .{fqn});
+ try mod.errNoteNonLazy(struct_obj.srcLoc(), msg, "struct declared here", .{});
break :msg msg;
};
return mod.failWithOwnedErrorMsg(&block.base, msg);
@@ -1070,6 +1067,31 @@ fn zirInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*In
return sema.mod.constIntUnsigned(sema.arena, .unneeded, Type.initTag(.comptime_int), int);
}
+fn zirFloat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+ const arena = sema.arena;
+ const inst_data = sema.code.instructions.items(.data)[inst].float;
+ const src = inst_data.src();
+ const number = inst_data.number;
+
+ return sema.mod.constInst(arena, src, .{
+ .ty = Type.initTag(.comptime_float),
+ .val = try Value.Tag.float_32.create(arena, number),
+ });
+}
+
+fn zirFloat128(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+ const arena = sema.arena;
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const extra = sema.code.extraData(zir.Inst.Float128, inst_data.payload_index).data;
+ const src = inst_data.src();
+ const number = extra.get();
+
+ return sema.mod.constInst(arena, src, .{
+ .ty = Type.initTag(.comptime_float),
+ .val = try Value.Tag.float_128.create(arena, number),
+ });
+}
+
fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
const tracy = trace(@src());
defer tracy.end();
@@ -1385,7 +1407,7 @@ fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
- const decl = sema.code.decls[inst_data.payload_index];
+ const decl = sema.owner_decl.dependencies.entries.items[inst_data.payload_index].key;
return sema.analyzeDeclRef(block, src, decl);
}
@@ -1395,7 +1417,7 @@ fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
- const decl = sema.code.decls[inst_data.payload_index];
+ const decl = sema.owner_decl.dependencies.entries.items[inst_data.payload_index].key;
return sema.analyzeDeclVal(block, src, decl);
}
@@ -1852,6 +1874,143 @@ fn zirEnumLiteralSmall(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) I
});
}
+fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+ const mod = sema.mod;
+ const arena = sema.arena;
+ const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+ const src = inst_data.src();
+ const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const operand = try sema.resolveInst(inst_data.operand);
+
+ const enum_tag: *Inst = switch (operand.ty.zigTypeTag()) {
+ .Enum => operand,
+ .Union => {
+ //if (!operand.ty.unionHasTag()) {
+ // return mod.fail(
+ // &block.base,
+ // operand_src,
+ // "untagged union '{}' cannot be converted to integer",
+ // .{dest_ty_src},
+ // );
+ //}
+ return mod.fail(&block.base, operand_src, "TODO zirEnumToInt for tagged unions", .{});
+ },
+ else => {
+ return mod.fail(&block.base, operand_src, "expected enum or tagged union, found {}", .{
+ operand.ty,
+ });
+ },
+ };
+
+ var int_tag_type_buffer: Type.Payload.Bits = undefined;
+ const int_tag_ty = try enum_tag.ty.intTagType(&int_tag_type_buffer).copy(arena);
+
+ if (enum_tag.ty.onePossibleValue()) |opv| {
+ return mod.constInst(arena, src, .{
+ .ty = int_tag_ty,
+ .val = opv,
+ });
+ }
+
+ if (enum_tag.value()) |enum_tag_val| {
+ if (enum_tag_val.castTag(.enum_field_index)) |enum_field_payload| {
+ const field_index = enum_field_payload.data;
+ switch (enum_tag.ty.tag()) {
+ .enum_full => {
+ const enum_full = enum_tag.ty.castTag(.enum_full).?.data;
+ if (enum_full.values.count() != 0) {
+ const val = enum_full.values.entries.items[field_index].key;
+ return mod.constInst(arena, src, .{
+ .ty = int_tag_ty,
+ .val = val,
+ });
+ } else {
+ // Field index and integer values are the same.
+ const val = try Value.Tag.int_u64.create(arena, field_index);
+ return mod.constInst(arena, src, .{
+ .ty = int_tag_ty,
+ .val = val,
+ });
+ }
+ },
+ .enum_simple => {
+ // Field index and integer values are the same.
+ const val = try Value.Tag.int_u64.create(arena, field_index);
+ return mod.constInst(arena, src, .{
+ .ty = int_tag_ty,
+ .val = val,
+ });
+ },
+ else => unreachable,
+ }
+ } else {
+ // Assume it is already an integer and return it directly.
+ return mod.constInst(arena, src, .{
+ .ty = int_tag_ty,
+ .val = enum_tag_val,
+ });
+ }
+ }
+
+ try sema.requireRuntimeBlock(block, src);
+ return block.addUnOp(src, int_tag_ty, .bitcast, enum_tag);
+}
+
+fn zirIntToEnum(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+ const mod = sema.mod;
+ const target = mod.getTarget();
+ const arena = sema.arena;
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
+ const src = inst_data.src();
+ const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+ const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
+ const operand = try sema.resolveInst(extra.rhs);
+
+ if (dest_ty.zigTypeTag() != .Enum) {
+ return mod.fail(&block.base, dest_ty_src, "expected enum, found {}", .{dest_ty});
+ }
+
+ if (dest_ty.isNonexhaustiveEnum()) {
+ if (operand.value()) |int_val| {
+ return mod.constInst(arena, src, .{
+ .ty = dest_ty,
+ .val = int_val,
+ });
+ }
+ }
+
+ if (try sema.resolveDefinedValue(block, operand_src, operand)) |int_val| {
+ if (!dest_ty.enumHasInt(int_val, target)) {
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ &block.base,
+ src,
+ "enum '{}' has no tag with value {}",
+ .{ dest_ty, int_val },
+ );
+ errdefer msg.destroy(sema.gpa);
+ try mod.errNoteNonLazy(
+ dest_ty.declSrcLoc(),
+ msg,
+ "enum declared here",
+ .{},
+ );
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(&block.base, msg);
+ }
+ return mod.constInst(arena, src, .{
+ .ty = dest_ty,
+ .val = int_val,
+ });
+ }
+
+ try sema.requireRuntimeBlock(block, src);
+ return block.addUnOp(src, dest_ty, .bitcast, operand);
+}
+
/// Pointer in, pointer out.
fn zirOptionalPayloadPtr(
sema: *Sema,
@@ -2584,6 +2743,8 @@ fn analyzeSwitch(
src_node_offset: i32,
) InnerError!*Inst {
const gpa = sema.gpa;
+ const mod = sema.mod;
+
const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) {
.none => .{ .body = &.{}, .end = extra_end },
.under, .@"else" => blk: {
@@ -2601,16 +2762,16 @@ fn analyzeSwitch(
const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
// Validate usage of '_' prongs.
- if (special_prong == .under and !operand.ty.isExhaustiveEnum()) {
+ if (special_prong == .under and !operand.ty.isNonexhaustiveEnum()) {
const msg = msg: {
- const msg = try sema.mod.errMsg(
+ const msg = try mod.errMsg(
&block.base,
src,
"'_' prong only allowed when switching on non-exhaustive enums",
.{},
);
errdefer msg.destroy(gpa);
- try sema.mod.errNote(
+ try mod.errNote(
&block.base,
special_prong_src,
msg,
@@ -2619,14 +2780,123 @@ fn analyzeSwitch(
);
break :msg msg;
};
- return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
+ return mod.failWithOwnedErrorMsg(&block.base, msg);
}
// Validate for duplicate items, missing else prong, and invalid range.
switch (operand.ty.zigTypeTag()) {
- .Enum => return sema.mod.fail(&block.base, src, "TODO validate switch .Enum", .{}),
- .ErrorSet => return sema.mod.fail(&block.base, src, "TODO validate switch .ErrorSet", .{}),
- .Union => return sema.mod.fail(&block.base, src, "TODO validate switch .Union", .{}),
+ .Enum => {
+ var seen_fields = try gpa.alloc(?AstGen.SwitchProngSrc, operand.ty.enumFieldCount());
+ defer gpa.free(seen_fields);
+
+ mem.set(?AstGen.SwitchProngSrc, seen_fields, null);
+
+ var extra_index: usize = special.end;
+ {
+ var scalar_i: u32 = 0;
+ while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
+ const item_ref = @intToEnum(zir.Inst.Ref, sema.code.extra[extra_index]);
+ extra_index += 1;
+ const body_len = sema.code.extra[extra_index];
+ extra_index += 1;
+ const body = sema.code.extra[extra_index..][0..body_len];
+ extra_index += body_len;
+
+ try sema.validateSwitchItemEnum(
+ block,
+ seen_fields,
+ item_ref,
+ src_node_offset,
+ .{ .scalar = scalar_i },
+ );
+ }
+ }
+ {
+ var multi_i: u32 = 0;
+ while (multi_i < multi_cases_len) : (multi_i += 1) {
+ const items_len = sema.code.extra[extra_index];
+ extra_index += 1;
+ const ranges_len = sema.code.extra[extra_index];
+ extra_index += 1;
+ const body_len = sema.code.extra[extra_index];
+ extra_index += 1;
+ const items = sema.code.refSlice(extra_index, items_len);
+ extra_index += items_len + body_len;
+
+ for (items) |item_ref, item_i| {
+ try sema.validateSwitchItemEnum(
+ block,
+ seen_fields,
+ item_ref,
+ src_node_offset,
+ .{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } },
+ );
+ }
+
+ try sema.validateSwitchNoRange(block, ranges_len, operand.ty, src_node_offset);
+ }
+ }
+ const all_tags_handled = for (seen_fields) |seen_src| {
+ if (seen_src == null) break false;
+ } else true;
+
+ switch (special_prong) {
+ .none => {
+ if (!all_tags_handled) {
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ &block.base,
+ src,
+ "switch must handle all possibilities",
+ .{},
+ );
+ errdefer msg.destroy(sema.gpa);
+ for (seen_fields) |seen_src, i| {
+ if (seen_src != null) continue;
+
+ const field_name = operand.ty.enumFieldName(i);
+
+ // TODO have this point to the tag decl instead of here
+ try mod.errNote(
+ &block.base,
+ src,
+ msg,
+ "unhandled enumeration value: '{s}'",
+ .{field_name},
+ );
+ }
+ try mod.errNoteNonLazy(
+ operand.ty.declSrcLoc(),
+ msg,
+ "enum '{}' declared here",
+ .{operand.ty},
+ );
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(&block.base, msg);
+ }
+ },
+ .under => {
+ if (all_tags_handled) return mod.fail(
+ &block.base,
+ special_prong_src,
+ "unreachable '_' prong; all cases already handled",
+ .{},
+ );
+ },
+ .@"else" => {
+ if (all_tags_handled) return mod.fail(
+ &block.base,
+ special_prong_src,
+ "unreachable else prong; all cases already handled",
+ .{},
+ );
+ },
+ }
+ },
+
+ .ErrorSet => return mod.fail(&block.base, src, "TODO validate switch .ErrorSet", .{}),
+ .Union => return mod.fail(&block.base, src, "TODO validate switch .Union", .{}),
.Int, .ComptimeInt => {
var range_set = RangeSet.init(gpa);
defer range_set.deinit();
@@ -2699,11 +2969,11 @@ fn analyzeSwitch(
var arena = std.heap.ArenaAllocator.init(gpa);
defer arena.deinit();
- const min_int = try operand.ty.minInt(&arena, sema.mod.getTarget());
- const max_int = try operand.ty.maxInt(&arena, sema.mod.getTarget());
+ const min_int = try operand.ty.minInt(&arena, mod.getTarget());
+ const max_int = try operand.ty.maxInt(&arena, mod.getTarget());
if (try range_set.spans(min_int, max_int)) {
if (special_prong == .@"else") {
- return sema.mod.fail(
+ return mod.fail(
&block.base,
special_prong_src,
"unreachable else prong; all cases already handled",
@@ -2714,7 +2984,7 @@ fn analyzeSwitch(
}
}
if (special_prong != .@"else") {
- return sema.mod.fail(
+ return mod.fail(
&block.base,
src,
"switch must handle all possibilities",
@@ -2777,7 +3047,7 @@ fn analyzeSwitch(
switch (special_prong) {
.@"else" => {
if (true_count + false_count == 2) {
- return sema.mod.fail(
+ return mod.fail(
&block.base,
src,
"unreachable else prong; all cases already handled",
@@ -2787,7 +3057,7 @@ fn analyzeSwitch(
},
.under, .none => {
if (true_count + false_count < 2) {
- return sema.mod.fail(
+ return mod.fail(
&block.base,
src,
"switch must handle all possibilities",
@@ -2799,7 +3069,7 @@ fn analyzeSwitch(
},
.EnumLiteral, .Void, .Fn, .Pointer, .Type => {
if (special_prong != .@"else") {
- return sema.mod.fail(
+ return mod.fail(
&block.base,
src,
"else prong required when switching on type '{}'",
@@ -2871,7 +3141,7 @@ fn analyzeSwitch(
.AnyFrame,
.ComptimeFloat,
.Float,
- => return sema.mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{
+ => return mod.fail(&block.base, operand_src, "invalid switch operand type '{}'", .{
operand.ty,
}),
}
@@ -3146,7 +3416,7 @@ fn resolveSwitchItemVal(
switch_node_offset: i32,
switch_prong_src: AstGen.SwitchProngSrc,
range_expand: AstGen.SwitchProngSrc.RangeExpand,
-) InnerError!Value {
+) InnerError!TypedValue {
const item = try sema.resolveInst(item_ref);
// We have to avoid the other helper functions here because we cannot construct a LazySrcLoc
// because we only have the switch AST node. Only if we know for sure we need to report
@@ -3156,7 +3426,7 @@ fn resolveSwitchItemVal(
const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand);
return sema.failWithUseOfUndef(block, src);
}
- return val;
+ return TypedValue{ .ty = item.ty, .val = val };
}
const src = switch_prong_src.resolve(block.src_decl, switch_node_offset, range_expand);
return sema.failWithNeededComptime(block, src);
@@ -3171,8 +3441,8 @@ fn validateSwitchRange(
src_node_offset: i32,
switch_prong_src: AstGen.SwitchProngSrc,
) InnerError!void {
- const first_val = try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first);
- const last_val = try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last);
+ const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val;
+ const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val;
const maybe_prev_src = try range_set.add(first_val, last_val, switch_prong_src);
return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
}
@@ -3185,11 +3455,46 @@ fn validateSwitchItem(
src_node_offset: i32,
switch_prong_src: AstGen.SwitchProngSrc,
) InnerError!void {
- const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
+ const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
const maybe_prev_src = try range_set.add(item_val, item_val, switch_prong_src);
return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
}
+fn validateSwitchItemEnum(
+ sema: *Sema,
+ block: *Scope.Block,
+ seen_fields: []?AstGen.SwitchProngSrc,
+ item_ref: zir.Inst.Ref,
+ src_node_offset: i32,
+ switch_prong_src: AstGen.SwitchProngSrc,
+) InnerError!void {
+ const mod = sema.mod;
+ const item_tv = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
+ const field_index = item_tv.ty.enumTagFieldIndex(item_tv.val) orelse {
+ const msg = msg: {
+ const src = switch_prong_src.resolve(block.src_decl, src_node_offset, .none);
+ const msg = try mod.errMsg(
+ &block.base,
+ src,
+ "enum '{}' has no tag with value '{}'",
+ .{ item_tv.ty, item_tv.val },
+ );
+ errdefer msg.destroy(sema.gpa);
+ try mod.errNoteNonLazy(
+ item_tv.ty.declSrcLoc(),
+ msg,
+ "enum declared here",
+ .{},
+ );
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(&block.base, msg);
+ };
+ const maybe_prev_src = seen_fields[field_index];
+ seen_fields[field_index] = switch_prong_src;
+ return sema.validateSwitchDupe(block, maybe_prev_src, switch_prong_src, src_node_offset);
+}
+
fn validateSwitchDupe(
sema: *Sema,
block: *Scope.Block,
@@ -3198,17 +3503,18 @@ fn validateSwitchDupe(
src_node_offset: i32,
) InnerError!void {
const prev_prong_src = maybe_prev_src orelse return;
+ const mod = sema.mod;
const src = switch_prong_src.resolve(block.src_decl, src_node_offset, .none);
const prev_src = prev_prong_src.resolve(block.src_decl, src_node_offset, .none);
const msg = msg: {
- const msg = try sema.mod.errMsg(
+ const msg = try mod.errMsg(
&block.base,
src,
"duplicate switch value",
.{},
);
errdefer msg.destroy(sema.gpa);
- try sema.mod.errNote(
+ try mod.errNote(
&block.base,
prev_src,
msg,
@@ -3217,7 +3523,7 @@ fn validateSwitchDupe(
);
break :msg msg;
};
- return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
+ return mod.failWithOwnedErrorMsg(&block.base, msg);
}
fn validateSwitchItemBool(
@@ -3229,7 +3535,7 @@ fn validateSwitchItemBool(
src_node_offset: i32,
switch_prong_src: AstGen.SwitchProngSrc,
) InnerError!void {
- const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
+ const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
if (item_val.toBool()) {
true_count.* += 1;
} else {
@@ -3251,7 +3557,7 @@ fn validateSwitchItemSparse(
src_node_offset: i32,
switch_prong_src: AstGen.SwitchProngSrc,
) InnerError!void {
- const item_val = try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none);
+ const item_val = (try sema.resolveSwitchItemVal(block, item_ref, src_node_offset, switch_prong_src, .none)).val;
const entry = (try seen_values.fetchPut(item_val, switch_prong_src)) orelse return;
return sema.validateSwitchDupe(block, entry.value, switch_prong_src, src_node_offset);
}
@@ -3631,9 +3937,13 @@ fn zirCmp(
const tracy = trace(@src());
defer tracy.end();
+ const mod = sema.mod;
+
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
const src: LazySrcLoc = inst_data.src();
+ const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
+ const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
const lhs = try sema.resolveInst(extra.lhs);
const rhs = try sema.resolveInst(extra.rhs);
@@ -3645,7 +3955,7 @@ fn zirCmp(
const rhs_ty_tag = rhs.ty.zigTypeTag();
if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) {
// null == null, null != null
- return sema.mod.constBool(sema.arena, src, op == .eq);
+ return mod.constBool(sema.arena, src, op == .eq);
} else if (is_equality_cmp and
((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or
rhs_ty_tag == .Null and lhs_ty_tag == .Optional))
@@ -3656,23 +3966,23 @@ fn zirCmp(
} else if (is_equality_cmp and
((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr())))
{
- return sema.mod.fail(&block.base, src, "TODO implement C pointer cmp", .{});
+ return mod.fail(&block.base, src, "TODO implement C pointer cmp", .{});
} else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) {
const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty;
- return sema.mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type});
+ return mod.fail(&block.base, src, "comparison of '{}' with null", .{non_null_type});
} else if (is_equality_cmp and
((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or
(rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union)))
{
- return sema.mod.fail(&block.base, src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
+ return mod.fail(&block.base, src, "TODO implement equality comparison between a union's tag value and an enum literal", .{});
} else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) {
if (!is_equality_cmp) {
- return sema.mod.fail(&block.base, src, "{s} operator not allowed for errors", .{@tagName(op)});
+ return mod.fail(&block.base, src, "{s} operator not allowed for errors", .{@tagName(op)});
}
if (rhs.value()) |rval| {
if (lhs.value()) |lval| {
// TODO optimisation oppurtunity: evaluate if std.mem.eql is faster with the names, or calling to Module.getErrorValue to get the values and then compare them is faster
- return sema.mod.constBool(sema.arena, src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq));
+ return mod.constBool(sema.arena, src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq));
}
}
try sema.requireRuntimeBlock(block, src);
@@ -3684,11 +3994,30 @@ fn zirCmp(
return sema.cmpNumeric(block, src, lhs, rhs, op);
} else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) {
if (!is_equality_cmp) {
- return sema.mod.fail(&block.base, src, "{s} operator not allowed for types", .{@tagName(op)});
+ return mod.fail(&block.base, src, "{s} operator not allowed for types", .{@tagName(op)});
}
- return sema.mod.constBool(sema.arena, src, lhs.value().?.eql(rhs.value().?) == (op == .eq));
+ return mod.constBool(sema.arena, src, lhs.value().?.eql(rhs.value().?) == (op == .eq));
+ }
+
+ const instructions = &[_]*Inst{ lhs, rhs };
+ const resolved_type = try sema.resolvePeerTypes(block, src, instructions);
+ if (!resolved_type.isSelfComparable(is_equality_cmp)) {
+ return mod.fail(&block.base, src, "operator not allowed for type '{}'", .{resolved_type});
}
- return sema.mod.fail(&block.base, src, "TODO implement more cmp analysis", .{});
+
+ const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
+ const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
+ try sema.requireRuntimeBlock(block, src); // TODO try to do it at comptime
+ const bool_type = Type.initTag(.bool); // TODO handle vectors
+ const tag: Inst.Tag = switch (op) {
+ .lt => .cmp_lt,
+ .lte => .cmp_lte,
+ .eq => .cmp_eq,
+ .gte => .cmp_gte,
+ .gt => .cmp_gt,
+ .neq => .cmp_neq,
+ };
+ return block.addBinOp(src, bool_type, tag, casted_lhs, casted_rhs);
}
fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -4215,22 +4544,25 @@ fn namedFieldPtr(
field_name: []const u8,
field_name_src: LazySrcLoc,
) InnerError!*Inst {
+ const mod = sema.mod;
+ const arena = sema.arena;
+
const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
.Pointer => object_ptr.ty.elemType(),
- else => return sema.mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
+ else => return mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
};
switch (elem_ty.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) {
- return sema.mod.constInst(sema.arena, src, .{
+ return mod.constInst(arena, src, .{
.ty = Type.initTag(.single_const_pointer_to_comptime_int),
.val = try Value.Tag.ref_val.create(
- sema.arena,
- try Value.Tag.int_u64.create(sema.arena, elem_ty.arrayLen()),
+ arena,
+ try Value.Tag.int_u64.create(arena, elem_ty.arrayLen()),
),
});
} else {
- return sema.mod.fail(
+ return mod.fail(
&block.base,
field_name_src,
"no member named '{s}' in '{}'",
@@ -4243,15 +4575,15 @@ fn namedFieldPtr(
switch (ptr_child.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) {
- return sema.mod.constInst(sema.arena, src, .{
+ return mod.constInst(arena, src, .{
.ty = Type.initTag(.single_const_pointer_to_comptime_int),
.val = try Value.Tag.ref_val.create(
- sema.arena,
- try Value.Tag.int_u64.create(sema.arena, ptr_child.arrayLen()),
+ arena,
+ try Value.Tag.int_u64.create(arena, ptr_child.arrayLen()),
),
});
} else {
- return sema.mod.fail(
+ return mod.fail(
&block.base,
field_name_src,
"no member named '{s}' in '{}'",
@@ -4266,7 +4598,7 @@ fn namedFieldPtr(
_ = try sema.resolveConstValue(block, object_ptr.src, object_ptr);
const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr.src);
const val = result.value().?;
- const child_type = try val.toType(sema.arena);
+ const child_type = try val.toType(arena);
switch (child_type.zigTypeTag()) {
.ErrorSet => {
// TODO resolve inferred error sets
@@ -4280,42 +4612,90 @@ fn namedFieldPtr(
break :blk name;
}
}
- return sema.mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{
+ return mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{
field_name,
child_type,
});
- } else (try sema.mod.getErrorValue(field_name)).key;
+ } else (try mod.getErrorValue(field_name)).key;
- return sema.mod.constInst(sema.arena, src, .{
- .ty = try sema.mod.simplePtrType(sema.arena, child_type, false, .One),
+ return mod.constInst(arena, src, .{
+ .ty = try mod.simplePtrType(arena, child_type, false, .One),
.val = try Value.Tag.ref_val.create(
- sema.arena,
- try Value.Tag.@"error".create(sema.arena, .{
+ arena,
+ try Value.Tag.@"error".create(arena, .{
.name = name,
}),
),
});
},
- .Struct => {
- const container_scope = child_type.getContainerScope();
- if (sema.mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
- // TODO if !decl.is_pub and inDifferentFiles() "{} is private"
- return sema.analyzeDeclRef(block, src, decl);
- }
+ .Struct, .Opaque, .Union => {
+ if (child_type.getContainerScope()) |container_scope| {
+ if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
+ // TODO if !decl.is_pub and inDifferentFiles() "{} is private"
+ return sema.analyzeDeclRef(block, src, decl);
+ }
- if (container_scope.file_scope == sema.mod.root_scope) {
- return sema.mod.fail(&block.base, src, "root source file has no member called '{s}'", .{field_name});
- } else {
- return sema.mod.fail(&block.base, src, "container '{}' has no member called '{s}'", .{ child_type, field_name });
+ // TODO this will give false positives for structs inside the root file
+ if (container_scope.file_scope == mod.root_scope) {
+ return mod.fail(
+ &block.base,
+ src,
+ "root source file has no member named '{s}'",
+ .{field_name},
+ );
+ }
}
+ // TODO add note: declared here
+ const kw_name = switch (child_type.zigTypeTag()) {
+ .Struct => "struct",
+ .Opaque => "opaque",
+ .Union => "union",
+ else => unreachable,
+ };
+ return mod.fail(&block.base, src, "{s} '{}' has no member named '{s}'", .{
+ kw_name, child_type, field_name,
+ });
},
- else => return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{child_type}),
+ .Enum => {
+ if (child_type.getContainerScope()) |container_scope| {
+ if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
+ // TODO if !decl.is_pub and inDifferentFiles() "{} is private"
+ return sema.analyzeDeclRef(block, src, decl);
+ }
+ }
+ const field_index = child_type.enumFieldIndex(field_name) orelse {
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ &block.base,
+ src,
+ "enum '{}' has no member named '{s}'",
+ .{ child_type, field_name },
+ );
+ errdefer msg.destroy(sema.gpa);
+ try mod.errNoteNonLazy(
+ child_type.declSrcLoc(),
+ msg,
+ "enum declared here",
+ .{},
+ );
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(&block.base, msg);
+ };
+ const field_index_u32 = @intCast(u32, field_index);
+ const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32);
+ return mod.constInst(arena, src, .{
+ .ty = try mod.simplePtrType(arena, child_type, false, .One),
+ .val = try Value.Tag.ref_val.create(arena, enum_val),
+ });
+ },
+ else => return mod.fail(&block.base, src, "type '{}' has no members", .{child_type}),
}
},
.Struct => return sema.analyzeStructFieldPtr(block, src, object_ptr, field_name, field_name_src, elem_ty),
else => {},
}
- return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty});
+ return mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty});
}
fn analyzeStructFieldPtr(
@@ -4400,10 +4780,13 @@ fn coerce(
return sema.bitcast(block, dest_type, inst);
}
+ const mod = sema.mod;
+ const arena = sema.arena;
+
// undefined to anything
if (inst.value()) |val| {
if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) {
- return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = val });
+ return mod.constInst(arena, inst_src, .{ .ty = dest_type, .val = val });
}
}
assert(inst.ty.zigTypeTag() != .Undefined);
@@ -4417,13 +4800,13 @@ fn coerce(
if (try sema.coerceNum(block, dest_type, inst)) |some|
return some;
- const target = sema.mod.getTarget();
+ const target = mod.getTarget();
switch (dest_type.zigTypeTag()) {
.Optional => {
// null to ?T
if (inst.ty.zigTypeTag() == .Null) {
- return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
+ return mod.constInst(arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
}
// T to ?T
@@ -4509,10 +4892,40 @@ fn coerce(
}
}
},
+ .Enum => {
+ // enum literal to enum
+ if (inst.ty.zigTypeTag() == .EnumLiteral) {
+ const val = try sema.resolveConstValue(block, inst_src, inst);
+ const bytes = val.castTag(.enum_literal).?.data;
+ const field_index = dest_type.enumFieldIndex(bytes) orelse {
+ const msg = msg: {
+ const msg = try mod.errMsg(
+ &block.base,
+ inst_src,
+ "enum '{}' has no field named '{s}'",
+ .{ dest_type, bytes },
+ );
+ errdefer msg.destroy(sema.gpa);
+ try mod.errNoteNonLazy(
+ dest_type.declSrcLoc(),
+ msg,
+ "enum declared here",
+ .{},
+ );
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(&block.base, msg);
+ };
+ return mod.constInst(arena, inst_src, .{
+ .ty = dest_type,
+ .val = try Value.Tag.enum_field_index.create(arena, @intCast(u32, field_index)),
+ });
+ }
+ },
else => {},
}
- return sema.mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty });
+ return mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty });
}
const InMemoryCoercionResult = enum {
@@ -4630,7 +5043,7 @@ fn analyzeDeclVal(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl
}
fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst {
- try sema.mod.declareDeclDependency(sema.owner_decl, decl);
+ _ = try sema.mod.declareDeclDependency(sema.owner_decl, decl);
sema.mod.ensureDeclAnalyzed(decl) catch |err| {
if (sema.func) |func| {
func.state = .dependency_failure;
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 876f86ed02..874baccf42 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -172,7 +172,10 @@ pub const DeclGen = struct {
val: Value,
) error{ OutOfMemory, AnalysisFail }!void {
if (val.isUndef()) {
- return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: properly handle undefined in all cases (with debug safety?)", .{});
+ // This should lower to 0xaa bytes in safe modes, and for unsafe modes should
+ // lower to leaving variables uninitialized (that might need to be implemented
+ // outside of this function).
+ return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement renderValue undef", .{});
}
switch (t.zigTypeTag()) {
.Int => {
@@ -288,6 +291,31 @@ pub const DeclGen = struct {
try writer.writeAll(", .error = 0 }");
}
},
+ .Enum => {
+ switch (val.tag()) {
+ .enum_field_index => {
+ const field_index = val.castTag(.enum_field_index).?.data;
+ switch (t.tag()) {
+ .enum_simple => return writer.print("{d}", .{field_index}),
+ .enum_full, .enum_nonexhaustive => {
+ const enum_full = t.cast(Type.Payload.EnumFull).?.data;
+ if (enum_full.values.count() != 0) {
+ const tag_val = enum_full.values.entries.items[field_index].key;
+ return dg.renderValue(writer, enum_full.tag_ty, tag_val);
+ } else {
+ return writer.print("{d}", .{field_index});
+ }
+ },
+ else => unreachable,
+ }
+ },
+ else => {
+ var int_tag_ty_buffer: Type.Payload.Bits = undefined;
+ const int_tag_ty = t.intTagType(&int_tag_ty_buffer);
+ return dg.renderValue(writer, int_tag_ty, val);
+ },
+ }
+ },
else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{
@tagName(e),
}),
@@ -368,6 +396,9 @@ pub const DeclGen = struct {
else => unreachable,
}
},
+
+ .Float => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Float", .{}),
+
.Pointer => {
if (t.isSlice()) {
return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement slices", .{});
@@ -472,10 +503,29 @@ pub const DeclGen = struct {
try w.writeAll(name);
dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered });
},
- .Null, .Undefined => unreachable, // must be const or comptime
- else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type {s}", .{
- @tagName(e),
- }),
+ .Enum => {
+ // For enums, we simply use the integer tag type.
+ var int_tag_ty_buffer: Type.Payload.Bits = undefined;
+ const int_tag_ty = t.intTagType(&int_tag_ty_buffer);
+
+ try dg.renderType(w, int_tag_ty);
+ },
+ .Union => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Union", .{}),
+ .Fn => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Fn", .{}),
+ .Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}),
+ .Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}),
+ .AnyFrame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type AnyFrame", .{}),
+ .Vector => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Vector", .{}),
+
+ .Null,
+ .Undefined,
+ .EnumLiteral,
+ .ComptimeFloat,
+ .ComptimeInt,
+ .Type,
+ => unreachable, // must be const or comptime
+
+ .BoundFn => unreachable, // this type will be deleted from the language
}
}
diff --git a/src/type.zig b/src/type.zig
index f5ce296e4d..ab31991c36 100644
--- a/src/type.zig
+++ b/src/type.zig
@@ -93,14 +93,56 @@ pub const Type = extern union {
.anyerror_void_error_union, .error_union => return .ErrorUnion,
- .empty_struct => return .Struct,
- .empty_struct_literal => return .Struct,
- .@"struct" => return .Struct,
+ .empty_struct,
+ .empty_struct_literal,
+ .@"struct",
+ => return .Struct,
+
+ .enum_full,
+ .enum_nonexhaustive,
+ .enum_simple,
+ => return .Enum,
.var_args_param => unreachable, // can be any type
}
}
+ pub fn isSelfComparable(ty: Type, is_equality_cmp: bool) bool {
+ return switch (ty.zigTypeTag()) {
+ .Int,
+ .Float,
+ .ComptimeFloat,
+ .ComptimeInt,
+ .Vector, // TODO some vectors require is_equality_cmp==true
+ => true,
+
+ .Bool,
+ .Type,
+ .Void,
+ .ErrorSet,
+ .Fn,
+ .BoundFn,
+ .Opaque,
+ .AnyFrame,
+ .Enum,
+ .EnumLiteral,
+ => is_equality_cmp,
+
+ .NoReturn,
+ .Array,
+ .Struct,
+ .Undefined,
+ .Null,
+ .ErrorUnion,
+ .Union,
+ .Frame,
+ => false,
+
+ .Pointer => is_equality_cmp or ty.isCPtr(),
+ .Optional => is_equality_cmp and ty.isPtrLikeOptional(),
+ };
+ }
+
pub fn initTag(comptime small_tag: Tag) Type {
comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
return .{ .tag_if_small_enough = @enumToInt(small_tag) };
@@ -614,6 +656,8 @@ pub const Type = extern union {
.error_set_single => return self.copyPayloadShallow(allocator, Payload.Name),
.empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope),
.@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct),
+ .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple),
+ .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull),
.@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),
}
}
@@ -626,13 +670,13 @@ pub const Type = extern union {
}
pub fn format(
- self: Type,
+ start_type: Type,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
- out_stream: anytype,
- ) @TypeOf(out_stream).Error!void {
+ writer: anytype,
+ ) @TypeOf(writer).Error!void {
comptime assert(fmt.len == 0);
- var ty = self;
+ var ty = start_type;
while (true) {
const t = ty.tag();
switch (t) {
@@ -670,132 +714,149 @@ pub const Type = extern union {
.comptime_float,
.noreturn,
.var_args_param,
- => return out_stream.writeAll(@tagName(t)),
-
- .enum_literal => return out_stream.writeAll("@Type(.EnumLiteral)"),
- .@"null" => return out_stream.writeAll("@Type(.Null)"),
- .@"undefined" => return out_stream.writeAll("@Type(.Undefined)"),
-
- .empty_struct, .empty_struct_literal => return out_stream.writeAll("struct {}"),
- .@"struct" => return out_stream.writeAll("(struct)"),
- .anyerror_void_error_union => return out_stream.writeAll("anyerror!void"),
- .const_slice_u8 => return out_stream.writeAll("[]const u8"),
- .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
- .fn_void_no_args => return out_stream.writeAll("fn() void"),
- .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
- .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
- .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
+ => return writer.writeAll(@tagName(t)),
+
+ .enum_literal => return writer.writeAll("@Type(.EnumLiteral)"),
+ .@"null" => return writer.writeAll("@Type(.Null)"),
+ .@"undefined" => return writer.writeAll("@Type(.Undefined)"),
+
+ .empty_struct, .empty_struct_literal => return writer.writeAll("struct {}"),
+
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ return struct_obj.owner_decl.renderFullyQualifiedName(writer);
+ },
+ .enum_full, .enum_nonexhaustive => {
+ const enum_full = ty.cast(Payload.EnumFull).?.data;
+ return enum_full.owner_decl.renderFullyQualifiedName(writer);
+ },
+ .enum_simple => {
+ const enum_simple = ty.castTag(.enum_simple).?.data;
+ return enum_simple.owner_decl.renderFullyQualifiedName(writer);
+ },
+ .@"opaque" => {
+ // TODO use declaration name
+ return writer.writeAll("opaque {}");
+ },
+
+ .anyerror_void_error_union => return writer.writeAll("anyerror!void"),
+ .const_slice_u8 => return writer.writeAll("[]const u8"),
+ .fn_noreturn_no_args => return writer.writeAll("fn() noreturn"),
+ .fn_void_no_args => return writer.writeAll("fn() void"),
+ .fn_naked_noreturn_no_args => return writer.writeAll("fn() callconv(.Naked) noreturn"),
+ .fn_ccc_void_no_args => return writer.writeAll("fn() callconv(.C) void"),
+ .single_const_pointer_to_comptime_int => return writer.writeAll("*const comptime_int"),
.function => {
const payload = ty.castTag(.function).?.data;
- try out_stream.writeAll("fn(");
+ try writer.writeAll("fn(");
for (payload.param_types) |param_type, i| {
- if (i != 0) try out_stream.writeAll(", ");
- try param_type.format("", .{}, out_stream);
+ if (i != 0) try writer.writeAll(", ");
+ try param_type.format("", .{}, writer);
}
if (payload.is_var_args) {
if (payload.param_types.len != 0) {
- try out_stream.writeAll(", ");
+ try writer.writeAll(", ");
}
- try out_stream.writeAll("...");
+ try writer.writeAll("...");
}
- try out_stream.writeAll(") callconv(.");
- try out_stream.writeAll(@tagName(payload.cc));
- try out_stream.writeAll(")");
+ try writer.writeAll(") callconv(.");
+ try writer.writeAll(@tagName(payload.cc));
+ try writer.writeAll(")");
ty = payload.return_type;
continue;
},
.array_u8 => {
const len = ty.castTag(.array_u8).?.data;
- return out_stream.print("[{d}]u8", .{len});
+ return writer.print("[{d}]u8", .{len});
},
.array_u8_sentinel_0 => {
const len = ty.castTag(.array_u8_sentinel_0).?.data;
- return out_stream.print("[{d}:0]u8", .{len});
+ return writer.print("[{d}:0]u8", .{len});
},
.array => {
const payload = ty.castTag(.array).?.data;
- try out_stream.print("[{d}]", .{payload.len});
+ try writer.print("[{d}]", .{payload.len});
ty = payload.elem_type;
continue;
},
.array_sentinel => {
const payload = ty.castTag(.array_sentinel).?.data;
- try out_stream.print("[{d}:{}]", .{ payload.len, payload.sentinel });
+ try writer.print("[{d}:{}]", .{ payload.len, payload.sentinel });
ty = payload.elem_type;
continue;
},
.single_const_pointer => {
const pointee_type = ty.castTag(.single_const_pointer).?.data;
- try out_stream.writeAll("*const ");
+ try writer.writeAll("*const ");
ty = pointee_type;
continue;
},
.single_mut_pointer => {
const pointee_type = ty.castTag(.single_mut_pointer).?.data;
- try out_stream.writeAll("*");
+ try writer.writeAll("*");
ty = pointee_type;
continue;
},
.many_const_pointer => {
const pointee_type = ty.castTag(.many_const_pointer).?.data;
- try out_stream.writeAll("[*]const ");
+ try writer.writeAll("[*]const ");
ty = pointee_type;
continue;
},
.many_mut_pointer => {
const pointee_type = ty.castTag(.many_mut_pointer).?.data;
- try out_stream.writeAll("[*]");
+ try writer.writeAll("[*]");
ty = pointee_type;
continue;
},
.c_const_pointer => {
const pointee_type = ty.castTag(.c_const_pointer).?.data;
- try out_stream.writeAll("[*c]const ");
+ try writer.writeAll("[*c]const ");
ty = pointee_type;
continue;
},
.c_mut_pointer => {
const pointee_type = ty.castTag(.c_mut_pointer).?.data;
- try out_stream.writeAll("[*c]");
+ try writer.writeAll("[*c]");
ty = pointee_type;
continue;
},
.const_slice => {
const pointee_type = ty.castTag(.const_slice).?.data;
- try out_stream.writeAll("[]const ");
+ try writer.writeAll("[]const ");
ty = pointee_type;
continue;
},
.mut_slice => {
const pointee_type = ty.castTag(.mut_slice).?.data;
- try out_stream.writeAll("[]");
+ try writer.writeAll("[]");
ty = pointee_type;
continue;
},
.int_signed => {
const bits = ty.castTag(.int_signed).?.data;
- return out_stream.print("i{d}", .{bits});
+ return writer.print("i{d}", .{bits});
},
.int_unsigned => {
const bits = ty.castTag(.int_unsigned).?.data;
- return out_stream.print("u{d}", .{bits});
+ return writer.print("u{d}", .{bits});
},
.optional => {
const child_type = ty.castTag(.optional).?.data;
- try out_stream.writeByte('?');
+ try writer.writeByte('?');
ty = child_type;
continue;
},
.optional_single_const_pointer => {
const pointee_type = ty.castTag(.optional_single_const_pointer).?.data;
- try out_stream.writeAll("?*const ");
+ try writer.writeAll("?*const ");
ty = pointee_type;
continue;
},
.optional_single_mut_pointer => {
const pointee_type = ty.castTag(.optional_single_mut_pointer).?.data;
- try out_stream.writeAll("?*");
+ try writer.writeAll("?*");
ty = pointee_type;
continue;
},
@@ -804,48 +865,46 @@ pub const Type = extern union {
const payload = ty.castTag(.pointer).?.data;
if (payload.sentinel) |some| switch (payload.size) {
.One, .C => unreachable,
- .Many => try out_stream.print("[*:{}]", .{some}),
- .Slice => try out_stream.print("[:{}]", .{some}),
+ .Many => try writer.print("[*:{}]", .{some}),
+ .Slice => try writer.print("[:{}]", .{some}),
} else switch (payload.size) {
- .One => try out_stream.writeAll("*"),
- .Many => try out_stream.writeAll("[*]"),
- .C => try out_stream.writeAll("[*c]"),
- .Slice => try out_stream.writeAll("[]"),
+ .One => try writer.writeAll("*"),
+ .Many => try writer.writeAll("[*]"),
+ .C => try writer.writeAll("[*c]"),
+ .Slice => try writer.writeAll("[]"),
}
if (payload.@"align" != 0) {
- try out_stream.print("align({d}", .{payload.@"align"});
+ try writer.print("align({d}", .{payload.@"align"});
if (payload.bit_offset != 0) {
- try out_stream.print(":{d}:{d}", .{ payload.bit_offset, payload.host_size });
+ try writer.print(":{d}:{d}", .{ payload.bit_offset, payload.host_size });
}
- try out_stream.writeAll(") ");
+ try writer.writeAll(") ");
}
- if (!payload.mutable) try out_stream.writeAll("const ");
- if (payload.@"volatile") try out_stream.writeAll("volatile ");
- if (payload.@"allowzero") try out_stream.writeAll("allowzero ");
+ if (!payload.mutable) try writer.writeAll("const ");
+ if (payload.@"volatile") try writer.writeAll("volatile ");
+ if (payload.@"allowzero") try writer.writeAll("allowzero ");
ty = payload.pointee_type;
continue;
},
.error_union => {
const payload = ty.castTag(.error_union).?.data;
- try payload.error_set.format("", .{}, out_stream);
- try out_stream.writeAll("!");
+ try payload.error_set.format("", .{}, writer);
+ try writer.writeAll("!");
ty = payload.payload;
continue;
},
.error_set => {
const error_set = ty.castTag(.error_set).?.data;
- return out_stream.writeAll(std.mem.spanZ(error_set.owner_decl.name));
+ return writer.writeAll(std.mem.spanZ(error_set.owner_decl.name));
},
.error_set_single => {
const name = ty.castTag(.error_set_single).?.data;
- return out_stream.print("error{{{s}}}", .{name});
+ return writer.print("error{{{s}}}", .{name});
},
- .inferred_alloc_const => return out_stream.writeAll("(inferred_alloc_const)"),
- .inferred_alloc_mut => return out_stream.writeAll("(inferred_alloc_mut)"),
- // TODO use declaration name
- .@"opaque" => return out_stream.writeAll("opaque {}"),
+ .inferred_alloc_const => return writer.writeAll("(inferred_alloc_const)"),
+ .inferred_alloc_mut => return writer.writeAll("(inferred_alloc_mut)"),
}
unreachable;
}
@@ -954,6 +1013,19 @@ pub const Type = extern union {
return false;
}
},
+ .enum_full => {
+ const enum_full = self.castTag(.enum_full).?.data;
+ return enum_full.fields.count() >= 2;
+ },
+ .enum_simple => {
+ const enum_simple = self.castTag(.enum_simple).?.data;
+ return enum_simple.fields.count() >= 2;
+ },
+ .enum_nonexhaustive => {
+ var buffer: Payload.Bits = undefined;
+ const int_tag_ty = self.intTagType(&buffer);
+ return int_tag_ty.hasCodeGenBits();
+ },
// TODO lazy types
.array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0,
@@ -1112,13 +1184,37 @@ pub const Type = extern union {
} else if (!payload.payload.hasCodeGenBits()) {
return payload.error_set.abiAlignment(target);
}
- @panic("TODO abiAlignment error union");
+ return std.math.max(
+ payload.payload.abiAlignment(target),
+ payload.error_set.abiAlignment(target),
+ );
},
.@"struct" => {
- @panic("TODO abiAlignment struct");
+ // TODO take into account field alignment
+ // also make this possible to fail, and lazy
+ // I think we need to move all the functions from type.zig which can
+ // fail into Sema.
+ // Probably will need to introduce multi-stage struct resolution just
+ // like we have in stage1.
+ const struct_obj = self.castTag(.@"struct").?.data;
+ var biggest: u32 = 0;
+ for (struct_obj.fields.entries.items) |entry| {
+ const field_ty = entry.value.ty;
+ if (!field_ty.hasCodeGenBits()) continue;
+ const field_align = field_ty.abiAlignment(target);
+ if (field_align > biggest) {
+ return field_align;
+ }
+ }
+ assert(biggest != 0);
+ return biggest;
+ },
+ .enum_full, .enum_nonexhaustive, .enum_simple => {
+ var buffer: Payload.Bits = undefined;
+ const int_tag_ty = self.intTagType(&buffer);
+ return int_tag_ty.abiAlignment(target);
},
-
.c_void,
.void,
.type,
@@ -1166,6 +1262,11 @@ pub const Type = extern union {
.@"struct" => {
@panic("TODO abiSize struct");
},
+ .enum_simple, .enum_full, .enum_nonexhaustive => {
+ var buffer: Payload.Bits = undefined;
+ const int_tag_ty = self.intTagType(&buffer);
+ return int_tag_ty.abiSize(target);
+ },
.u8,
.i8,
@@ -1276,76 +1377,25 @@ pub const Type = extern union {
};
}
+ /// Asserts the type is an enum.
+ pub fn intTagType(self: Type, buffer: *Payload.Bits) Type {
+ switch (self.tag()) {
+ .enum_full, .enum_nonexhaustive => return self.cast(Payload.EnumFull).?.data.tag_ty,
+ .enum_simple => {
+ const enum_simple = self.castTag(.enum_simple).?.data;
+ const bits = std.math.log2_int_ceil(usize, enum_simple.fields.count());
+ buffer.* = .{
+ .base = .{ .tag = .int_unsigned },
+ .data = bits,
+ };
+ return Type.initPayload(&buffer.base);
+ },
+ else => unreachable,
+ }
+ }
+
pub fn isSinglePointer(self: Type) bool {
return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .const_slice_u8,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .@"opaque",
- .var_args_param,
- => false,
-
.single_const_pointer,
.single_mut_pointer,
.single_const_pointer_to_comptime_int,
@@ -1354,73 +1404,14 @@ pub const Type = extern union {
=> true,
.pointer => self.castTag(.pointer).?.data.size == .One,
+
+ else => false,
};
}
/// Asserts the `Type` is a pointer.
pub fn ptrSize(self: Type) std.builtin.TypeInfo.Pointer.Size {
return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .empty_struct,
- .empty_struct_literal,
- .@"opaque",
- .@"struct",
- .var_args_param,
- => unreachable,
-
.const_slice,
.mut_slice,
.const_slice_u8,
@@ -1442,159 +1433,26 @@ pub const Type = extern union {
=> .One,
.pointer => self.castTag(.pointer).?.data.size,
+
+ else => unreachable,
};
}
pub fn isSlice(self: Type) bool {
return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .single_const_pointer_to_comptime_int,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"struct",
- .@"opaque",
- .var_args_param,
- => false,
-
.const_slice,
.mut_slice,
.const_slice_u8,
=> true,
.pointer => self.castTag(.pointer).?.data.size == .Slice,
+
+ else => false,
};
}
pub fn isConstPtr(self: Type) bool {
return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .single_mut_pointer,
- .many_mut_pointer,
- .c_mut_pointer,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .mut_slice,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"struct",
- .@"opaque",
- .var_args_param,
- => false,
-
.single_const_pointer,
.many_const_pointer,
.c_const_pointer,
@@ -1604,170 +1462,40 @@ pub const Type = extern union {
=> true,
.pointer => !self.castTag(.pointer).?.data.mutable,
+
+ else => false,
};
}
pub fn isVolatilePtr(self: Type) bool {
return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .single_mut_pointer,
- .single_const_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"struct",
- .@"opaque",
- .var_args_param,
- => false,
-
.pointer => {
const payload = self.castTag(.pointer).?.data;
return payload.@"volatile";
},
+ else => false,
};
}
pub fn isAllowzeroPtr(self: Type) bool {
return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .int_unsigned,
- .int_signed,
- .single_mut_pointer,
- .single_const_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"struct",
- .@"opaque",
- .var_args_param,
- => false,
-
.pointer => {
const payload = self.castTag(.pointer).?.data;
return payload.@"allowzero";
},
+ else => false,
+ };
+ }
+
+ pub fn isCPtr(self: Type) bool {
+ return switch (self.tag()) {
+ .c_const_pointer,
+ .c_mut_pointer,
+ => return true,
+
+ .pointer => self.castTag(.pointer).?.data.size == .C,
+
+ else => return false,
};
}
@@ -1833,64 +1561,6 @@ pub const Type = extern union {
/// Asserts the type is a pointer or array type.
pub fn elemType(self: Type) Type {
return switch (self.tag()) {
- .u8 => unreachable,
- .i8 => unreachable,
- .u16 => unreachable,
- .i16 => unreachable,
- .u32 => unreachable,
- .i32 => unreachable,
- .u64 => unreachable,
- .i64 => unreachable,
- .u128 => unreachable,
- .i128 => unreachable,
- .usize => unreachable,
- .isize => unreachable,
- .c_short => unreachable,
- .c_ushort => unreachable,
- .c_int => unreachable,
- .c_uint => unreachable,
- .c_long => unreachable,
- .c_ulong => unreachable,
- .c_longlong => unreachable,
- .c_ulonglong => unreachable,
- .c_longdouble => unreachable,
- .f16 => unreachable,
- .f32 => unreachable,
- .f64 => unreachable,
- .f128 => unreachable,
- .c_void => unreachable,
- .bool => unreachable,
- .void => unreachable,
- .type => unreachable,
- .anyerror => unreachable,
- .comptime_int => unreachable,
- .comptime_float => unreachable,
- .noreturn => unreachable,
- .@"null" => unreachable,
- .@"undefined" => unreachable,
- .fn_noreturn_no_args => unreachable,
- .fn_void_no_args => unreachable,
- .fn_naked_noreturn_no_args => unreachable,
- .fn_ccc_void_no_args => unreachable,
- .function => unreachable,
- .int_unsigned => unreachable,
- .int_signed => unreachable,
- .optional => unreachable,
- .optional_single_const_pointer => unreachable,
- .optional_single_mut_pointer => unreachable,
- .enum_literal => unreachable,
- .error_union => unreachable,
- .anyerror_void_error_union => unreachable,
- .error_set => unreachable,
- .error_set_single => unreachable,
- .@"struct" => unreachable,
- .empty_struct => unreachable,
- .empty_struct_literal => unreachable,
- .inferred_alloc_const => unreachable,
- .inferred_alloc_mut => unreachable,
- .@"opaque" => unreachable,
- .var_args_param => unreachable,
-
.array => self.castTag(.array).?.data.elem_type,
.array_sentinel => self.castTag(.array_sentinel).?.data.elem_type,
.single_const_pointer,
@@ -1902,9 +1572,12 @@ pub const Type = extern union {
.const_slice,
.mut_slice,
=> self.castPointer().?.data,
+
.array_u8, .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
.single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
.pointer => self.castTag(.pointer).?.data.pointee_type,
+
+ else => unreachable,
};
}
@@ -1972,148 +1645,18 @@ pub const Type = extern union {
/// Asserts the type is an array or vector.
pub fn arrayLen(self: Type) u64 {
return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => unreachable,
-
.array => self.castTag(.array).?.data.len,
.array_sentinel => self.castTag(.array_sentinel).?.data.len,
.array_u8 => self.castTag(.array_u8).?.data,
.array_u8_sentinel_0 => self.castTag(.array_u8_sentinel_0).?.data,
+
+ else => unreachable,
};
}
/// Asserts the type is an array, pointer or vector.
pub fn sentinel(self: Type) ?Value {
return switch (self.tag()) {
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .const_slice,
- .mut_slice,
- .const_slice_u8,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => unreachable,
-
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
@@ -2128,6 +1671,8 @@ pub const Type = extern union {
.pointer => return self.castTag(.pointer).?.data.sentinel,
.array_sentinel => return self.castTag(.array_sentinel).?.data.sentinel,
.array_u8_sentinel_0 => return Value.initTag(.zero),
+
+ else => unreachable,
};
}
@@ -2139,68 +1684,6 @@ pub const Type = extern union {
/// Returns true if and only if the type is a fixed-width, signed integer.
pub fn isSignedInt(self: Type) bool {
return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .int_unsigned,
- .u8,
- .usize,
- .c_ushort,
- .c_uint,
- .c_ulong,
- .c_ulonglong,
- .u16,
- .u32,
- .u64,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => false,
-
.int_signed,
.i8,
.isize,
@@ -2211,79 +1694,16 @@ pub const Type = extern union {
.i16,
.i32,
.i64,
- .u128,
.i128,
=> true,
+
+ else => false,
};
}
/// Returns true if and only if the type is a fixed-width, unsigned integer.
pub fn isUnsignedInt(self: Type) bool {
return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .int_signed,
- .i8,
- .isize,
- .c_short,
- .c_int,
- .c_long,
- .c_longlong,
- .i16,
- .i32,
- .i64,
- .u128,
- .i128,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => false,
-
.int_unsigned,
.u8,
.usize,
@@ -2294,65 +1714,16 @@ pub const Type = extern union {
.u16,
.u32,
.u64,
+ .u128,
=> true,
+
+ else => false,
};
}
/// Asserts the type is an integer.
pub fn intInfo(self: Type, target: Target) struct { signedness: std.builtin.Signedness, bits: u16 } {
return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => unreachable,
-
.int_unsigned => .{
.signedness = .unsigned,
.bits = self.castTag(.int_unsigned).?.data,
@@ -2381,75 +1752,13 @@ pub const Type = extern union {
.c_ulong => .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) },
.c_longlong => .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) },
.c_ulonglong => .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) },
+
+ else => unreachable,
};
}
pub fn isNamedInt(self: Type) bool {
return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .int_unsigned,
- .int_signed,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => false,
-
.usize,
.isize,
.c_short,
@@ -2461,6 +1770,8 @@ pub const Type = extern union {
.c_longlong,
.c_ulonglong,
=> true,
+
+ else => false,
};
}
@@ -2499,74 +1810,7 @@ pub const Type = extern union {
.fn_ccc_void_no_args => 0,
.function => self.castTag(.function).?.data.param_types.len,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => unreachable,
+ else => unreachable,
};
}
@@ -2583,74 +1827,7 @@ pub const Type = extern union {
std.mem.copy(Type, types, payload.param_types);
},
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => unreachable,
+ else => unreachable,
}
}
@@ -2662,78 +1839,7 @@ pub const Type = extern union {
return payload.param_types[index];
},
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => unreachable,
+ else => unreachable,
}
}
@@ -2749,74 +1855,7 @@ pub const Type = extern union {
.function => self.castTag(.function).?.data.return_type,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => unreachable,
+ else => unreachable,
};
}
@@ -2829,74 +1868,7 @@ pub const Type = extern union {
.fn_ccc_void_no_args => .C,
.function => self.castTag(.function).?.data.cc,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => unreachable,
+ else => unreachable,
};
}
@@ -2909,74 +1881,7 @@ pub const Type = extern union {
.fn_ccc_void_no_args => false,
.function => self.castTag(.function).?.data.is_var_args,
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .comptime_int,
- .comptime_float,
- .noreturn,
- .@"null",
- .@"undefined",
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .int_unsigned,
- .int_signed,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => unreachable,
+ else => unreachable,
};
}
@@ -3013,55 +1918,12 @@ pub const Type = extern union {
.int_signed,
=> true,
- .c_void,
- .bool,
- .void,
- .type,
- .anyerror,
- .noreturn,
- .@"null",
- .@"undefined",
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => false,
+ else => false,
};
}
- pub fn onePossibleValue(self: Type) ?Value {
- var ty = self;
+ pub fn onePossibleValue(starting_type: Type) ?Value {
+ var ty = starting_type;
while (true) switch (ty.tag()) {
.f16,
.f32,
@@ -3127,6 +1989,23 @@ pub const Type = extern union {
}
return Value.initTag(.empty_struct_value);
},
+ .enum_full => {
+ const enum_full = ty.castTag(.enum_full).?.data;
+ if (enum_full.fields.count() == 1) {
+ return enum_full.values.entries.items[0].key;
+ } else {
+ return null;
+ }
+ },
+ .enum_simple => {
+ const enum_simple = ty.castTag(.enum_simple).?.data;
+ if (enum_simple.fields.count() == 1) {
+ return Value.initTag(.zero);
+ } else {
+ return null;
+ }
+ },
+ .enum_nonexhaustive => ty = ty.castTag(.enum_nonexhaustive).?.data.tag_ty,
.empty_struct, .empty_struct_literal => return Value.initTag(.empty_struct_value),
.void => return Value.initTag(.void_value),
@@ -3166,87 +2045,6 @@ pub const Type = extern union {
};
}
- pub fn isCPtr(self: Type) bool {
- return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .comptime_int,
- .comptime_float,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .bool,
- .type,
- .anyerror,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .c_void,
- .void,
- .noreturn,
- .@"null",
- .@"undefined",
- .int_unsigned,
- .int_signed,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .const_slice,
- .mut_slice,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .@"struct",
- .empty_struct,
- .empty_struct_literal,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .@"opaque",
- .var_args_param,
- => return false,
-
- .c_const_pointer,
- .c_mut_pointer,
- => return true,
-
- .pointer => self.castTag(.pointer).?.data.size == .C,
- };
- }
-
pub fn isIndexable(self: Type) bool {
const zig_tag = self.zigTypeTag();
// TODO tuples are indexable
@@ -3254,83 +2052,15 @@ pub const Type = extern union {
(self.isSinglePointer() and self.elemType().zigTypeTag() == .Array);
}
- /// Asserts that the type is a container. (note: ErrorSet is not a container).
- pub fn getContainerScope(self: Type) *Module.Scope.Container {
+ /// Returns null if the type has no container.
+ pub fn getContainerScope(self: Type) ?*Module.Scope.Container {
return switch (self.tag()) {
- .f16,
- .f32,
- .f64,
- .f128,
- .c_longdouble,
- .comptime_int,
- .comptime_float,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .bool,
- .type,
- .anyerror,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .function,
- .single_const_pointer_to_comptime_int,
- .const_slice_u8,
- .c_void,
- .void,
- .noreturn,
- .@"null",
- .@"undefined",
- .int_unsigned,
- .int_signed,
- .array,
- .array_sentinel,
- .array_u8,
- .array_u8_sentinel_0,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .const_slice,
- .mut_slice,
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- .enum_literal,
- .error_union,
- .anyerror_void_error_union,
- .error_set,
- .error_set_single,
- .c_const_pointer,
- .c_mut_pointer,
- .pointer,
- .inferred_alloc_const,
- .inferred_alloc_mut,
- .var_args_param,
- .empty_struct_literal,
- => unreachable,
-
.@"struct" => &self.castTag(.@"struct").?.data.container,
+ .enum_full => &self.castTag(.enum_full).?.data.container,
.empty_struct => self.castTag(.empty_struct).?.data,
.@"opaque" => &self.castTag(.@"opaque").?.data,
+
+ else => null,
};
}
@@ -3389,8 +2119,144 @@ pub const Type = extern union {
}
}
- pub fn isExhaustiveEnum(ty: Type) bool {
- return false; // TODO
+ pub fn isNonexhaustiveEnum(ty: Type) bool {
+ return switch (ty.tag()) {
+ .enum_nonexhaustive => true,
+ else => false,
+ };
+ }
+
+ pub fn enumFieldCount(ty: Type) usize {
+ switch (ty.tag()) {
+ .enum_full, .enum_nonexhaustive => {
+ const enum_full = ty.cast(Payload.EnumFull).?.data;
+ return enum_full.fields.count();
+ },
+ .enum_simple => {
+ const enum_simple = ty.castTag(.enum_simple).?.data;
+ return enum_simple.fields.count();
+ },
+ else => unreachable,
+ }
+ }
+
+ pub fn enumFieldName(ty: Type, field_index: usize) []const u8 {
+ switch (ty.tag()) {
+ .enum_full, .enum_nonexhaustive => {
+ const enum_full = ty.cast(Payload.EnumFull).?.data;
+ return enum_full.fields.entries.items[field_index].key;
+ },
+ .enum_simple => {
+ const enum_simple = ty.castTag(.enum_simple).?.data;
+ return enum_simple.fields.entries.items[field_index].key;
+ },
+ else => unreachable,
+ }
+ }
+
+ pub fn enumFieldIndex(ty: Type, field_name: []const u8) ?usize {
+ switch (ty.tag()) {
+ .enum_full, .enum_nonexhaustive => {
+ const enum_full = ty.cast(Payload.EnumFull).?.data;
+ return enum_full.fields.getIndex(field_name);
+ },
+ .enum_simple => {
+ const enum_simple = ty.castTag(.enum_simple).?.data;
+ return enum_simple.fields.getIndex(field_name);
+ },
+ else => unreachable,
+ }
+ }
+
+ /// Asserts `ty` is an enum. `enum_tag` can either be `enum_field_index` or
+ /// an integer which represents the enum value. Returns the field index in
+ /// declaration order, or `null` if `enum_tag` does not match any field.
+ pub fn enumTagFieldIndex(ty: Type, enum_tag: Value) ?usize {
+ if (enum_tag.castTag(.enum_field_index)) |payload| {
+ return @as(usize, payload.data);
+ }
+ const S = struct {
+ fn fieldWithRange(int_val: Value, end: usize) ?usize {
+ if (int_val.compareWithZero(.lt)) return null;
+ var end_payload: Value.Payload.U64 = .{
+ .base = .{ .tag = .int_u64 },
+ .data = end,
+ };
+ const end_val = Value.initPayload(&end_payload.base);
+ if (int_val.compare(.gte, end_val)) return null;
+ return int_val.toUnsignedInt();
+ }
+ };
+ switch (ty.tag()) {
+ .enum_full, .enum_nonexhaustive => {
+ const enum_full = ty.cast(Payload.EnumFull).?.data;
+ if (enum_full.values.count() == 0) {
+ return S.fieldWithRange(enum_tag, enum_full.fields.count());
+ } else {
+ return enum_full.values.getIndex(enum_tag);
+ }
+ },
+ .enum_simple => {
+ const enum_simple = ty.castTag(.enum_simple).?.data;
+ return S.fieldWithRange(enum_tag, enum_simple.fields.count());
+ },
+ else => unreachable,
+ }
+ }
+
+ pub fn declSrcLoc(ty: Type) Module.SrcLoc {
+ switch (ty.tag()) {
+ .enum_full, .enum_nonexhaustive => {
+ const enum_full = ty.cast(Payload.EnumFull).?.data;
+ return enum_full.srcLoc();
+ },
+ .enum_simple => {
+ const enum_simple = ty.castTag(.enum_simple).?.data;
+ return enum_simple.srcLoc();
+ },
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ return struct_obj.srcLoc();
+ },
+ .error_set => {
+ const error_set = ty.castTag(.error_set).?.data;
+ return error_set.srcLoc();
+ },
+ else => unreachable,
+ }
+ }
+
+ /// Asserts the type is an enum.
+ pub fn enumHasInt(ty: Type, int: Value, target: Target) bool {
+ const S = struct {
+ fn intInRange(int_val: Value, end: usize) bool {
+ if (int_val.compareWithZero(.lt)) return false;
+ var end_payload: Value.Payload.U64 = .{
+ .base = .{ .tag = .int_u64 },
+ .data = end,
+ };
+ const end_val = Value.initPayload(&end_payload.base);
+ if (int_val.compare(.gte, end_val)) return false;
+ return true;
+ }
+ };
+ switch (ty.tag()) {
+ .enum_nonexhaustive => return int.intFitsInType(ty, target),
+ .enum_full => {
+ const enum_full = ty.castTag(.enum_full).?.data;
+ if (enum_full.values.count() == 0) {
+ return S.intInRange(int, enum_full.fields.count());
+ } else {
+ return enum_full.values.contains(int);
+ }
+ },
+ .enum_simple => {
+ const enum_simple = ty.castTag(.enum_simple).?.data;
+ return S.intInRange(int, enum_simple.fields.count());
+ },
+
+ else => unreachable,
+ }
}
/// This enum does not directly correspond to `std.builtin.TypeId` because
@@ -3482,6 +2348,9 @@ pub const Type = extern union {
empty_struct,
@"opaque",
@"struct",
+ enum_simple,
+ enum_full,
+ enum_nonexhaustive,
pub const last_no_payload_tag = Tag.inferred_alloc_const;
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
@@ -3568,6 +2437,8 @@ pub const Type = extern union {
.error_set_single => Payload.Name,
.@"opaque" => Payload.Opaque,
.@"struct" => Payload.Struct,
+ .enum_full, .enum_nonexhaustive => Payload.EnumFull,
+ .enum_simple => Payload.EnumSimple,
.empty_struct => Payload.ContainerScope,
};
}
@@ -3705,6 +2576,16 @@ pub const Type = extern union {
base: Payload = .{ .tag = .@"struct" },
data: *Module.Struct,
};
+
+ pub const EnumFull = struct {
+ base: Payload,
+ data: *Module.EnumFull,
+ };
+
+ pub const EnumSimple = struct {
+ base: Payload = .{ .tag = .enum_simple },
+ data: *Module.EnumSimple,
+ };
};
};
diff --git a/src/value.zig b/src/value.zig
index 4170b005bf..66a23692c1 100644
--- a/src/value.zig
+++ b/src/value.zig
@@ -103,6 +103,8 @@ pub const Value = extern union {
float_64,
float_128,
enum_literal,
+ /// A specific enum tag, indicated by the field index (declaration order).
+ enum_field_index,
@"error",
error_union,
/// This is a special value that tracks a set of types that have been stored
@@ -186,6 +188,8 @@ pub const Value = extern union {
.enum_literal,
=> Payload.Bytes,
+ .enum_field_index => Payload.U32,
+
.ty => Payload.Ty,
.int_type => Payload.IntType,
.int_u64 => Payload.U64,
@@ -394,6 +398,7 @@ pub const Value = extern union {
};
return Value{ .ptr_otherwise = &new_payload.base };
},
+ .enum_field_index => return self.copyPayloadShallow(allocator, Payload.U32),
.@"error" => return self.copyPayloadShallow(allocator, Payload.Error),
.error_union => {
const payload = self.castTag(.error_union).?;
@@ -416,6 +421,8 @@ pub const Value = extern union {
return Value{ .ptr_otherwise = &new_payload.base };
}
+ /// TODO this should become a debug dump() function. In order to print values in a meaningful way
+ /// we also need access to the type.
pub fn format(
self: Value,
comptime fmt: []const u8,
@@ -506,6 +513,7 @@ pub const Value = extern union {
},
.empty_array => return out_stream.writeAll(".{}"),
.enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(self.castTag(.enum_literal).?.data)}),
+ .enum_field_index => return out_stream.print("(enum field {d})", .{self.castTag(.enum_field_index).?.data}),
.bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(self.castTag(.bytes).?.data)}),
.repeated => {
try out_stream.writeAll("(repeated) ");
@@ -626,6 +634,7 @@ pub const Value = extern union {
.float_64,
.float_128,
.enum_literal,
+ .enum_field_index,
.@"error",
.error_union,
.empty_struct_value,
@@ -638,76 +647,6 @@ pub const Value = extern union {
/// Asserts the value is an integer.
pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst {
switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .u128_type,
- .i128_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .null_value,
- .function,
- .extern_fn,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .error_union,
- .@"error",
- .empty_struct_value,
- .inferred_alloc,
- .abi_align_default,
- => unreachable,
-
- .undef => unreachable,
-
.zero,
.bool_false,
=> return BigIntMutable.init(&space.limbs, 0).toConst(),
@@ -720,82 +659,15 @@ pub const Value = extern union {
.int_i64 => return BigIntMutable.init(&space.limbs, self.castTag(.int_i64).?.data).toConst(),
.int_big_positive => return self.castTag(.int_big_positive).?.asBigInt(),
.int_big_negative => return self.castTag(.int_big_negative).?.asBigInt(),
+
+ .undef => unreachable,
+ else => unreachable,
}
}
/// Asserts the value is an integer and it fits in a u64
pub fn toUnsignedInt(self: Value) u64 {
switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .u128_type,
- .i128_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .null_value,
- .function,
- .extern_fn,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .@"error",
- .error_union,
- .empty_struct_value,
- .inferred_alloc,
- .abi_align_default,
- => unreachable,
-
- .undef => unreachable,
-
.zero,
.bool_false,
=> return 0,
@@ -808,82 +680,15 @@ pub const Value = extern union {
.int_i64 => return @intCast(u64, self.castTag(.int_i64).?.data),
.int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().to(u64) catch unreachable,
.int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().to(u64) catch unreachable,
+
+ .undef => unreachable,
+ else => unreachable,
}
}
/// Asserts the value is an integer and it fits in a i64
pub fn toSignedInt(self: Value) i64 {
switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .u128_type,
- .i128_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .null_value,
- .function,
- .extern_fn,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .@"error",
- .error_union,
- .empty_struct_value,
- .inferred_alloc,
- .abi_align_default,
- => unreachable,
-
- .undef => unreachable,
-
.zero,
.bool_false,
=> return 0,
@@ -896,6 +701,9 @@ pub const Value = extern union {
.int_i64 => return self.castTag(.int_i64).?.data,
.int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().to(i64) catch unreachable,
.int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().to(i64) catch unreachable,
+
+ .undef => unreachable,
+ else => unreachable,
}
}
@@ -929,75 +737,6 @@ pub const Value = extern union {
/// Returns the number of bits the value requires to represent stored in twos complement form.
pub fn intBitCountTwosComp(self: Value) usize {
switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .u128_type,
- .i128_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .null_value,
- .function,
- .extern_fn,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .undef,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .@"error",
- .error_union,
- .empty_struct_value,
- .inferred_alloc,
- .abi_align_default,
- => unreachable,
-
.zero,
.bool_false,
=> return 0,
@@ -1016,80 +755,14 @@ pub const Value = extern union {
},
.int_big_positive => return self.castTag(.int_big_positive).?.asBigInt().bitCountTwosComp(),
.int_big_negative => return self.castTag(.int_big_negative).?.asBigInt().bitCountTwosComp(),
+
+ else => unreachable,
}
}
/// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
pub fn intFitsInType(self: Value, ty: Type, target: Target) bool {
switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .u128_type,
- .i128_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .null_value,
- .function,
- .extern_fn,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .@"error",
- .error_union,
- .empty_struct_value,
- .inferred_alloc,
- .abi_align_default,
- => unreachable,
-
.zero,
.undef,
.bool_false,
@@ -1144,6 +817,8 @@ pub const Value = extern union {
.ComptimeInt => return true,
else => unreachable,
},
+
+ else => unreachable,
}
}
@@ -1180,77 +855,6 @@ pub const Value = extern union {
/// Asserts the value is a float
pub fn floatHasFraction(self: Value) bool {
return switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .u128_type,
- .i128_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .bool_true,
- .bool_false,
- .null_value,
- .function,
- .extern_fn,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .undef,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .empty_array,
- .void_value,
- .unreachable_value,
- .enum_literal,
- .@"error",
- .error_union,
- .empty_struct_value,
- .inferred_alloc,
- .abi_align_default,
- => unreachable,
-
.zero,
.one,
=> false,
@@ -1260,76 +864,13 @@ pub const Value = extern union {
.float_64 => @rem(self.castTag(.float_64).?.data, 1) != 0,
// .float_128 => @rem(self.castTag(.float_128).?.data, 1) != 0,
.float_128 => @panic("TODO lld: error: undefined symbol: fmodl"),
+
+ else => unreachable,
};
}
pub fn orderAgainstZero(lhs: Value) std.math.Order {
return switch (lhs.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .u128_type,
- .i128_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .null_value,
- .function,
- .extern_fn,
- .variable,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .undef,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .@"error",
- .error_union,
- .empty_struct_value,
- .inferred_alloc,
- .abi_align_default,
- => unreachable,
-
.zero,
.bool_false,
=> .eq,
@@ -1347,6 +888,8 @@ pub const Value = extern union {
.float_32 => std.math.order(lhs.castTag(.float_32).?.data, 0),
.float_64 => std.math.order(lhs.castTag(.float_64).?.data, 0),
.float_128 => std.math.order(lhs.castTag(.float_128).?.data, 0),
+
+ else => unreachable,
};
}
@@ -1396,10 +939,12 @@ pub const Value = extern union {
}
pub fn eql(a: Value, b: Value) bool {
- if (a.tag() == b.tag()) {
- if (a.tag() == .void_value or a.tag() == .null_value) {
+ const a_tag = a.tag();
+ const b_tag = b.tag();
+ if (a_tag == b_tag) {
+ if (a_tag == .void_value or a_tag == .null_value) {
return true;
- } else if (a.tag() == .enum_literal) {
+ } else if (a_tag == .enum_literal) {
const a_name = a.castTag(.enum_literal).?.data;
const b_name = b.castTag(.enum_literal).?.data;
return std.mem.eql(u8, a_name, b_name);
@@ -1416,6 +961,10 @@ pub const Value = extern union {
return compare(a, .eq, b);
}
+ pub fn hash_u32(self: Value) u32 {
+ return @truncate(u32, self.hash());
+ }
+
pub fn hash(self: Value) u64 {
var hasher = std.hash.Wyhash.init(0);
@@ -1493,11 +1042,18 @@ pub const Value = extern union {
.zero, .bool_false => std.hash.autoHash(&hasher, @as(u64, 0)),
.one, .bool_true => std.hash.autoHash(&hasher, @as(u64, 1)),
- .float_16, .float_32, .float_64, .float_128 => {},
+ .float_16, .float_32, .float_64, .float_128 => {
+ @panic("TODO implement Value.hash for floats");
+ },
+
.enum_literal => {
const payload = self.castTag(.enum_literal).?;
hasher.update(payload.data);
},
+ .enum_field_index => {
+ const payload = self.castTag(.enum_field_index).?;
+ std.hash.autoHash(&hasher, payload.data);
+ },
.bytes => {
const payload = self.castTag(.bytes).?;
hasher.update(payload.data);
@@ -1573,80 +1129,6 @@ pub const Value = extern union {
/// Returns error.AnalysisFail if the pointer points to a Decl that failed semantic analysis.
pub fn pointerDeref(self: Value, allocator: *Allocator) error{ AnalysisFail, OutOfMemory }!Value {
return switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .u128_type,
- .i128_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .zero,
- .one,
- .bool_true,
- .bool_false,
- .null_value,
- .function,
- .extern_fn,
- .variable,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .bytes,
- .undef,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .empty_array,
- .enum_literal,
- .@"error",
- .error_union,
- .empty_struct_value,
- .inferred_alloc,
- .abi_align_default,
- => unreachable,
-
.ref_val => self.castTag(.ref_val).?.data,
.decl_ref => self.castTag(.decl_ref).?.data.value(),
.elem_ptr => {
@@ -1654,6 +1136,8 @@ pub const Value = extern union {
const array_val = try elem_ptr.array_ptr.pointerDeref(allocator);
return array_val.elemValue(allocator, elem_ptr.index);
},
+
+ else => unreachable,
};
}
@@ -1661,86 +1145,14 @@ pub const Value = extern union {
/// or an unknown-length pointer, and returns the element value at the index.
pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value {
switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .u128_type,
- .i128_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .zero,
- .one,
- .bool_true,
- .bool_false,
- .null_value,
- .function,
- .extern_fn,
- .variable,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .undef,
- .elem_ptr,
- .ref_val,
- .decl_ref,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .unreachable_value,
- .enum_literal,
- .@"error",
- .error_union,
- .empty_struct_value,
- .inferred_alloc,
- .abi_align_default,
- => unreachable,
-
.empty_array => unreachable, // out of bounds array index
.bytes => return Tag.int_u64.create(allocator, self.castTag(.bytes).?.data[index]),
// No matter the index; all the elements are the same!
.repeated => return self.castTag(.repeated).?.data,
+
+ else => unreachable,
}
}
@@ -1766,161 +1178,18 @@ pub const Value = extern union {
/// Valid for all types. Asserts the value is not undefined and not unreachable.
pub fn isNull(self: Value) bool {
return switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .u128_type,
- .i128_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .zero,
- .one,
- .empty_array,
- .bool_true,
- .bool_false,
- .function,
- .extern_fn,
- .variable,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .enum_literal,
- .@"error",
- .error_union,
- .empty_struct_value,
- .abi_align_default,
- => false,
-
.undef => unreachable,
.unreachable_value => unreachable,
.inferred_alloc => unreachable,
.null_value => true,
+
+ else => false,
};
}
/// Valid for all types. Asserts the value is not undefined and not unreachable.
pub fn getError(self: Value) ?[]const u8 {
return switch (self.tag()) {
- .ty,
- .int_type,
- .u8_type,
- .i8_type,
- .u16_type,
- .i16_type,
- .u32_type,
- .i32_type,
- .u64_type,
- .i64_type,
- .u128_type,
- .i128_type,
- .usize_type,
- .isize_type,
- .c_short_type,
- .c_ushort_type,
- .c_int_type,
- .c_uint_type,
- .c_long_type,
- .c_ulong_type,
- .c_longlong_type,
- .c_ulonglong_type,
- .c_longdouble_type,
- .f16_type,
- .f32_type,
- .f64_type,
- .f128_type,
- .c_void_type,
- .bool_type,
- .void_type,
- .type_type,
- .anyerror_type,
- .comptime_int_type,
- .comptime_float_type,
- .noreturn_type,
- .null_type,
- .undefined_type,
- .fn_noreturn_no_args_type,
- .fn_void_no_args_type,
- .fn_naked_noreturn_no_args_type,
- .fn_ccc_void_no_args_type,
- .single_const_pointer_to_comptime_int_type,
- .const_slice_u8_type,
- .enum_literal_type,
- .zero,
- .one,
- .null_value,
- .empty_array,
- .bool_true,
- .bool_false,
- .function,
- .extern_fn,
- .variable,
- .int_u64,
- .int_i64,
- .int_big_positive,
- .int_big_negative,
- .ref_val,
- .decl_ref,
- .elem_ptr,
- .bytes,
- .repeated,
- .float_16,
- .float_32,
- .float_64,
- .float_128,
- .void_value,
- .enum_literal,
- .empty_struct_value,
- .abi_align_default,
- => null,
-
.error_union => {
const data = self.castTag(.error_union).?.data;
return if (data.tag() == .@"error")
@@ -1932,6 +1201,8 @@ pub const Value = extern union {
.undef => unreachable,
.unreachable_value => unreachable,
.inferred_alloc => unreachable,
+
+ else => null,
};
}
/// Valid for all types. Asserts the value is not undefined.
@@ -2021,6 +1292,7 @@ pub const Value = extern union {
.float_128,
.void_value,
.enum_literal,
+ .enum_field_index,
.@"error",
.error_union,
.empty_struct_value,
@@ -2038,6 +1310,11 @@ pub const Value = extern union {
pub const Payload = struct {
tag: Tag,
+ pub const U32 = struct {
+ base: Payload,
+ data: u32,
+ };
+
pub const U64 = struct {
base: Payload,
data: u64,
diff --git a/src/zir.zig b/src/zir.zig
index c70ef17fcd..d3d9d56bfd 100644
--- a/src/zir.zig
+++ b/src/zir.zig
@@ -37,8 +37,6 @@ pub const Code = struct {
string_bytes: []u8,
/// The meaning of this data is determined by `Inst.Tag` value.
extra: []u32,
- /// Used for decl_val and decl_ref instructions.
- decls: []*Module.Decl,
/// Returns the requested data, as well as the new index which is at the start of the
/// trailers for the object.
@@ -78,7 +76,6 @@ pub const Code = struct {
code.instructions.deinit(gpa);
gpa.free(code.string_bytes);
gpa.free(code.extra);
- gpa.free(code.decls);
code.* = undefined;
}
@@ -133,7 +130,7 @@ pub const Inst = struct {
/// Same as `alloc` except mutable.
alloc_mut,
/// Same as `alloc` except the type is inferred.
- /// The operand is unused.
+ /// Uses the `node` union field.
alloc_inferred,
/// Same as `alloc_inferred` except mutable.
alloc_inferred_mut,
@@ -267,9 +264,6 @@ pub const Inst = struct {
/// only the taken branch is analyzed. The then block and else block must
/// terminate with an "inline" variant of a noreturn instruction.
condbr_inline,
- /// A comptime known value.
- /// Uses the `const` union field.
- @"const",
/// A struct type definition. Contains references to ZIR instructions for
/// the field types, defaults, and alignments.
/// Uses the `pl_node` union field. Payload is `StructDecl`.
@@ -286,6 +280,8 @@ pub const Inst = struct {
/// the field value expressions and optional type tag expression.
/// Uses the `pl_node` union field. Payload is `EnumDecl`.
enum_decl,
+ /// Same as `enum_decl`, except the enum is non-exhaustive.
+ enum_decl_nonexhaustive,
/// An opaque type definition. Provides an AST node only.
/// Uses the `node` union field.
opaque_decl,
@@ -369,6 +365,11 @@ pub const Inst = struct {
import,
/// Integer literal that fits in a u64. Uses the int union value.
int,
+ /// A float literal that fits in a f32. Uses the float union value.
+ float,
+ /// A float literal that fits in a f128. Uses the `pl_node` union value.
+ /// Payload is `Float128`.
+ float128,
/// Convert an integer value to another integer type, asserting that the destination type
/// can hold the same mathematical value.
/// Uses the `pl_node` field. AST is the `@intCast` syntax.
@@ -667,6 +668,12 @@ pub const Inst = struct {
/// A struct literal with a specified type, with no fields.
/// Uses the `un_node` field.
struct_init_empty,
+ /// Converts an integer into an enum value.
+ /// Uses `pl_node` with payload `Bin`. `lhs` is enum type, `rhs` is operand.
+ int_to_enum,
+ /// Converts an enum value into an integer. Resulting type will be the tag type
+ /// of the enum. Uses `un_node`.
+ enum_to_int,
/// Returns whether the instruction is one of the control flow "noreturn" types.
/// Function calls do not count.
@@ -712,12 +719,12 @@ pub const Inst = struct {
.cmp_gt,
.cmp_neq,
.coerce_result_ptr,
- .@"const",
.struct_decl,
.struct_decl_packed,
.struct_decl_extern,
.union_decl,
.enum_decl,
+ .enum_decl_nonexhaustive,
.opaque_decl,
.dbg_stmt_node,
.decl_ref,
@@ -740,6 +747,8 @@ pub const Inst = struct {
.fn_type_cc,
.fn_type_cc_var_args,
.int,
+ .float,
+ .float128,
.intcast,
.int_type,
.is_non_null,
@@ -822,6 +831,8 @@ pub const Inst = struct {
.switch_block_ref_under_multi,
.validate_struct_init_ptr,
.struct_init_empty,
+ .int_to_enum,
+ .enum_to_int,
=> false,
.@"break",
@@ -1184,7 +1195,6 @@ pub const Inst = struct {
}
},
bin: Bin,
- @"const": *TypedValue,
/// For strings which may contain null bytes.
str: struct {
/// Offset into `string_bytes`.
@@ -1226,6 +1236,16 @@ pub const Inst = struct {
/// Offset from Decl AST node index.
node: i32,
int: u64,
+ float: struct {
+ /// Offset from Decl AST node index.
+ /// `Tag` determines which kind of AST node this points to.
+ src_node: i32,
+ number: f32,
+
+ pub fn src(self: @This()) LazySrcLoc {
+ return .{ .node_offset = self.src_node };
+ }
+ },
array_type_sentinel: struct {
len: Ref,
/// index into extra, points to an `ArrayTypeSentinel`
@@ -1507,6 +1527,22 @@ pub const Inst = struct {
tag_type: Ref,
fields_len: u32,
};
+
+ /// A f128 value, broken up into 4 u32 parts.
+ pub const Float128 = struct {
+ piece0: u32,
+ piece1: u32,
+ piece2: u32,
+ piece3: u32,
+
+ pub fn get(self: Float128) f128 {
+ const int_bits = @as(u128, self.piece0) |
+ (@as(u128, self.piece1) << 32) |
+ (@as(u128, self.piece2) << 64) |
+ (@as(u128, self.piece3) << 96);
+ return @bitCast(f128, int_bits);
+ }
+ };
};
pub const SpecialProng = enum { none, @"else", under };
@@ -1536,12 +1572,11 @@ const Writer = struct {
.intcast,
.store,
.store_to_block_ptr,
+ .store_to_inferred_ptr,
=> try self.writeBin(stream, inst),
.alloc,
.alloc_mut,
- .alloc_inferred,
- .alloc_inferred_mut,
.indexable_ptr_len,
.bit_not,
.bool_not,
@@ -1581,6 +1616,7 @@ const Writer = struct {
.typeof,
.typeof_elem,
.struct_init_empty,
+ .enum_to_int,
=> try self.writeUnNode(stream, inst),
.ref,
@@ -1594,11 +1630,12 @@ const Writer = struct {
=> try self.writeBoolBr(stream, inst),
.array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst),
- .@"const" => try self.writeConst(stream, inst),
.param_type => try self.writeParamType(stream, inst),
.ptr_type_simple => try self.writePtrTypeSimple(stream, inst),
.ptr_type => try self.writePtrType(stream, inst),
.int => try self.writeInt(stream, inst),
+ .float => try self.writeFloat(stream, inst),
+ .float128 => try self.writeFloat128(stream, inst),
.str => try self.writeStr(stream, inst),
.elided => try stream.writeAll(")"),
.int_type => try self.writeIntType(stream, inst),
@@ -1619,6 +1656,7 @@ const Writer = struct {
.slice_sentinel,
.union_decl,
.enum_decl,
+ .enum_decl_nonexhaustive,
=> try self.writePlNode(stream, inst),
.add,
@@ -1647,6 +1685,7 @@ const Writer = struct {
.merge_error_sets,
.bit_and,
.bit_or,
+ .int_to_enum,
=> try self.writePlNodeBin(stream, inst),
.call,
@@ -1704,6 +1743,8 @@ const Writer = struct {
.ret_type,
.repeat,
.repeat_inline,
+ .alloc_inferred,
+ .alloc_inferred_mut,
=> try self.writeNode(stream, inst),
.error_value,
@@ -1729,7 +1770,6 @@ const Writer = struct {
.bitcast,
.bitcast_result_ptr,
- .store_to_inferred_ptr,
=> try stream.writeAll("TODO)"),
}
}
@@ -1773,15 +1813,6 @@ const Writer = struct {
try stream.writeAll("TODO)");
}
- fn writeConst(
- self: *Writer,
- stream: anytype,
- inst: Inst.Index,
- ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
- const inst_data = self.code.instructions.items(.data)[inst].@"const";
- try stream.writeAll("TODO)");
- }
-
fn writeParamType(
self: *Writer,
stream: anytype,
@@ -1819,6 +1850,23 @@ const Writer = struct {
try stream.print("{d})", .{inst_data});
}
+ fn writeFloat(self: *Writer, stream: anytype, inst: Inst.Index) !void {
+ const inst_data = self.code.instructions.items(.data)[inst].float;
+ const src = inst_data.src();
+ try stream.print("{d}) ", .{inst_data.number});
+ try self.writeSrc(stream, src);
+ }
+
+ fn writeFloat128(self: *Writer, stream: anytype, inst: Inst.Index) !void {
+ const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+ const extra = self.code.extraData(Inst.Float128, inst_data.payload_index).data;
+ const src = inst_data.src();
+ const number = extra.get();
+ // TODO improve std.format to be able to print f128 values
+ try stream.print("{d}) ", .{@floatCast(f64, number)});
+ try self.writeSrc(stream, src);
+ }
+
fn writeStr(
self: *Writer,
stream: anytype,
@@ -2136,7 +2184,8 @@ const Writer = struct {
fn writePlNodeDecl(self: *Writer, stream: anytype, inst: Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
- const decl = self.code.decls[inst_data.payload_index];
+ const owner_decl = self.scope.ownerDecl().?;
+ const decl = owner_decl.dependencies.entries.items[inst_data.payload_index].key;
try stream.print("{s}) ", .{decl.name});
try self.writeSrc(stream, inst_data.src());
}