aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-08-28 12:41:24 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-08-28 12:41:24 -0700
commit6aeab0f323ff14d7ad248e18c372573f7a5a8cd1 (patch)
tree7080297f629f39baa0b742985c5804cf6a2047e8 /src
parent47f7ed1c4cb8acf7fed99a057fb84202962e4b1b (diff)
parent05cf44933d753f7a5a53ab289ea60fd43761de57 (diff)
downloadzig-6aeab0f323ff14d7ad248e18c372573f7a5a8cd1.tar.gz
zig-6aeab0f323ff14d7ad248e18c372573f7a5a8cd1.zip
Merge remote-tracking branch 'origin/master' into llvm13
Conflicts: lib/libcxx/include/__config d57c0cc3bfeff9af297279759ec2b631e6d95140 added support for DragonFlyBSD to libc++ by updating some ifdefs. This needed to be synced with llvm13.
Diffstat (limited to 'src')
-rw-r--r--src/Air.zig33
-rw-r--r--src/AstGen.zig264
-rw-r--r--src/Compilation.zig1
-rw-r--r--src/Liveness.zig10
-rw-r--r--src/Module.zig398
-rw-r--r--src/Sema.zig948
-rw-r--r--src/TypedValue.zig15
-rw-r--r--src/Zir.zig16
-rw-r--r--src/codegen.zig183
-rw-r--r--src/codegen/arm.zig206
-rw-r--r--src/codegen/c.zig55
-rw-r--r--src/codegen/llvm.zig120
-rw-r--r--src/codegen/llvm/bindings.zig17
-rw-r--r--src/codegen/wasm.zig26
-rw-r--r--src/link/Wasm.zig8
-rw-r--r--src/main.zig1
-rw-r--r--src/mingw.zig36
-rw-r--r--src/print_air.zig22
-rw-r--r--src/stage1/all_types.hpp1
-rw-r--r--src/stage1/analyze.cpp48
-rw-r--r--src/stage1/astgen.cpp111
-rw-r--r--src/stage1/ir.cpp39
-rw-r--r--src/stage1/parser.cpp19
-rw-r--r--src/stage1/parser.hpp1
-rw-r--r--src/stage1/target.cpp2
-rw-r--r--src/target.zig2
-rw-r--r--src/translate_c.zig79
-rw-r--r--src/translate_c/ast.zig66
-rw-r--r--src/type.zig39
-rw-r--r--src/value.zig137
30 files changed, 2016 insertions, 887 deletions
diff --git a/src/Air.zig b/src/Air.zig
index 391683afd5..6e4125be44 100644
--- a/src/Air.zig
+++ b/src/Air.zig
@@ -94,6 +94,12 @@ pub const Inst = struct {
/// Result type is the same as both operands.
/// Uses the `bin_op` field.
bit_or,
+ /// Shift right. `>>`
+ /// Uses the `bin_op` field.
+ shr,
+ /// Shift left. `<<`
+ /// Uses the `bin_op` field.
+ shl,
/// Bitwise XOR. `^`
/// Uses the `bin_op` field.
xor,
@@ -258,6 +264,13 @@ pub const Inst = struct {
/// Given a pointer to a struct and a field index, returns a pointer to the field.
/// Uses the `ty_pl` field, payload is `StructField`.
struct_field_ptr,
+ /// Given a pointer to a struct, returns a pointer to the field.
+ /// The field index is the number at the end of the name.
+ /// Uses `ty_op` field.
+ struct_field_ptr_index_0,
+ struct_field_ptr_index_1,
+ struct_field_ptr_index_2,
+ struct_field_ptr_index_3,
/// Given a byval struct and a field index, returns the field byval.
/// Uses the `ty_pl` field, payload is `StructField`.
struct_field_val,
@@ -280,6 +293,10 @@ pub const Inst = struct {
/// Result type is the element type of the pointer operand.
/// Uses the `bin_op` field.
ptr_elem_val,
+ /// Given a pointer value, and element index, return the element pointer at that index.
+ /// Result type is pointer to the element type of the pointer operand.
+ /// Uses the `ty_pl` field with payload `Bin`.
+ ptr_elem_ptr,
/// Given a pointer to a pointer, and element index, return the element value of the inner
/// pointer at that index.
/// Result type is the element type of the inner pointer operand.
@@ -404,6 +421,11 @@ pub const StructField = struct {
field_index: u32,
};
+pub const Bin = struct {
+ lhs: Inst.Ref,
+ rhs: Inst.Ref,
+};
+
/// Trailing:
/// 0. `Inst.Ref` for every outputs_len
/// 1. `Inst.Ref` for every inputs_len
@@ -445,6 +467,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.xor,
.ptr_add,
.ptr_sub,
+ .shr,
+ .shl,
=> return air.typeOf(datas[inst].bin_op.lhs),
.cmp_lt,
@@ -474,6 +498,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.constant,
.struct_field_ptr,
.struct_field_val,
+ .ptr_elem_ptr,
=> return air.getRefType(datas[inst].ty_pl.ty),
.not,
@@ -492,6 +517,10 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.wrap_errunion_payload,
.wrap_errunion_err,
.slice_ptr,
+ .struct_field_ptr_index_0,
+ .struct_field_ptr_index_1,
+ .struct_field_ptr_index_2,
+ .struct_field_ptr_index_3,
=> return air.getRefType(datas[inst].ty_op.ty),
.loop,
@@ -519,8 +548,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
},
.slice_elem_val, .ptr_elem_val => {
- const slice_ty = air.typeOf(datas[inst].bin_op.lhs);
- return slice_ty.elemType();
+ const ptr_ty = air.typeOf(datas[inst].bin_op.lhs);
+ return ptr_ty.elemType();
},
.ptr_slice_elem_val, .ptr_ptr_elem_val => {
const outer_ptr_ty = air.typeOf(datas[inst].bin_op.lhs);
diff --git a/src/AstGen.zig b/src/AstGen.zig
index e3f33ec332..0a34e9a0ca 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -370,10 +370,6 @@ fn lvalExpr(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Ins
.bool_not,
.address_of,
.float_literal,
- .undefined_literal,
- .true_literal,
- .false_literal,
- .null_literal,
.optional_type,
.block,
.block_semicolon,
@@ -698,7 +694,13 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
.lhs = lhs,
.start = start,
});
- return rvalue(gz, rl, result, node);
+ switch (rl) {
+ .ref, .none_or_ref => return result,
+ else => {
+ const dereffed = try gz.addUnNode(.load, result, node);
+ return rvalue(gz, rl, dereffed, node);
+ },
+ }
},
.slice => {
const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
@@ -710,7 +712,13 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
.start = start,
.end = end,
});
- return rvalue(gz, rl, result, node);
+ switch (rl) {
+ .ref, .none_or_ref => return result,
+ else => {
+ const dereffed = try gz.addUnNode(.load, result, node);
+ return rvalue(gz, rl, dereffed, node);
+ },
+ }
},
.slice_sentinel => {
const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
@@ -724,7 +732,13 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
.end = end,
.sentinel = sentinel,
});
- return rvalue(gz, rl, result, node);
+ switch (rl) {
+ .ref, .none_or_ref => return result,
+ else => {
+ const dereffed = try gz.addUnNode(.load, result, node);
+ return rvalue(gz, rl, dereffed, node);
+ },
+ }
},
.deref => {
@@ -741,10 +755,6 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
const result = try expr(gz, scope, .ref, node_datas[node].lhs);
return rvalue(gz, rl, result, node);
},
- .undefined_literal => return rvalue(gz, rl, .undef, node),
- .true_literal => return rvalue(gz, rl, .bool_true, node),
- .false_literal => return rvalue(gz, rl, .bool_false, node),
- .null_literal => return rvalue(gz, rl, .null_value, node),
.optional_type => {
const operand = try typeExpr(gz, scope, node_datas[node].lhs);
const result = try gz.addUnNode(.optional_type, operand, node);
@@ -2367,7 +2377,7 @@ fn varDecl(
}
const ident_name = try astgen.identAsString(name_token);
- try astgen.detectLocalShadowing(scope, ident_name, name_token);
+ try astgen.detectLocalShadowing(scope, ident_name, name_token, ident_name_raw);
if (var_decl.ast.init_node == 0) {
return astgen.failNode(node, "variables must be initialized", .{});
@@ -2873,7 +2883,7 @@ fn fnDecl(
};
const fn_name_str_index = try astgen.identAsString(fn_name_token);
- try astgen.declareNewName(scope, fn_name_str_index, decl_node);
+ try astgen.declareNewName(scope, fn_name_str_index, decl_node, fn_name_token);
// We insert this at the beginning so that its instruction index marks the
// start of the top level declaration.
@@ -2934,12 +2944,13 @@ fn fnDecl(
} else false;
const param_name: u32 = if (param.name_token) |name_token| blk: {
- if (mem.eql(u8, "_", tree.tokenSlice(name_token)))
+ const name_bytes = tree.tokenSlice(name_token);
+ if (mem.eql(u8, "_", name_bytes))
break :blk 0;
const param_name = try astgen.identAsString(name_token);
if (!is_extern) {
- try astgen.detectLocalShadowing(params_scope, param_name, name_token);
+ try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes);
}
break :blk param_name;
} else if (!is_extern) {
@@ -3142,7 +3153,7 @@ fn globalVarDecl(
const name_token = var_decl.ast.mut_token + 1;
const name_str_index = try astgen.identAsString(name_token);
- try astgen.declareNewName(scope, name_str_index, node);
+ try astgen.declareNewName(scope, name_str_index, node, name_token);
var block_scope: GenZir = .{
.parent = scope,
@@ -5017,7 +5028,7 @@ fn ifExpr(
const token_name_str = tree.tokenSlice(token_name_index);
if (mem.eql(u8, "_", token_name_str))
break :s &then_scope.base;
- try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index);
+ try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str);
payload_val_scope = .{
.parent = &then_scope.base,
.gen_zir = &then_scope,
@@ -5036,11 +5047,12 @@ fn ifExpr(
.optional_payload_unsafe_ptr
else
.optional_payload_unsafe;
- if (mem.eql(u8, "_", tree.tokenSlice(ident_token)))
+ const ident_bytes = tree.tokenSlice(ident_token);
+ if (mem.eql(u8, "_", ident_bytes))
break :s &then_scope.base;
const payload_inst = try then_scope.addUnNode(tag, cond.inst, node);
const ident_name = try astgen.identAsString(ident_token);
- try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token);
+ try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes);
payload_val_scope = .{
.parent = &then_scope.base,
.gen_zir = &then_scope,
@@ -5082,7 +5094,7 @@ fn ifExpr(
const error_token_str = tree.tokenSlice(error_token);
if (mem.eql(u8, "_", error_token_str))
break :s &else_scope.base;
- try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token);
+ try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str);
payload_val_scope = .{
.parent = &else_scope.base,
.gen_zir = &else_scope,
@@ -5273,11 +5285,12 @@ fn whileExpr(
.err_union_payload_unsafe;
const payload_inst = try then_scope.addUnNode(tag, cond.inst, node);
const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
- if (mem.eql(u8, "_", tree.tokenSlice(ident_token)))
+ const ident_bytes = tree.tokenSlice(ident_token);
+ if (mem.eql(u8, "_", ident_bytes))
break :s &then_scope.base;
const payload_name_loc = payload_token + @boolToInt(payload_is_ref);
const ident_name = try astgen.identAsString(payload_name_loc);
- try astgen.detectLocalShadowing(&then_scope.base, ident_name, payload_name_loc);
+ try astgen.detectLocalShadowing(&then_scope.base, ident_name, payload_name_loc, ident_bytes);
payload_val_scope = .{
.parent = &then_scope.base,
.gen_zir = &then_scope,
@@ -5298,9 +5311,10 @@ fn whileExpr(
.optional_payload_unsafe;
const payload_inst = try then_scope.addUnNode(tag, cond.inst, node);
const ident_name = try astgen.identAsString(ident_token);
- if (mem.eql(u8, "_", tree.tokenSlice(ident_token)))
+ const ident_bytes = tree.tokenSlice(ident_token);
+ if (mem.eql(u8, "_", ident_bytes))
break :s &then_scope.base;
- try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token);
+ try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes);
payload_val_scope = .{
.parent = &then_scope.base,
.gen_zir = &then_scope,
@@ -5356,9 +5370,10 @@ fn whileExpr(
.err_union_code;
const payload_inst = try else_scope.addUnNode(tag, cond.inst, node);
const ident_name = try astgen.identAsString(error_token);
- if (mem.eql(u8, tree.tokenSlice(error_token), "_"))
+ const ident_bytes = tree.tokenSlice(error_token);
+ if (mem.eql(u8, ident_bytes, "_"))
break :s &else_scope.base;
- try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token);
+ try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes);
payload_val_scope = .{
.parent = &else_scope.base,
.gen_zir = &else_scope,
@@ -5418,12 +5433,19 @@ fn forExpr(
if (for_full.label_token) |label_token| {
try astgen.checkLabelRedefinition(scope, label_token);
}
+
// Set up variables and constants.
const is_inline = parent_gz.force_comptime or for_full.inline_token != null;
const tree = astgen.tree;
const token_tags = tree.tokens.items(.tag);
- const array_ptr = try expr(parent_gz, scope, .none_or_ref, for_full.ast.cond_expr);
+ const payload_is_ref = if (for_full.payload_token) |payload_token|
+ token_tags[payload_token] == .asterisk
+ else
+ false;
+
+ const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
+ const array_ptr = try expr(parent_gz, scope, cond_rl, for_full.ast.cond_expr);
const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);
const index_ptr = blk: {
@@ -5498,7 +5520,7 @@ fn forExpr(
const name_str_index = try astgen.identAsString(ident);
const tag: Zir.Inst.Tag = if (is_ptr) .elem_ptr else .elem_val;
const payload_inst = try then_scope.addBin(tag, array_ptr, index);
- try astgen.detectLocalShadowing(&then_scope.base, name_str_index, ident);
+ try astgen.detectLocalShadowing(&then_scope.base, name_str_index, ident, value_name);
payload_val_scope = .{
.parent = &then_scope.base,
.gen_zir = &then_scope,
@@ -5518,11 +5540,12 @@ fn forExpr(
ident + 2
else
break :blk payload_sub_scope;
- if (mem.eql(u8, tree.tokenSlice(index_token), "_")) {
+ const token_bytes = tree.tokenSlice(index_token);
+ if (mem.eql(u8, token_bytes, "_")) {
return astgen.failTok(index_token, "discard of index capture; omit it instead", .{});
}
const index_name = try astgen.identAsString(index_token);
- try astgen.detectLocalShadowing(payload_sub_scope, index_name, index_token);
+ try astgen.detectLocalShadowing(payload_sub_scope, index_name, index_token, token_bytes);
index_scope = .{
.parent = payload_sub_scope,
.gen_zir = &then_scope,
@@ -6294,34 +6317,36 @@ fn identifier(
}
const ident_name = try astgen.identifierTokenString(ident_token);
- if (simple_types.get(ident_name)) |zir_const_ref| {
- return rvalue(gz, rl, zir_const_ref, ident);
- }
+ if (ident_name_raw[0] != '@') {
+ if (simple_types.get(ident_name)) |zir_const_ref| {
+ return rvalue(gz, rl, zir_const_ref, ident);
+ }
- if (ident_name.len >= 2) integer: {
- const first_c = ident_name[0];
- if (first_c == 'i' or first_c == 'u') {
- const signedness: std.builtin.Signedness = switch (first_c == 'i') {
- true => .signed,
- false => .unsigned,
- };
- const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
- error.Overflow => return astgen.failNode(
- ident,
- "primitive integer type '{s}' exceeds maximum bit width of 65535",
- .{ident_name},
- ),
- error.InvalidCharacter => break :integer,
- };
- const result = try gz.add(.{
- .tag = .int_type,
- .data = .{ .int_type = .{
- .src_node = gz.nodeIndexToRelative(ident),
- .signedness = signedness,
- .bit_count = bit_count,
- } },
- });
- return rvalue(gz, rl, result, ident);
+ if (ident_name.len >= 2) integer: {
+ const first_c = ident_name[0];
+ if (first_c == 'i' or first_c == 'u') {
+ const signedness: std.builtin.Signedness = switch (first_c == 'i') {
+ true => .signed,
+ false => .unsigned,
+ };
+ const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
+ error.Overflow => return astgen.failNode(
+ ident,
+ "primitive integer type '{s}' exceeds maximum bit width of 65535",
+ .{ident_name},
+ ),
+ error.InvalidCharacter => break :integer,
+ };
+ const result = try gz.add(.{
+ .tag = .int_type,
+ .data = .{ .int_type = .{
+ .src_node = gz.nodeIndexToRelative(ident),
+ .signedness = signedness,
+ .bit_count = bit_count,
+ } },
+ });
+ return rvalue(gz, rl, result, ident);
+ }
}
}
@@ -7102,38 +7127,38 @@ fn builtinCall(
.bit_size_of => return simpleUnOpType(gz, scope, rl, node, params[0], .bit_size_of),
.align_of => return simpleUnOpType(gz, scope, rl, node, params[0], .align_of),
- .ptr_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ptr_to_int),
- .error_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .error_to_int),
- .int_to_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u16_type }, params[0], .int_to_error),
- .compile_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .compile_error),
- .set_eval_branch_quota => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u32_type }, params[0], .set_eval_branch_quota),
- .enum_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .enum_to_int),
- .bool_to_int => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .bool_to_int),
- .embed_file => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .embed_file),
- .error_name => return simpleUnOp(gz, scope, rl, node, .{ .ty = .anyerror_type }, params[0], .error_name),
- .panic => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .panic),
- .set_align_stack => return simpleUnOp(gz, scope, rl, node, align_rl, params[0], .set_align_stack),
- .set_cold => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_cold),
- .set_float_mode => return simpleUnOp(gz, scope, rl, node, .{ .ty = .float_mode_type }, params[0], .set_float_mode),
- .set_runtime_safety => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_runtime_safety),
- .sqrt => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sqrt),
- .sin => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sin),
- .cos => return simpleUnOp(gz, scope, rl, node, .none, params[0], .cos),
- .exp => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp),
- .exp2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp2),
- .log => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log),
- .log2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log2),
- .log10 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log10),
- .fabs => return simpleUnOp(gz, scope, rl, node, .none, params[0], .fabs),
- .floor => return simpleUnOp(gz, scope, rl, node, .none, params[0], .floor),
- .ceil => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ceil),
- .trunc => return simpleUnOp(gz, scope, rl, node, .none, params[0], .trunc),
- .round => return simpleUnOp(gz, scope, rl, node, .none, params[0], .round),
- .tag_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .tag_name),
- .Type => return simpleUnOp(gz, scope, rl, node, .none, params[0], .reify),
- .type_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .type_name),
- .Frame => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_type),
- .frame_size => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_size),
+ .ptr_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ptr_to_int),
+ .error_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .error_to_int),
+ .int_to_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u16_type }, params[0], .int_to_error),
+ .compile_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .compile_error),
+ .set_eval_branch_quota => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u32_type }, params[0], .set_eval_branch_quota),
+ .enum_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .enum_to_int),
+ .bool_to_int => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .bool_to_int),
+ .embed_file => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .embed_file),
+ .error_name => return simpleUnOp(gz, scope, rl, node, .{ .ty = .anyerror_type }, params[0], .error_name),
+ .panic => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .panic),
+ .set_align_stack => return simpleUnOp(gz, scope, rl, node, align_rl, params[0], .set_align_stack),
+ .set_cold => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_cold),
+ .set_float_mode => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .float_mode_type }, params[0], .set_float_mode),
+ .set_runtime_safety => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_runtime_safety),
+ .sqrt => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sqrt),
+ .sin => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sin),
+ .cos => return simpleUnOp(gz, scope, rl, node, .none, params[0], .cos),
+ .exp => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp),
+ .exp2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp2),
+ .log => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log),
+ .log2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log2),
+ .log10 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log10),
+ .fabs => return simpleUnOp(gz, scope, rl, node, .none, params[0], .fabs),
+ .floor => return simpleUnOp(gz, scope, rl, node, .none, params[0], .floor),
+ .ceil => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ceil),
+ .trunc => return simpleUnOp(gz, scope, rl, node, .none, params[0], .trunc),
+ .round => return simpleUnOp(gz, scope, rl, node, .none, params[0], .round),
+ .tag_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .tag_name),
+ .Type => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .type_info_type }, params[0], .reify),
+ .type_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .type_name),
+ .Frame => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_type),
+ .frame_size => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_size),
.float_to_int => return typeCast(gz, scope, rl, node, params[0], params[1], .float_to_int),
.int_to_float => return typeCast(gz, scope, rl, node, params[0], params[1], .int_to_float),
@@ -7819,10 +7844,6 @@ fn nodeMayNeedMemoryLocation(tree: *const ast.Tree, start_node: ast.Node.Index)
.string_literal,
.multiline_string_literal,
.char_literal,
- .true_literal,
- .false_literal,
- .null_literal,
- .undefined_literal,
.unreachable_literal,
.identifier,
.error_set_decl,
@@ -8059,10 +8080,6 @@ fn nodeMayEvalToError(tree: *const ast.Tree, start_node: ast.Node.Index) enum {
.string_literal,
.multiline_string_literal,
.char_literal,
- .true_literal,
- .false_literal,
- .null_literal,
- .undefined_literal,
.unreachable_literal,
.error_set_decl,
.container_decl,
@@ -8232,10 +8249,6 @@ fn nodeImpliesRuntimeBits(tree: *const ast.Tree, start_node: ast.Node.Index) boo
.string_literal,
.multiline_string_literal,
.char_literal,
- .true_literal,
- .false_literal,
- .null_literal,
- .undefined_literal,
.unreachable_literal,
.identifier,
.error_set_decl,
@@ -10006,8 +10019,21 @@ fn declareNewName(
start_scope: *Scope,
name_index: u32,
node: ast.Node.Index,
+ name_token: ast.TokenIndex,
) !void {
const gpa = astgen.gpa;
+
+ const token_bytes = astgen.tree.tokenSlice(name_token);
+ if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
+ return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
+ token_bytes,
+ }, &[_]u32{
+ try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
+ token_bytes,
+ }),
+ });
+ }
+
var scope = start_scope;
while (true) {
switch (scope.tag) {
@@ -10019,7 +10045,7 @@ fn declareNewName(
const ns = scope.cast(Scope.Namespace).?;
const gop = try ns.decls.getOrPut(gpa, name_index);
if (gop.found_existing) {
- const name = try gpa.dupe(u8, mem.spanZ(astgen.nullTerminatedString(name_index)));
+ const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(name_index)));
defer gpa.free(name);
return astgen.failNodeNotes(node, "redeclaration of '{s}'", .{
name,
@@ -10035,21 +10061,45 @@ fn declareNewName(
}
}
-/// Local variables shadowing detection, including function parameters.
+fn isPrimitive(name: []const u8) bool {
+ if (simple_types.get(name) != null) return true;
+ if (name.len < 2) return false;
+ const first_c = name[0];
+ if (first_c != 'i' and first_c != 'u') return false;
+ if (std.fmt.parseInt(u16, name[1..], 10)) |_| {
+ return true;
+ } else |err| switch (err) {
+ error.Overflow => return true,
+ error.InvalidCharacter => return false,
+ }
+}
+
+/// Local variables shadowing detection, including function parameters and primitives.
fn detectLocalShadowing(
astgen: *AstGen,
scope: *Scope,
ident_name: u32,
name_token: ast.TokenIndex,
+ token_bytes: []const u8,
) !void {
const gpa = astgen.gpa;
+ if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
+ return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
+ token_bytes,
+ }, &[_]u32{
+ try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
+ token_bytes,
+ }),
+ });
+ }
var s = scope;
while (true) switch (s.tag) {
.local_val => {
const local_val = s.cast(Scope.LocalVal).?;
if (local_val.name == ident_name) {
- const name = try gpa.dupe(u8, mem.spanZ(astgen.nullTerminatedString(ident_name)));
+ const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
+ const name = try gpa.dupe(u8, name_slice);
defer gpa.free(name);
return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
@tagName(local_val.id_cat), name,
@@ -10066,7 +10116,8 @@ fn detectLocalShadowing(
.local_ptr => {
const local_ptr = s.cast(Scope.LocalPtr).?;
if (local_ptr.name == ident_name) {
- const name = try gpa.dupe(u8, mem.spanZ(astgen.nullTerminatedString(ident_name)));
+ const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
+ const name = try gpa.dupe(u8, name_slice);
defer gpa.free(name);
return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
@tagName(local_ptr.id_cat), name,
@@ -10086,7 +10137,8 @@ fn detectLocalShadowing(
s = ns.parent;
continue;
};
- const name = try gpa.dupe(u8, mem.spanZ(astgen.nullTerminatedString(ident_name)));
+ const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
+ const name = try gpa.dupe(u8, name_slice);
defer gpa.free(name);
return astgen.failTokNotes(name_token, "local shadows declaration of '{s}'", .{
name,
diff --git a/src/Compilation.zig b/src/Compilation.zig
index 63475821ab..b830fdc652 100644
--- a/src/Compilation.zig
+++ b/src/Compilation.zig
@@ -2557,6 +2557,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult {
var argv = std.ArrayList([]const u8).init(comp.gpa);
defer argv.deinit();
+ try argv.append(""); // argv[0] is program name, actual args start at [1]
try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path);
try argv.append(out_h_path);
diff --git a/src/Liveness.zig b/src/Liveness.zig
index 48603fc7c9..6a47bfe597 100644
--- a/src/Liveness.zig
+++ b/src/Liveness.zig
@@ -249,6 +249,8 @@ fn analyzeInst(
.ptr_slice_elem_val,
.ptr_elem_val,
.ptr_ptr_elem_val,
+ .shl,
+ .shr,
=> {
const o = inst_datas[inst].bin_op;
return trackOperands(a, new_set, inst, main_tomb, .{ o.lhs, o.rhs, .none });
@@ -280,6 +282,10 @@ fn analyzeInst(
.wrap_errunion_err,
.slice_ptr,
.slice_len,
+ .struct_field_ptr_index_0,
+ .struct_field_ptr_index_1,
+ .struct_field_ptr_index_2,
+ .struct_field_ptr_index_3,
=> {
const o = inst_datas[inst].ty_op;
return trackOperands(a, new_set, inst, main_tomb, .{ o.operand, .none, .none });
@@ -328,6 +334,10 @@ fn analyzeInst(
const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;
return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_operand, .none, .none });
},
+ .ptr_elem_ptr => {
+ const extra = a.air.extraData(Air.Bin, inst_datas[inst].ty_pl.payload).data;
+ return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none });
+ },
.br => {
const br = inst_datas[inst].br;
return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none });
diff --git a/src/Module.zig b/src/Module.zig
index 319363e9b8..4ed39c9954 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -66,6 +66,10 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},
/// to the same function.
monomorphed_funcs: MonomorphedFuncsSet = .{},
+/// The set of all comptime function calls that have been cached so that future calls
+/// with the same parameters will get the same return value.
+memoized_calls: MemoizedCallSet = .{},
+
/// We optimize memory usage for a compilation with no compile errors by storing the
/// error messages and mapping outside of `Decl`.
/// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator.
@@ -157,6 +161,60 @@ const MonomorphedFuncsContext = struct {
}
};
+pub const MemoizedCallSet = std.HashMapUnmanaged(
+ MemoizedCall.Key,
+ MemoizedCall.Result,
+ MemoizedCall,
+ std.hash_map.default_max_load_percentage,
+);
+
+pub const MemoizedCall = struct {
+ pub const Key = struct {
+ func: *Fn,
+ args: []TypedValue,
+ };
+
+ pub const Result = struct {
+ val: Value,
+ arena: std.heap.ArenaAllocator.State,
+ };
+
+ pub fn eql(ctx: @This(), a: Key, b: Key) bool {
+ _ = ctx;
+
+ if (a.func != b.func) return false;
+
+ assert(a.args.len == b.args.len);
+ for (a.args) |a_arg, arg_i| {
+ const b_arg = b.args[arg_i];
+ if (!a_arg.eql(b_arg)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /// Must match `Sema.GenericCallAdapter.hash`.
+ pub fn hash(ctx: @This(), key: Key) u64 {
+ _ = ctx;
+
+ var hasher = std.hash.Wyhash.init(0);
+
+ // The generic function Decl is guaranteed to be the first dependency
+ // of each of its instantiations.
+ std.hash.autoHash(&hasher, @ptrToInt(key.func));
+
+ // This logic must be kept in sync with the logic in `analyzeCall` that
+ // computes the hash.
+ for (key.args) |arg| {
+ arg.hash(&hasher);
+ }
+
+ return hasher.final();
+ }
+};
+
/// A `Module` has zero or one of these depending on whether `-femit-h` is enabled.
pub const GlobalEmitH = struct {
/// Where to put the output.
@@ -554,8 +612,8 @@ pub const Decl = struct {
assert(struct_obj.owner_decl == decl);
return &struct_obj.namespace;
},
- .enum_full => {
- const enum_obj = ty.castTag(.enum_full).?.data;
+ .enum_full, .enum_nonexhaustive => {
+ const enum_obj = ty.cast(Type.Payload.EnumFull).?.data;
assert(enum_obj.owner_decl == decl);
return &enum_obj.namespace;
},
@@ -660,6 +718,7 @@ pub const Struct = struct {
/// is necessary to determine whether it has bits at runtime.
known_has_bits: bool,
+ /// The `Type` and `Value` memory is owned by the arena of the Struct's owner_decl.
pub const Field = struct {
/// Uses `noreturn` to indicate `anytype`.
/// undefined until `status` is `have_field_types` or `have_layout`.
@@ -2254,15 +2313,26 @@ pub fn deinit(mod: *Module) void {
}
mod.export_owners.deinit(gpa);
- var it = mod.global_error_set.keyIterator();
- while (it.next()) |key| {
- gpa.free(key.*);
+ {
+ var it = mod.global_error_set.keyIterator();
+ while (it.next()) |key| {
+ gpa.free(key.*);
+ }
+ mod.global_error_set.deinit(gpa);
}
- mod.global_error_set.deinit(gpa);
mod.error_name_list.deinit(gpa);
mod.test_functions.deinit(gpa);
mod.monomorphed_funcs.deinit(gpa);
+
+ {
+ var it = mod.memoized_calls.iterator();
+ while (it.next()) |entry| {
+ gpa.free(entry.key_ptr.args);
+ entry.value_ptr.arena.promote(gpa).deinit();
+ }
+ mod.memoized_calls.deinit(gpa);
+ }
}
fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
@@ -3091,6 +3161,9 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
if (linksection_ref == .none) break :blk Value.initTag(.null_value);
break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val;
};
+ // Note this resolves the type of the Decl, not the value; if this Decl
+ // is a struct, for example, this resolves `type` (which needs no resolution),
+ // not the struct itself.
try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty);
// We need the memory for the Type to go into the arena for the Decl
@@ -3193,6 +3266,15 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
if (type_changed and mod.emit_h != null) {
try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl });
}
+ } else if (decl_tv.ty.zigTypeTag() == .Type) {
+ // In case this Decl is a struct or union, we need to resolve the fields
+ // while we still have the `Sema` in scope, so that the field type expressions
+ // can use the resolved AIR instructions that they possibly reference.
+ // We do this after the decl is populated and set to `complete` so that a `Decl`
+ // may reference itself.
+ var buffer: Value.ToTypeBuffer = undefined;
+ const ty = decl.val.toType(&buffer);
+ try sema.resolveDeclFields(&block_scope, src, ty);
}
if (decl.is_exported) {
@@ -4024,7 +4106,6 @@ pub fn createAnonymousDeclFromDeclNamed(
new_decl.ty = typed_value.ty;
new_decl.val = typed_value.val;
new_decl.has_tv = true;
- new_decl.owns_tv = true;
new_decl.analysis = .complete;
new_decl.generation = mod.generation;
@@ -4450,309 +4531,6 @@ pub const PeerTypeCandidateSrc = union(enum) {
}
};
-pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) CompileError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const gpa = mod.gpa;
- const zir = struct_obj.owner_decl.namespace.file_scope.zir;
- const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended;
- assert(extended.opcode == .struct_decl);
- const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
- var extra_index: usize = extended.operand;
-
- const src: LazySrcLoc = .{ .node_offset = struct_obj.node_offset };
- extra_index += @boolToInt(small.has_src_node);
-
- const body_len = if (small.has_body_len) blk: {
- const body_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk body_len;
- } else 0;
-
- const fields_len = if (small.has_fields_len) blk: {
- const fields_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk fields_len;
- } else 0;
-
- const decls_len = if (small.has_decls_len) decls_len: {
- const decls_len = zir.extra[extra_index];
- extra_index += 1;
- break :decls_len decls_len;
- } else 0;
-
- // Skip over decls.
- var decls_it = zir.declIteratorInner(extra_index, decls_len);
- while (decls_it.next()) |_| {}
- extra_index = decls_it.extra_index;
-
- const body = zir.extra[extra_index..][0..body_len];
- if (fields_len == 0) {
- assert(body.len == 0);
- return;
- }
- extra_index += body.len;
-
- var decl_arena = struct_obj.owner_decl.value_arena.?.promote(gpa);
- defer struct_obj.owner_decl.value_arena.?.* = decl_arena.state;
-
- try struct_obj.fields.ensureCapacity(&decl_arena.allocator, fields_len);
-
- // We create a block for the field type instructions because they
- // may need to reference Decls from inside the struct namespace.
- // Within the field type, default value, and alignment expressions, the "owner decl"
- // should be the struct itself. Thus we need a new Sema.
- var sema: Sema = .{
- .mod = mod,
- .gpa = gpa,
- .arena = &decl_arena.allocator,
- .code = zir,
- .owner_decl = struct_obj.owner_decl,
- .namespace = &struct_obj.namespace,
- .owner_func = null,
- .func = null,
- .fn_ret_ty = Type.initTag(.void),
- };
- defer sema.deinit();
-
- var block: Scope.Block = .{
- .parent = null,
- .sema = &sema,
- .src_decl = struct_obj.owner_decl,
- .instructions = .{},
- .inlining = null,
- .is_comptime = true,
- };
- defer assert(block.instructions.items.len == 0); // should all be comptime instructions
-
- if (body.len != 0) {
- _ = try sema.analyzeBody(&block, body);
- }
-
- const bits_per_field = 4;
- const fields_per_u32 = 32 / bits_per_field;
- const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
- var bit_bag_index: usize = extra_index;
- extra_index += bit_bags_count;
- var cur_bit_bag: u32 = undefined;
- var field_i: u32 = 0;
- while (field_i < fields_len) : (field_i += 1) {
- if (field_i % fields_per_u32 == 0) {
- cur_bit_bag = zir.extra[bit_bag_index];
- bit_bag_index += 1;
- }
- const has_align = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const has_default = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const is_comptime = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const unused = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
-
- _ = unused;
-
- const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
- extra_index += 1;
- const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
- extra_index += 1;
-
- // This string needs to outlive the ZIR code.
- const field_name = try decl_arena.allocator.dupe(u8, field_name_zir);
- if (field_type_ref == .none) {
- return mod.fail(&block.base, src, "TODO: implement anytype struct field", .{});
- }
- const field_ty: Type = if (field_type_ref == .none)
- Type.initTag(.noreturn)
- else
- // TODO: if we need to report an error here, use a source location
- // that points to this type expression rather than the struct.
- // But only resolve the source location if we need to emit a compile error.
- try sema.resolveType(&block, src, field_type_ref);
-
- const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
- assert(!gop.found_existing);
- gop.value_ptr.* = .{
- .ty = field_ty,
- .abi_align = Value.initTag(.abi_align_default),
- .default_val = Value.initTag(.unreachable_value),
- .is_comptime = is_comptime,
- .offset = undefined,
- };
-
- if (has_align) {
- const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
- extra_index += 1;
- // TODO: if we need to report an error here, use a source location
- // that points to this alignment expression rather than the struct.
- // But only resolve the source location if we need to emit a compile error.
- gop.value_ptr.abi_align = (try sema.resolveInstConst(&block, src, align_ref)).val;
- }
- if (has_default) {
- const default_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
- extra_index += 1;
- // TODO: if we need to report an error here, use a source location
- // that points to this default value expression rather than the struct.
- // But only resolve the source location if we need to emit a compile error.
- gop.value_ptr.default_val = (try sema.resolveInstConst(&block, src, default_ref)).val;
- }
- }
-}
-
-pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) CompileError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const gpa = mod.gpa;
- const zir = union_obj.owner_decl.namespace.file_scope.zir;
- const extended = zir.instructions.items(.data)[union_obj.zir_index].extended;
- assert(extended.opcode == .union_decl);
- const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small);
- var extra_index: usize = extended.operand;
-
- const src: LazySrcLoc = .{ .node_offset = union_obj.node_offset };
- extra_index += @boolToInt(small.has_src_node);
-
- if (small.has_tag_type) {
- extra_index += 1;
- }
-
- const body_len = if (small.has_body_len) blk: {
- const body_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk body_len;
- } else 0;
-
- const fields_len = if (small.has_fields_len) blk: {
- const fields_len = zir.extra[extra_index];
- extra_index += 1;
- break :blk fields_len;
- } else 0;
-
- const decls_len = if (small.has_decls_len) decls_len: {
- const decls_len = zir.extra[extra_index];
- extra_index += 1;
- break :decls_len decls_len;
- } else 0;
-
- // Skip over decls.
- var decls_it = zir.declIteratorInner(extra_index, decls_len);
- while (decls_it.next()) |_| {}
- extra_index = decls_it.extra_index;
-
- const body = zir.extra[extra_index..][0..body_len];
- if (fields_len == 0) {
- assert(body.len == 0);
- return;
- }
- extra_index += body.len;
-
- var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa);
- defer union_obj.owner_decl.value_arena.?.* = decl_arena.state;
-
- try union_obj.fields.ensureCapacity(&decl_arena.allocator, fields_len);
-
- // We create a block for the field type instructions because they
- // may need to reference Decls from inside the struct namespace.
- // Within the field type, default value, and alignment expressions, the "owner decl"
- // should be the struct itself. Thus we need a new Sema.
- var sema: Sema = .{
- .mod = mod,
- .gpa = gpa,
- .arena = &decl_arena.allocator,
- .code = zir,
- .owner_decl = union_obj.owner_decl,
- .namespace = &union_obj.namespace,
- .owner_func = null,
- .func = null,
- .fn_ret_ty = Type.initTag(.void),
- };
- defer sema.deinit();
-
- var block: Scope.Block = .{
- .parent = null,
- .sema = &sema,
- .src_decl = union_obj.owner_decl,
- .instructions = .{},
- .inlining = null,
- .is_comptime = true,
- };
- defer assert(block.instructions.items.len == 0); // should all be comptime instructions
-
- if (body.len != 0) {
- _ = try sema.analyzeBody(&block, body);
- }
-
- const bits_per_field = 4;
- const fields_per_u32 = 32 / bits_per_field;
- const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
- var bit_bag_index: usize = extra_index;
- extra_index += bit_bags_count;
- var cur_bit_bag: u32 = undefined;
- var field_i: u32 = 0;
- while (field_i < fields_len) : (field_i += 1) {
- if (field_i % fields_per_u32 == 0) {
- cur_bit_bag = zir.extra[bit_bag_index];
- bit_bag_index += 1;
- }
- const has_type = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const has_align = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const has_tag = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- const unused = @truncate(u1, cur_bit_bag) != 0;
- cur_bit_bag >>= 1;
- _ = unused;
-
- const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
- extra_index += 1;
-
- const field_type_ref: Zir.Inst.Ref = if (has_type) blk: {
- const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
- extra_index += 1;
- break :blk field_type_ref;
- } else .none;
-
- const align_ref: Zir.Inst.Ref = if (has_align) blk: {
- const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
- extra_index += 1;
- break :blk align_ref;
- } else .none;
-
- if (has_tag) {
- extra_index += 1;
- }
-
- // This string needs to outlive the ZIR code.
- const field_name = try decl_arena.allocator.dupe(u8, field_name_zir);
- const field_ty: Type = if (field_type_ref == .none)
- Type.initTag(.void)
- else
- // TODO: if we need to report an error here, use a source location
- // that points to this type expression rather than the union.
- // But only resolve the source location if we need to emit a compile error.
- try sema.resolveType(&block, src, field_type_ref);
-
- const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
- assert(!gop.found_existing);
- gop.value_ptr.* = .{
- .ty = field_ty,
- .abi_align = Value.initTag(.abi_align_default),
- };
-
- if (align_ref != .none) {
- // TODO: if we need to report an error here, use a source location
- // that points to this alignment expression rather than the struct.
- // But only resolve the source location if we need to emit a compile error.
- gop.value_ptr.abi_align = (try sema.resolveInstConst(&block, src, align_ref)).val;
- }
- }
-
- // TODO resolve the union tag_type_ref
-}
-
/// Called from `performAllTheWork`, after all AstGen workers have finished,
/// and before the main semantic analysis loop begins.
pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
diff --git a/src/Sema.zig b/src/Sema.zig
index 8b6f6d4a9f..90505c6806 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -649,6 +649,24 @@ fn resolveValue(
return sema.failWithNeededComptime(block, src);
}
+/// Value Tag `variable` will cause a compile error.
+/// Value Tag `undef` may be returned.
+fn resolveConstMaybeUndefVal(
+ sema: *Sema,
+ block: *Scope.Block,
+ src: LazySrcLoc,
+ inst: Air.Inst.Ref,
+) CompileError!Value {
+ if (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) |val| {
+ switch (val.tag()) {
+ .variable => return sema.failWithNeededComptime(block, src),
+ .generic_poison => return error.GenericPoison,
+ else => return val,
+ }
+ }
+ return sema.failWithNeededComptime(block, src);
+}
+
/// Will not return Value Tags: `variable`, `undef`. Instead they will emit compile errors.
/// See `resolveValue` for an alternative.
fn resolveConstValue(
@@ -866,6 +884,7 @@ fn zirStructDecl(
.ty = Type.initTag(.type),
.val = struct_val,
}, type_name);
+ new_decl.owns_tv = true;
errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
struct_obj.* = .{
.owner_decl = new_decl,
@@ -986,6 +1005,7 @@ fn zirEnumDecl(
.ty = Type.initTag(.type),
.val = enum_val,
}, type_name);
+ new_decl.owns_tv = true;
errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
enum_obj.* = .{
@@ -1032,25 +1052,27 @@ fn zirEnumDecl(
// We create a block for the field type instructions because they
// may need to reference Decls from inside the enum namespace.
// Within the field type, default value, and alignment expressions, the "owner decl"
- // should be the enum itself. Thus we need a new Sema.
- var enum_sema: Sema = .{
- .mod = mod,
- .gpa = gpa,
- .arena = &new_decl_arena.allocator,
- .code = sema.code,
- .inst_map = sema.inst_map,
- .owner_decl = new_decl,
- .namespace = &enum_obj.namespace,
- .owner_func = null,
- .func = null,
- .fn_ret_ty = Type.initTag(.void),
- .branch_quota = sema.branch_quota,
- .branch_count = sema.branch_count,
- };
+ // should be the enum itself.
+
+ const prev_owner_decl = sema.owner_decl;
+ sema.owner_decl = new_decl;
+ defer sema.owner_decl = prev_owner_decl;
+
+ const prev_namespace = sema.namespace;
+ sema.namespace = &enum_obj.namespace;
+ defer sema.namespace = prev_namespace;
+
+ const prev_owner_func = sema.owner_func;
+ sema.owner_func = null;
+ defer sema.owner_func = prev_owner_func;
+
+ const prev_func = sema.func;
+ sema.func = null;
+ defer sema.func = prev_func;
var enum_block: Scope.Block = .{
.parent = null,
- .sema = &enum_sema,
+ .sema = sema,
.src_decl = new_decl,
.instructions = .{},
.inlining = null,
@@ -1059,11 +1081,8 @@ fn zirEnumDecl(
defer assert(enum_block.instructions.items.len == 0); // should all be comptime instructions
if (body.len != 0) {
- _ = try enum_sema.analyzeBody(&enum_block, body);
+ _ = try sema.analyzeBody(&enum_block, body);
}
-
- sema.branch_count = enum_sema.branch_count;
- sema.branch_quota = enum_sema.branch_quota;
}
var bit_bag_index: usize = body_end;
var cur_bit_bag: u32 = undefined;
@@ -1153,6 +1172,7 @@ fn zirUnionDecl(
.ty = Type.initTag(.type),
.val = union_val,
}, type_name);
+ new_decl.owns_tv = true;
errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
union_obj.* = .{
.owner_decl = new_decl,
@@ -1224,6 +1244,7 @@ fn zirErrorSetDecl(
.ty = Type.initTag(.type),
.val = error_set_val,
}, type_name);
+ new_decl.owns_tv = true;
errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
const names = try new_decl_arena.allocator.alloc([]const u8, fields.len);
for (fields) |str_index, i| {
@@ -1466,8 +1487,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
const ptr = sema.resolveInst(inst_data.operand);
const ptr_inst = Air.refToIndex(ptr).?;
assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant);
- const air_datas = sema.air_instructions.items(.data);
- const value_index = air_datas[ptr_inst].ty_pl.payload;
+ const value_index = sema.air_instructions.items(.data)[ptr_inst].ty_pl.payload;
const ptr_val = sema.air_values.items[value_index];
const var_is_mut = switch (sema.typeOf(ptr).tag()) {
.inferred_alloc_const => false,
@@ -1481,7 +1501,8 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde
const final_elem_ty = try decl.ty.copy(sema.arena);
const final_ptr_ty = try Module.simplePtrType(sema.arena, final_elem_ty, true, .One);
- air_datas[ptr_inst].ty_pl.ty = try sema.addType(final_ptr_ty);
+ const final_ptr_ty_inst = try sema.addType(final_ptr_ty);
+ sema.air_instructions.items(.data)[ptr_inst].ty_pl.ty = final_ptr_ty_inst;
if (var_is_mut) {
sema.air_values.items[value_index] = try Value.Tag.decl_ref_mut.create(sema.arena, .{
@@ -2562,6 +2583,19 @@ fn analyzeCall(
defer merges.results.deinit(gpa);
defer merges.br_list.deinit(gpa);
+ // If it's a comptime function call, we need to memoize it as long as no external
+ // comptime memory is mutated.
+ var memoized_call_key: Module.MemoizedCall.Key = undefined;
+ var delete_memoized_call_key = false;
+ defer if (delete_memoized_call_key) gpa.free(memoized_call_key.args);
+ if (is_comptime_call) {
+ memoized_call_key = .{
+ .func = module_fn,
+ .args = try gpa.alloc(TypedValue, func_ty_info.param_types.len),
+ };
+ delete_memoized_call_key = true;
+ }
+
try sema.emitBackwardBranch(&child_block, call_src);
// This will have return instructions analyzed as break instructions to
@@ -2586,12 +2620,32 @@ fn analyzeCall(
const arg_src = call_src; // TODO: better source location
const casted_arg = try sema.coerce(&child_block, param_ty, uncasted_args[arg_i], arg_src);
try sema.inst_map.putNoClobber(gpa, inst, casted_arg);
+
+ if (is_comptime_call) {
+ const arg_val = try sema.resolveConstMaybeUndefVal(&child_block, arg_src, casted_arg);
+ memoized_call_key.args[arg_i] = .{
+ .ty = param_ty,
+ .val = arg_val,
+ };
+ }
+
arg_i += 1;
continue;
},
.param_anytype, .param_anytype_comptime => {
// No coercion needed.
- try sema.inst_map.putNoClobber(gpa, inst, uncasted_args[arg_i]);
+ const uncasted_arg = uncasted_args[arg_i];
+ try sema.inst_map.putNoClobber(gpa, inst, uncasted_arg);
+
+ if (is_comptime_call) {
+ const arg_src = call_src; // TODO: better source location
+ const arg_val = try sema.resolveConstMaybeUndefVal(&child_block, arg_src, uncasted_arg);
+ memoized_call_key.args[arg_i] = .{
+ .ty = sema.typeOf(uncasted_arg),
+ .val = arg_val,
+ };
+ }
+
arg_i += 1;
continue;
},
@@ -2623,8 +2677,61 @@ fn analyzeCall(
sema.fn_ret_ty = fn_ret_ty;
defer sema.fn_ret_ty = parent_fn_ret_ty;
- _ = try sema.analyzeBody(&child_block, fn_info.body);
- break :res try sema.analyzeBlockBody(block, call_src, &child_block, merges);
+ // This `res2` is here instead of directly breaking from `res` due to a stage1
+ // bug generating invalid LLVM IR.
+ const res2: Air.Inst.Ref = res2: {
+ if (is_comptime_call) {
+ if (mod.memoized_calls.get(memoized_call_key)) |result| {
+ const ty_inst = try sema.addType(fn_ret_ty);
+ try sema.air_values.append(gpa, result.val);
+ sema.air_instructions.set(block_inst, .{
+ .tag = .constant,
+ .data = .{ .ty_pl = .{
+ .ty = ty_inst,
+ .payload = @intCast(u32, sema.air_values.items.len - 1),
+ } },
+ });
+ break :res2 Air.indexToRef(block_inst);
+ }
+ }
+
+ _ = try sema.analyzeBody(&child_block, fn_info.body);
+ const result = try sema.analyzeBlockBody(block, call_src, &child_block, merges);
+
+ if (is_comptime_call) {
+ const result_val = try sema.resolveConstMaybeUndefVal(block, call_src, result);
+
+ // TODO: check whether any external comptime memory was mutated by the
+ // comptime function call. If so, then do not memoize the call here.
+ {
+ var arena_allocator = std.heap.ArenaAllocator.init(gpa);
+ errdefer arena_allocator.deinit();
+ const arena = &arena_allocator.allocator;
+
+ for (memoized_call_key.args) |*arg| {
+ arg.* = try arg.*.copy(arena);
+ }
+
+ try mod.memoized_calls.put(gpa, memoized_call_key, .{
+ .val = result_val,
+ .arena = arena_allocator.state,
+ });
+ delete_memoized_call_key = false;
+ }
+
+ // Much like in `Module.semaDecl`, if the result is a struct or union type,
+ // we need to resolve the field type expressions right here, right now, while
+ // the child `Sema` is still available, with the AIR instruction map intact,
+ // because the field type expressions may reference into it.
+ if (sema.typeOf(result).zigTypeTag() == .Type) {
+ const ty = try sema.analyzeAsType(&child_block, call_src, result);
+ try sema.resolveDeclFields(&child_block, call_src, ty);
+ }
+ }
+
+ break :res2 result;
+ };
+ break :res res2;
} else if (func_ty_info.is_generic) res: {
const func_val = try sema.resolveConstValue(block, func_src, func);
const module_fn = func_val.castTag(.function).?.data;
@@ -3291,31 +3398,9 @@ fn zirEnumToInt(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
}
if (try sema.resolveMaybeUndefVal(block, operand_src, enum_tag)) |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.keys()[field_index];
- return sema.addConstant(int_tag_ty, val);
- } else {
- // Field index and integer values are the same.
- const val = try Value.Tag.int_u64.create(arena, field_index);
- return sema.addConstant(int_tag_ty, val);
- }
- },
- .enum_simple => {
- // Field index and integer values are the same.
- const val = try Value.Tag.int_u64.create(arena, field_index);
- return sema.addConstant(int_tag_ty, val);
- },
- else => unreachable,
- }
- } else {
- // Assume it is already an integer and return it directly.
- return sema.addConstant(int_tag_ty, enum_tag_val);
- }
+ var buffer: Value.Payload.U64 = undefined;
+ const val = enum_tag_val.enumToInt(enum_tag_ty, &buffer);
+ return sema.addConstant(int_tag_ty, try val.copy(sema.arena));
}
try sema.requireRuntimeBlock(block, src);
@@ -3400,7 +3485,10 @@ fn zirOptionalPayloadPtr(
return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
}
// The same Value represents the pointer to the optional and the payload.
- return sema.addConstant(child_pointer, pointer_val);
+ return sema.addConstant(
+ child_pointer,
+ try Value.Tag.opt_payload_ptr.create(sema.arena, pointer_val),
+ );
}
}
@@ -3437,7 +3525,8 @@ fn zirOptionalPayload(
if (val.isNull()) {
return sema.mod.fail(&block.base, src, "unable to unwrap null", .{});
}
- return sema.addConstant(child_type, val);
+ const sub_val = val.castTag(.opt_payload).?.data;
+ return sema.addConstant(child_type, sub_val);
}
try sema.requireRuntimeBlock(block, src);
@@ -5294,17 +5383,56 @@ fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
const tracy = trace(@src());
defer tracy.end();
- _ = block;
- _ = inst;
- return sema.mod.fail(&block.base, sema.src, "TODO implement zirShl", .{});
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
+ 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 extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const lhs = sema.resolveInst(extra.lhs);
+ const rhs = sema.resolveInst(extra.rhs);
+
+ if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
+ if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| {
+ if (lhs_val.isUndef() or rhs_val.isUndef()) {
+ return sema.addConstUndef(sema.typeOf(lhs));
+ }
+ return sema.mod.fail(&block.base, src, "TODO implement comptime shl", .{});
+ }
+ }
+
+ try sema.requireRuntimeBlock(block, src);
+ return block.addBinOp(.shl, lhs, rhs);
}
fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
- _ = inst;
- return sema.mod.fail(&block.base, sema.src, "TODO implement zirShr", .{});
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
+ 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 extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const lhs = sema.resolveInst(extra.lhs);
+ const rhs = sema.resolveInst(extra.rhs);
+
+ if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| {
+ if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| {
+ const lhs_ty = sema.typeOf(lhs);
+ if (lhs_val.isUndef() or rhs_val.isUndef()) {
+ return sema.addConstUndef(lhs_ty);
+ }
+ // If rhs is 0, return lhs without doing any calculations.
+ if (rhs_val.compareWithZero(.eq)) {
+ return sema.addConstant(lhs_ty, lhs_val);
+ }
+ const val = try lhs_val.shr(rhs_val, sema.arena);
+ return sema.addConstant(lhs_ty, val);
+ }
+ }
+
+ try sema.requireRuntimeBlock(block, src);
+ return block.addBinOp(.shr, lhs, rhs);
}
fn zirBitwise(
@@ -5975,6 +6103,28 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
}),
);
},
+ .Int => {
+ const info = ty.intInfo(target);
+ const field_values = try sema.arena.alloc(Value, 2);
+ // signedness: Signedness,
+ field_values[0] = try Value.Tag.enum_field_index.create(
+ sema.arena,
+ @enumToInt(info.signedness),
+ );
+ // bits: comptime_int,
+ field_values[1] = try Value.Tag.int_u64.create(sema.arena, info.bits);
+
+ return sema.addConstant(
+ type_info_ty,
+ try Value.Tag.@"union".create(sema.arena, .{
+ .tag = try Value.Tag.enum_field_index.create(
+ sema.arena,
+ @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Int),
+ ),
+ .val = try Value.Tag.@"struct".create(sema.arena, field_values.ptr),
+ }),
+ );
+ },
else => |t| return sema.mod.fail(&block.base, src, "TODO: implement zirTypeInfo for {s}", .{
@tagName(t),
}),
@@ -6001,13 +6151,37 @@ fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compile
fn zirTypeofLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirTypeofLog2IntType", .{});
+ const operand = sema.resolveInst(inst_data.operand);
+ const operand_ty = sema.typeOf(operand);
+ return sema.log2IntType(block, operand_ty, src);
}
fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirLog2IntType", .{});
+ const operand = try sema.resolveType(block, src, inst_data.operand);
+ return sema.log2IntType(block, operand, src);
+}
+
+fn log2IntType(sema: *Sema, block: *Scope.Block, operand: Type, src: LazySrcLoc) CompileError!Air.Inst.Ref {
+ switch (operand.zigTypeTag()) {
+ .ComptimeInt => return Air.Inst.Ref.comptime_int_type,
+ .Int => {
+ var count: u16 = 0;
+ var s = operand.bitSize(sema.mod.getTarget()) - 1;
+ while (s != 0) : (s >>= 1) {
+ count += 1;
+ }
+ const res = try Module.makeIntType(sema.arena, .unsigned, count);
+ return sema.addType(res);
+ },
+ else => return sema.mod.fail(
+ &block.base,
+ src,
+ "bit shifting operation expected integer type, found '{}'",
+ .{operand},
+ ),
+ }
}
fn zirTypeofPeer(
@@ -6464,99 +6638,134 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref:
const first_field_type_data = zir_datas[first_item.field_type].pl_node;
const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data;
const unresolved_struct_type = try sema.resolveType(block, src, first_field_type_extra.container_type);
- const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type);
- const struct_obj = struct_ty.castTag(.@"struct").?.data;
-
- // Maps field index to field_type index of where it was already initialized.
- // For making sure all fields are accounted for and no fields are duplicated.
- const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count());
- defer gpa.free(found_fields);
- mem.set(Zir.Inst.Index, found_fields, 0);
+ const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type);
+
+ if (resolved_ty.castTag(.@"struct")) |struct_payload| {
+ const struct_obj = struct_payload.data;
+
+ // Maps field index to field_type index of where it was already initialized.
+ // For making sure all fields are accounted for and no fields are duplicated.
+ const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count());
+ defer gpa.free(found_fields);
+ mem.set(Zir.Inst.Index, found_fields, 0);
+
+ // The init values to use for the struct instance.
+ const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count());
+ defer gpa.free(field_inits);
+
+ var field_i: u32 = 0;
+ var extra_index = extra.end;
+
+ while (field_i < extra.data.fields_len) : (field_i += 1) {
+ const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index);
+ extra_index = item.end;
+
+ const field_type_data = zir_datas[item.data.field_type].pl_node;
+ const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node };
+ const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
+ const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);
+ const field_index = struct_obj.fields.getIndex(field_name) orelse
+ return sema.failWithBadFieldAccess(block, struct_obj, field_src, field_name);
+ if (found_fields[field_index] != 0) {
+ const other_field_type = found_fields[field_index];
+ const other_field_type_data = zir_datas[other_field_type].pl_node;
+ const other_field_src: LazySrcLoc = .{ .node_offset_back2tok = other_field_type_data.src_node };
+ const msg = msg: {
+ const msg = try mod.errMsg(&block.base, field_src, "duplicate field", .{});
+ errdefer msg.destroy(gpa);
+ try mod.errNote(&block.base, other_field_src, msg, "other field here", .{});
+ break :msg msg;
+ };
+ return mod.failWithOwnedErrorMsg(&block.base, msg);
+ }
+ found_fields[field_index] = item.data.field_type;
+ field_inits[field_index] = sema.resolveInst(item.data.init);
+ }
- // The init values to use for the struct instance.
- const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count());
- defer gpa.free(field_inits);
+ var root_msg: ?*Module.ErrorMsg = null;
- var field_i: u32 = 0;
- var extra_index = extra.end;
-
- while (field_i < extra.data.fields_len) : (field_i += 1) {
- const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index);
- extra_index = item.end;
+ for (found_fields) |field_type_inst, i| {
+ if (field_type_inst != 0) continue;
- const field_type_data = zir_datas[item.data.field_type].pl_node;
- const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node };
- const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
- const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);
- const field_index = struct_obj.fields.getIndex(field_name) orelse
- return sema.failWithBadFieldAccess(block, struct_obj, field_src, field_name);
- if (found_fields[field_index] != 0) {
- const other_field_type = found_fields[field_index];
- const other_field_type_data = zir_datas[other_field_type].pl_node;
- const other_field_src: LazySrcLoc = .{ .node_offset_back2tok = other_field_type_data.src_node };
- const msg = msg: {
- const msg = try mod.errMsg(&block.base, field_src, "duplicate field", .{});
- errdefer msg.destroy(gpa);
- try mod.errNote(&block.base, other_field_src, msg, "other field here", .{});
- break :msg msg;
- };
+ // Check if the field has a default init.
+ const field = struct_obj.fields.values()[i];
+ if (field.default_val.tag() == .unreachable_value) {
+ const field_name = struct_obj.fields.keys()[i];
+ const template = "missing struct field: {s}";
+ const args = .{field_name};
+ if (root_msg) |msg| {
+ try mod.errNote(&block.base, src, msg, template, args);
+ } else {
+ root_msg = try mod.errMsg(&block.base, src, template, args);
+ }
+ } else {
+ field_inits[i] = try sema.addConstant(field.ty, field.default_val);
+ }
+ }
+ if (root_msg) |msg| {
+ const fqn = try struct_obj.getFullyQualifiedName(gpa);
+ defer gpa.free(fqn);
+ try mod.errNoteNonLazy(
+ struct_obj.srcLoc(),
+ msg,
+ "struct '{s}' declared here",
+ .{fqn},
+ );
return mod.failWithOwnedErrorMsg(&block.base, msg);
}
- found_fields[field_index] = item.data.field_type;
- field_inits[field_index] = sema.resolveInst(item.data.init);
- }
- var root_msg: ?*Module.ErrorMsg = null;
+ if (is_ref) {
+ return mod.fail(&block.base, src, "TODO: Sema.zirStructInit is_ref=true", .{});
+ }
- for (found_fields) |field_type_inst, i| {
- if (field_type_inst != 0) continue;
-
- // Check if the field has a default init.
- const field = struct_obj.fields.values()[i];
- if (field.default_val.tag() == .unreachable_value) {
- const field_name = struct_obj.fields.keys()[i];
- const template = "missing struct field: {s}";
- const args = .{field_name};
- if (root_msg) |msg| {
- try mod.errNote(&block.base, src, msg, template, args);
- } else {
- root_msg = try mod.errMsg(&block.base, src, template, args);
+ const is_comptime = for (field_inits) |field_init| {
+ if (!(try sema.isComptimeKnown(block, src, field_init))) {
+ break false;
}
- } else {
- field_inits[i] = try sema.addConstant(field.ty, field.default_val);
+ } else true;
+
+ if (is_comptime) {
+ const values = try sema.arena.alloc(Value, field_inits.len);
+ for (field_inits) |field_init, i| {
+ values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?;
+ }
+ return sema.addConstant(resolved_ty, try Value.Tag.@"struct".create(sema.arena, values.ptr));
}
- }
- if (root_msg) |msg| {
- const fqn = try struct_obj.getFullyQualifiedName(gpa);
- defer gpa.free(fqn);
- try mod.errNoteNonLazy(
- struct_obj.srcLoc(),
- msg,
- "struct '{s}' declared here",
- .{fqn},
- );
- return mod.failWithOwnedErrorMsg(&block.base, msg);
- }
- if (is_ref) {
- return mod.fail(&block.base, src, "TODO: Sema.zirStructInit is_ref=true", .{});
- }
+ return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{});
+ } else if (resolved_ty.cast(Type.Payload.Union)) |union_payload| {
+ const union_obj = union_payload.data;
+
+ if (extra.data.fields_len != 1) {
+ return sema.mod.fail(&block.base, src, "union initialization expects exactly one field", .{});
+ }
- const is_comptime = for (field_inits) |field_init| {
- if (!(try sema.isComptimeKnown(block, src, field_init))) {
- break false;
+ const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end);
+
+ const field_type_data = zir_datas[item.data.field_type].pl_node;
+ const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node };
+ const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
+ const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);
+ const field_index = union_obj.fields.getIndex(field_name) orelse
+ return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
+
+ if (is_ref) {
+ return mod.fail(&block.base, src, "TODO: Sema.zirStructInit is_ref=true union", .{});
}
- } else true;
- if (is_comptime) {
- const values = try sema.arena.alloc(Value, field_inits.len);
- for (field_inits) |field_init, i| {
- values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?;
+ const init_inst = sema.resolveInst(item.data.init);
+ if (try sema.resolveMaybeUndefVal(block, field_src, init_inst)) |val| {
+ return sema.addConstant(
+ resolved_ty,
+ try Value.Tag.@"union".create(sema.arena, .{
+ .tag = try Value.Tag.int_u64.create(sema.arena, field_index),
+ .val = val,
+ }),
+ );
}
- return sema.addConstant(struct_ty, try Value.Tag.@"struct".create(sema.arena, values.ptr));
+ return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known union values", .{});
}
-
- return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{});
+ unreachable;
}
fn zirStructInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
@@ -6594,17 +6803,25 @@ fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
const src = inst_data.src();
const field_name = sema.code.nullTerminatedString(extra.name_start);
- const unresolved_struct_type = try sema.resolveType(block, src, extra.container_type);
- if (unresolved_struct_type.zigTypeTag() != .Struct) {
- return sema.mod.fail(&block.base, src, "expected struct; found '{}'", .{
- unresolved_struct_type,
- });
+ const unresolved_ty = try sema.resolveType(block, src, extra.container_type);
+ const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_ty);
+ switch (resolved_ty.zigTypeTag()) {
+ .Struct => {
+ const struct_obj = resolved_ty.castTag(.@"struct").?.data;
+ const field = struct_obj.fields.get(field_name) orelse
+ return sema.failWithBadFieldAccess(block, struct_obj, src, field_name);
+ return sema.addType(field.ty);
+ },
+ .Union => {
+ const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;
+ const field = union_obj.fields.get(field_name) orelse
+ return sema.failWithBadUnionFieldAccess(block, union_obj, src, field_name);
+ return sema.addType(field.ty);
+ },
+ else => return sema.mod.fail(&block.base, src, "expected struct or union; found '{}'", .{
+ resolved_ty,
+ }),
}
- const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type);
- const struct_obj = struct_ty.castTag(.@"struct").?.data;
- const field = struct_obj.fields.get(field_name) orelse
- return sema.failWithBadFieldAccess(block, struct_obj, src, field_name);
- return sema.addType(field.ty);
}
fn zirErrorReturnTrace(
@@ -6679,7 +6896,54 @@ fn zirTagName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
fn zirReify(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify", .{});
+ const type_info_ty = try sema.getBuiltinType(block, src, "TypeInfo");
+ const uncasted_operand = sema.resolveInst(inst_data.operand);
+ const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src);
+ const val = try sema.resolveConstValue(block, operand_src, type_info);
+ const union_val = val.cast(Value.Payload.Union).?.data;
+ const TypeInfoTag = std.meta.Tag(std.builtin.TypeInfo);
+ const tag_index = @intCast(std.meta.Tag(TypeInfoTag), union_val.tag.toUnsignedInt());
+ switch (@intToEnum(std.builtin.TypeId, tag_index)) {
+ .Type => return Air.Inst.Ref.type_type,
+ .Void => return Air.Inst.Ref.void_type,
+ .Bool => return Air.Inst.Ref.bool_type,
+ .NoReturn => return Air.Inst.Ref.noreturn_type,
+ .Int => {
+ const struct_val = union_val.val.castTag(.@"struct").?.data;
+ // TODO use reflection instead of magic numbers here
+ const signedness_val = struct_val[0];
+ const bits_val = struct_val[1];
+
+ const signedness = signedness_val.toEnum(std.builtin.Signedness);
+ const bits = @intCast(u16, bits_val.toUnsignedInt());
+ const ty = switch (signedness) {
+ .signed => try Type.Tag.int_signed.create(sema.arena, bits),
+ .unsigned => try Type.Tag.int_unsigned.create(sema.arena, bits),
+ };
+ return sema.addType(ty);
+ },
+ .Float => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Float", .{}),
+ .Pointer => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Pointer", .{}),
+ .Array => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Array", .{}),
+ .Struct => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Struct", .{}),
+ .ComptimeFloat => return Air.Inst.Ref.comptime_float_type,
+ .ComptimeInt => return Air.Inst.Ref.comptime_int_type,
+ .Undefined => return Air.Inst.Ref.undefined_type,
+ .Null => return Air.Inst.Ref.null_type,
+ .Optional => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Optional", .{}),
+ .ErrorUnion => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for ErrorUnion", .{}),
+ .ErrorSet => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for ErrorSet", .{}),
+ .Enum => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Enum", .{}),
+ .Union => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Union", .{}),
+ .Fn => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Fn", .{}),
+ .BoundFn => @panic("TODO delete BoundFn from the language"),
+ .Opaque => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Opaque", .{}),
+ .Frame => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Frame", .{}),
+ .AnyFrame => return Air.Inst.Ref.anyframe_type,
+ .Vector => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Vector", .{}),
+ .EnumLiteral => return Air.Inst.Ref.enum_literal_type,
+ }
}
fn zirTypeName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -7855,14 +8119,29 @@ fn structFieldPtr(
}
try sema.requireRuntimeBlock(block, src);
+ const tag: Air.Inst.Tag = switch (field_index) {
+ 0 => .struct_field_ptr_index_0,
+ 1 => .struct_field_ptr_index_1,
+ 2 => .struct_field_ptr_index_2,
+ 3 => .struct_field_ptr_index_3,
+ else => {
+ return block.addInst(.{
+ .tag = .struct_field_ptr,
+ .data = .{ .ty_pl = .{
+ .ty = try sema.addType(ptr_field_ty),
+ .payload = try sema.addExtra(Air.StructField{
+ .struct_operand = struct_ptr,
+ .field_index = @intCast(u32, field_index),
+ }),
+ } },
+ });
+ },
+ };
return block.addInst(.{
- .tag = .struct_field_ptr,
- .data = .{ .ty_pl = .{
+ .tag = tag,
+ .data = .{ .ty_op = .{
.ty = try sema.addType(ptr_field_ty),
- .payload = try sema.addExtra(Air.StructField{
- .struct_operand = struct_ptr,
- .field_index = @intCast(u32, field_index),
- }),
+ .operand = struct_ptr,
} },
});
}
@@ -8099,24 +8378,35 @@ fn elemPtrArray(
elem_index: Air.Inst.Ref,
elem_index_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
+ const array_ptr_ty = sema.typeOf(array_ptr);
+ const pointee_type = array_ptr_ty.elemType().elemType();
+ const result_ty = if (array_ptr_ty.ptrIsMutable())
+ try Type.Tag.single_mut_pointer.create(sema.arena, pointee_type)
+ else
+ try Type.Tag.single_const_pointer.create(sema.arena, pointee_type);
+
if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| {
- if (try sema.resolveDefinedValue(block, src, elem_index)) |index_val| {
+ if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| {
// Both array pointer and index are compile-time known.
const index_u64 = index_val.toUnsignedInt();
// @intCast here because it would have been impossible to construct a value that
// required a larger index.
const elem_ptr = try array_ptr_val.elemPtr(sema.arena, @intCast(usize, index_u64));
- const pointee_type = sema.typeOf(array_ptr).elemType().elemType();
-
- return sema.addConstant(
- try Type.Tag.single_const_pointer.create(sema.arena, pointee_type),
- elem_ptr,
- );
+ return sema.addConstant(result_ty, elem_ptr);
}
}
- _ = elem_index;
- _ = elem_index_src;
- return sema.mod.fail(&block.base, src, "TODO implement more analyze elemptr for arrays", .{});
+ // TODO safety check for array bounds
+ try sema.requireRuntimeBlock(block, src);
+ return block.addInst(.{
+ .tag = .ptr_elem_ptr,
+ .data = .{ .ty_pl = .{
+ .ty = try sema.addType(result_ty),
+ .payload = try sema.addExtra(Air.Bin{
+ .lhs = array_ptr,
+ .rhs = elem_index,
+ }),
+ } },
+ });
}
fn coerce(
@@ -8528,9 +8818,12 @@ fn analyzeRef(
try sema.requireRuntimeBlock(block, src);
const ptr_type = try Module.simplePtrType(sema.arena, operand_ty, false, .One);
- const alloc = try block.addTy(.alloc, ptr_type);
+ const mut_ptr_type = try Module.simplePtrType(sema.arena, operand_ty, true, .One);
+ const alloc = try block.addTy(.alloc, mut_ptr_type);
try sema.storePtr(block, src, alloc, operand);
- return alloc;
+
+ // TODO: Replace with sema.coerce when that supports adding pointer constness.
+ return sema.bitcast(block, ptr_type, alloc, src);
}
fn analyzeLoad(
@@ -8895,7 +9188,7 @@ fn wrapOptional(
inst_src: LazySrcLoc,
) !Air.Inst.Ref {
if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
- return sema.addConstant(dest_type, val);
+ return sema.addConstant(dest_type, try Value.Tag.opt_payload.create(sema.arena, val));
}
try sema.requireRuntimeBlock(block, inst_src);
@@ -9124,22 +9417,62 @@ pub fn resolveTypeLayout(
}
}
-fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!Type {
+/// `sema` and `block` are expected to be the same ones used for the `Decl`.
+pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void {
switch (ty.tag()) {
.@"struct" => {
const struct_obj = ty.castTag(.@"struct").?.data;
+ if (struct_obj.owner_decl.namespace != sema.owner_decl.namespace) return;
switch (struct_obj.status) {
.none => {},
.field_types_wip => {
return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{ty});
},
- .have_field_types, .have_layout, .layout_wip => return ty,
+ .have_field_types, .have_layout, .layout_wip => return,
}
+ const prev_namespace = sema.namespace;
+ sema.namespace = &struct_obj.namespace;
+ defer sema.namespace = prev_namespace;
+
struct_obj.status = .field_types_wip;
- try sema.mod.analyzeStructFields(struct_obj);
+ try sema.analyzeStructFields(block, struct_obj);
struct_obj.status = .have_field_types;
- return ty;
},
+ .@"union", .union_tagged => {
+ const union_obj = ty.cast(Type.Payload.Union).?.data;
+ if (union_obj.owner_decl.namespace != sema.owner_decl.namespace) return;
+ switch (union_obj.status) {
+ .none => {},
+ .field_types_wip => {
+ return sema.mod.fail(&block.base, src, "union {} depends on itself", .{ty});
+ },
+ .have_field_types, .have_layout, .layout_wip => return,
+ }
+ const prev_namespace = sema.namespace;
+ sema.namespace = &union_obj.namespace;
+ defer sema.namespace = prev_namespace;
+
+ union_obj.status = .field_types_wip;
+ try sema.analyzeUnionFields(block, union_obj);
+ union_obj.status = .have_field_types;
+ },
+ else => return,
+ }
+}
+
+fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!Type {
+ switch (ty.tag()) {
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ switch (struct_obj.status) {
+ .none => unreachable,
+ .field_types_wip => {
+ return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{ty});
+ },
+ .have_field_types, .have_layout, .layout_wip => return ty,
+ }
+ },
+ .type_info => return sema.resolveBuiltinTypeFields(block, src, "TypeInfo"),
.extern_options => return sema.resolveBuiltinTypeFields(block, src, "ExternOptions"),
.export_options => return sema.resolveBuiltinTypeFields(block, src, "ExportOptions"),
.atomic_ordering => return sema.resolveBuiltinTypeFields(block, src, "AtomicOrdering"),
@@ -9152,18 +9485,12 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type
.@"union", .union_tagged => {
const union_obj = ty.cast(Type.Payload.Union).?.data;
switch (union_obj.status) {
- .none => {},
+ .none => unreachable,
.field_types_wip => {
- return sema.mod.fail(&block.base, src, "union {} depends on itself", .{
- ty,
- });
+ return sema.mod.fail(&block.base, src, "union {} depends on itself", .{ty});
},
.have_field_types, .have_layout, .layout_wip => return ty,
}
- union_obj.status = .field_types_wip;
- try sema.mod.analyzeUnionFields(union_obj);
- union_obj.status = .have_field_types;
- return ty;
},
else => return ty,
}
@@ -9179,6 +9506,265 @@ fn resolveBuiltinTypeFields(
return sema.resolveTypeFields(block, src, resolved_ty);
}
+fn analyzeStructFields(
+ sema: *Sema,
+ block: *Scope.Block,
+ struct_obj: *Module.Struct,
+) CompileError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = sema.gpa;
+ const zir = sema.code;
+ const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended;
+ assert(extended.opcode == .struct_decl);
+ const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
+ var extra_index: usize = extended.operand;
+
+ const src: LazySrcLoc = .{ .node_offset = struct_obj.node_offset };
+ extra_index += @boolToInt(small.has_src_node);
+
+ const body_len = if (small.has_body_len) blk: {
+ const body_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk body_len;
+ } else 0;
+
+ const fields_len = if (small.has_fields_len) blk: {
+ const fields_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk fields_len;
+ } else 0;
+
+ const decls_len = if (small.has_decls_len) decls_len: {
+ const decls_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :decls_len decls_len;
+ } else 0;
+
+ // Skip over decls.
+ var decls_it = zir.declIteratorInner(extra_index, decls_len);
+ while (decls_it.next()) |_| {}
+ extra_index = decls_it.extra_index;
+
+ const body = zir.extra[extra_index..][0..body_len];
+ if (fields_len == 0) {
+ assert(body.len == 0);
+ return;
+ }
+ extra_index += body.len;
+
+ var decl_arena = struct_obj.owner_decl.value_arena.?.promote(gpa);
+ defer struct_obj.owner_decl.value_arena.?.* = decl_arena.state;
+
+ try struct_obj.fields.ensureTotalCapacity(&decl_arena.allocator, fields_len);
+
+ if (body.len != 0) {
+ _ = try sema.analyzeBody(block, body);
+ }
+
+ const bits_per_field = 4;
+ const fields_per_u32 = 32 / bits_per_field;
+ const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
+ var bit_bag_index: usize = extra_index;
+ extra_index += bit_bags_count;
+ var cur_bit_bag: u32 = undefined;
+ var field_i: u32 = 0;
+ while (field_i < fields_len) : (field_i += 1) {
+ if (field_i % fields_per_u32 == 0) {
+ cur_bit_bag = zir.extra[bit_bag_index];
+ bit_bag_index += 1;
+ }
+ const has_align = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const has_default = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const is_comptime = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const unused = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+
+ _ = unused;
+
+ const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
+ extra_index += 1;
+ const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
+ extra_index += 1;
+
+ // This string needs to outlive the ZIR code.
+ const field_name = try decl_arena.allocator.dupe(u8, field_name_zir);
+ const field_ty: Type = if (field_type_ref == .none)
+ Type.initTag(.noreturn)
+ else
+ // TODO: if we need to report an error here, use a source location
+ // that points to this type expression rather than the struct.
+ // But only resolve the source location if we need to emit a compile error.
+ try sema.resolveType(block, src, field_type_ref);
+
+ const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name);
+ assert(!gop.found_existing);
+ gop.value_ptr.* = .{
+ .ty = try field_ty.copy(&decl_arena.allocator),
+ .abi_align = Value.initTag(.abi_align_default),
+ .default_val = Value.initTag(.unreachable_value),
+ .is_comptime = is_comptime,
+ .offset = undefined,
+ };
+
+ if (has_align) {
+ const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
+ extra_index += 1;
+ // TODO: if we need to report an error here, use a source location
+ // that points to this alignment expression rather than the struct.
+ // But only resolve the source location if we need to emit a compile error.
+ const abi_align_val = (try sema.resolveInstConst(block, src, align_ref)).val;
+ gop.value_ptr.abi_align = try abi_align_val.copy(&decl_arena.allocator);
+ }
+ if (has_default) {
+ const default_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
+ extra_index += 1;
+ const default_inst = sema.resolveInst(default_ref);
+ // TODO: if we need to report an error here, use a source location
+ // that points to this default value expression rather than the struct.
+ // But only resolve the source location if we need to emit a compile error.
+ const default_val = (try sema.resolveMaybeUndefVal(block, src, default_inst)) orelse
+ return sema.failWithNeededComptime(block, src);
+ gop.value_ptr.default_val = try default_val.copy(&decl_arena.allocator);
+ }
+ }
+}
+
+fn analyzeUnionFields(
+ sema: *Sema,
+ block: *Scope.Block,
+ union_obj: *Module.Union,
+) CompileError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const gpa = sema.gpa;
+ const zir = sema.code;
+ const extended = zir.instructions.items(.data)[union_obj.zir_index].extended;
+ assert(extended.opcode == .union_decl);
+ const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small);
+ var extra_index: usize = extended.operand;
+
+ const src: LazySrcLoc = .{ .node_offset = union_obj.node_offset };
+ extra_index += @boolToInt(small.has_src_node);
+
+ if (small.has_tag_type) {
+ extra_index += 1;
+ }
+
+ const body_len = if (small.has_body_len) blk: {
+ const body_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk body_len;
+ } else 0;
+
+ const fields_len = if (small.has_fields_len) blk: {
+ const fields_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk fields_len;
+ } else 0;
+
+ const decls_len = if (small.has_decls_len) decls_len: {
+ const decls_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :decls_len decls_len;
+ } else 0;
+
+ // Skip over decls.
+ var decls_it = zir.declIteratorInner(extra_index, decls_len);
+ while (decls_it.next()) |_| {}
+ extra_index = decls_it.extra_index;
+
+ const body = zir.extra[extra_index..][0..body_len];
+ if (fields_len == 0) {
+ assert(body.len == 0);
+ return;
+ }
+ extra_index += body.len;
+
+ var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa);
+ defer union_obj.owner_decl.value_arena.?.* = decl_arena.state;
+
+ try union_obj.fields.ensureCapacity(&decl_arena.allocator, fields_len);
+
+ if (body.len != 0) {
+ _ = try sema.analyzeBody(block, body);
+ }
+
+ const bits_per_field = 4;
+ const fields_per_u32 = 32 / bits_per_field;
+ const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
+ var bit_bag_index: usize = extra_index;
+ extra_index += bit_bags_count;
+ var cur_bit_bag: u32 = undefined;
+ var field_i: u32 = 0;
+ while (field_i < fields_len) : (field_i += 1) {
+ if (field_i % fields_per_u32 == 0) {
+ cur_bit_bag = zir.extra[bit_bag_index];
+ bit_bag_index += 1;
+ }
+ const has_type = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const has_align = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const has_tag = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ const unused = @truncate(u1, cur_bit_bag) != 0;
+ cur_bit_bag >>= 1;
+ _ = unused;
+
+ const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]);
+ extra_index += 1;
+
+ const field_type_ref: Zir.Inst.Ref = if (has_type) blk: {
+ const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
+ extra_index += 1;
+ break :blk field_type_ref;
+ } else .none;
+
+ const align_ref: Zir.Inst.Ref = if (has_align) blk: {
+ const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
+ extra_index += 1;
+ break :blk align_ref;
+ } else .none;
+
+ if (has_tag) {
+ extra_index += 1;
+ }
+
+ // This string needs to outlive the ZIR code.
+ const field_name = try decl_arena.allocator.dupe(u8, field_name_zir);
+ const field_ty: Type = if (field_type_ref == .none)
+ Type.initTag(.void)
+ else
+ // TODO: if we need to report an error here, use a source location
+ // that points to this type expression rather than the union.
+ // But only resolve the source location if we need to emit a compile error.
+ try sema.resolveType(block, src, field_type_ref);
+
+ const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
+ assert(!gop.found_existing);
+ gop.value_ptr.* = .{
+ .ty = try field_ty.copy(&decl_arena.allocator),
+ .abi_align = Value.initTag(.abi_align_default),
+ };
+
+ if (align_ref != .none) {
+ // TODO: if we need to report an error here, use a source location
+ // that points to this alignment expression rather than the struct.
+ // But only resolve the source location if we need to emit a compile error.
+ const abi_align_val = (try sema.resolveInstConst(block, src, align_ref)).val;
+ gop.value_ptr.abi_align = try abi_align_val.copy(&decl_arena.allocator);
+ }
+ }
+
+ // TODO resolve the union tag_type_ref
+}
+
fn getBuiltin(
sema: *Sema,
block: *Scope.Block,
@@ -9291,6 +9877,7 @@ fn typeHasOnePossibleValue(
.call_options,
.export_options,
.extern_options,
+ .type_info,
.@"anyframe",
.anyframe_T,
.many_const_pointer,
@@ -9475,6 +10062,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
.call_options => return .call_options_type,
.export_options => return .export_options_type,
.extern_options => return .extern_options_type,
+ .type_info => return .type_info_type,
.manyptr_u8 => return .manyptr_u8_type,
.manyptr_const_u8 => return .manyptr_const_u8_type,
.fn_noreturn_no_args => return .fn_noreturn_no_args_type,
diff --git a/src/TypedValue.zig b/src/TypedValue.zig
index 48b2c04970..83242b5329 100644
--- a/src/TypedValue.zig
+++ b/src/TypedValue.zig
@@ -23,9 +23,18 @@ pub const Managed = struct {
};
/// Assumes arena allocation. Does a recursive copy.
-pub fn copy(self: TypedValue, allocator: *Allocator) error{OutOfMemory}!TypedValue {
+pub fn copy(self: TypedValue, arena: *Allocator) error{OutOfMemory}!TypedValue {
return TypedValue{
- .ty = try self.ty.copy(allocator),
- .val = try self.val.copy(allocator),
+ .ty = try self.ty.copy(arena),
+ .val = try self.val.copy(arena),
};
}
+
+pub fn eql(a: TypedValue, b: TypedValue) bool {
+ if (!a.ty.eql(b.ty)) return false;
+ return a.val.eql(b.val, a.ty);
+}
+
+pub fn hash(tv: TypedValue, hasher: *std.hash.Wyhash) void {
+ return tv.val.hash(tv.ty, hasher);
+}
diff --git a/src/Zir.zig b/src/Zir.zig
index 1aed609de9..2092a7b5e4 100644
--- a/src/Zir.zig
+++ b/src/Zir.zig
@@ -495,12 +495,15 @@ pub const Inst = struct {
/// Uses the `ptr_type` union field.
ptr_type,
/// Slice operation `lhs[rhs..]`. No sentinel and no end offset.
+ /// Returns a pointer to the subslice.
/// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceStart`.
slice_start,
/// Slice operation `array_ptr[start..end]`. No sentinel.
+ /// Returns a pointer to the subslice.
/// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceEnd`.
slice_end,
/// Slice operation `array_ptr[start..end:sentinel]`.
+ /// Returns a pointer to the subslice.
/// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.
slice_sentinel,
/// Write a value to a pointer. For loading, see `load`.
@@ -687,14 +690,14 @@ pub const Inst = struct {
/// A struct literal with a specified type, with no fields.
/// Uses the `un_node` field.
struct_init_empty,
- /// Given a struct, union, or enum, and a field name as a string index,
+ /// Given a struct or union, and a field name as a string index,
/// returns the field type. Uses the `pl_node` field. Payload is `FieldType`.
field_type,
- /// Given a struct, union, or enum, and a field name as a Ref,
+ /// Given a struct or union, and a field name as a Ref,
/// returns the field type. Uses the `pl_node` field. Payload is `FieldTypeRef`.
field_type_ref,
- /// Finalizes a typed struct initialization, performs validation, and returns the
- /// struct value.
+ /// Finalizes a typed struct or union initialization, performs validation, and returns the
+ /// struct or union value.
/// Uses the `pl_node` field. Payload is `StructInit`.
struct_init,
/// Struct initialization syntax, make the result a pointer.
@@ -1703,6 +1706,7 @@ pub const Inst = struct {
call_options_type,
export_options_type,
extern_options_type,
+ type_info_type,
manyptr_u8_type,
manyptr_const_u8_type,
fn_noreturn_no_args_type,
@@ -1973,6 +1977,10 @@ pub const Inst = struct {
.ty = Type.initTag(.type),
.val = Value.initTag(.extern_options_type),
},
+ .type_info_type = .{
+ .ty = Type.initTag(.type),
+ .val = Value.initTag(.type_info_type),
+ },
.undef = .{
.ty = Type.initTag(.@"undefined"),
diff --git a/src/codegen.zig b/src/codegen.zig
index 11c007dbed..9103c7ad17 100644
--- a/src/codegen.zig
+++ b/src/codegen.zig
@@ -822,6 +822,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.bit_and => try self.airBitAnd(inst),
.bit_or => try self.airBitOr(inst),
.xor => try self.airXor(inst),
+ .shr => try self.airShr(inst),
+ .shl => try self.airShl(inst),
.alloc => try self.airAlloc(inst),
.arg => try self.airArg(inst),
@@ -853,6 +855,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.store => try self.airStore(inst),
.struct_field_ptr=> try self.airStructFieldPtr(inst),
.struct_field_val=> try self.airStructFieldVal(inst),
+
+ .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
+ .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
+ .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
+ .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
+
.switch_br => try self.airSwitch(inst),
.slice_ptr => try self.airSlicePtr(inst),
.slice_len => try self.airSliceLen(inst),
@@ -860,6 +868,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.slice_elem_val => try self.airSliceElemVal(inst),
.ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
.ptr_elem_val => try self.airPtrElemVal(inst),
+ .ptr_elem_ptr => try self.airPtrElemPtr(inst),
.ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst),
.constant => unreachable, // excluded from function bodies
@@ -970,6 +979,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
log.debug("%{d} => {}", .{ inst, result });
const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
branch.inst_table.putAssumeCapacityNoClobber(inst, result);
+
+ switch (result) {
+ .register => |reg| {
+ // In some cases (such as bitcast), an operand
+ // may be the same MCValue as the result. If
+ // that operand died and was a register, it
+ // was freed by processDeath. We have to
+ // "re-allocate" the register.
+ if (self.register_manager.isRegFree(reg)) {
+ self.register_manager.getRegAssumeFree(reg, inst);
+ }
+ },
+ else => {},
+ }
}
self.finishAirBookkeeping();
}
@@ -1272,6 +1295,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
+ fn airShl(self: *Self, inst: Air.Inst.Index) !void {
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
+ .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .shl),
+ else => return self.fail("TODO implement shl for {}", .{self.target.cpu.arch}),
+ };
+ return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
+ }
+
+ fn airShr(self: *Self, inst: Air.Inst.Index) !void {
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
+ .arm, .armeb => try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .shr),
+ else => return self.fail("TODO implement shr for {}", .{self.target.cpu.arch}),
+ };
+ return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
+ }
+
fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
@@ -1399,6 +1440,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
+ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) {
+ else => return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch}),
+ };
+ return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
+ }
+
fn airPtrPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
const is_volatile = false; // TODO
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
@@ -1439,7 +1489,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return true;
}
- fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) !void {
+ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void {
const elem_ty = ptr_ty.elemType();
switch (ptr) {
.none => unreachable,
@@ -1456,11 +1506,25 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.embedded_in_code => {
return self.fail("TODO implement loading from MCValue.embedded_in_code", .{});
},
- .register => {
- return self.fail("TODO implement loading from MCValue.register", .{});
+ .register => |reg| {
+ switch (arch) {
+ .arm, .armeb => switch (dst_mcv) {
+ .dead => unreachable,
+ .undef => unreachable,
+ .compare_flags_signed, .compare_flags_unsigned => unreachable,
+ .embedded_in_code => unreachable,
+ .register => |dst_reg| {
+ writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, dst_reg, reg, .{ .offset = Instruction.Offset.none }).toU32());
+ },
+ else => return self.fail("TODO load from register into {}", .{dst_mcv}),
+ },
+ else => return self.fail("TODO implement loading from MCValue.register for {}", .{arch}),
+ }
},
- .memory => {
- return self.fail("TODO implement loading from MCValue.memory", .{});
+ .memory => |addr| {
+ const reg = try self.register_manager.allocReg(null, &.{});
+ try self.genSetReg(ptr_ty, reg, .{ .memory = addr });
+ try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
},
.stack_offset => {
return self.fail("TODO implement loading from MCValue.stack_offset", .{});
@@ -1534,7 +1598,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
- _ = extra;
+ return self.structFieldPtr(extra.struct_operand, ty_pl.ty, extra.field_index);
+ }
+
+ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ return self.structFieldPtr(ty_op.operand, ty_op.ty, index);
+ }
+ fn structFieldPtr(self: *Self, operand: Air.Inst.Ref, ty: Air.Inst.Ref, index: u32) !void {
+ _ = self;
+ _ = operand;
+ _ = ty;
+ _ = index;
return self.fail("TODO implement codegen struct_field_ptr", .{});
//return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
}
@@ -1572,15 +1647,53 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue {
+ // In the case of bitshifts, the type of rhs is different
+ // from the resulting type
+ const ty = self.air.typeOf(op_lhs);
+
+ switch (ty.zigTypeTag()) {
+ .Float => return self.fail("TODO ARM binary operations on floats", .{}),
+ .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
+ .Bool => {
+ return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, 1, .unsigned);
+ },
+ .Int => {
+ const int_info = ty.intInfo(self.target.*);
+ return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, int_info.bits, int_info.signedness);
+ },
+ else => unreachable,
+ }
+ }
+
+ fn genArmBinIntOp(
+ self: *Self,
+ inst: Air.Inst.Index,
+ op_lhs: Air.Inst.Ref,
+ op_rhs: Air.Inst.Ref,
+ op: Air.Inst.Tag,
+ bits: u16,
+ signedness: std.builtin.Signedness,
+ ) !MCValue {
+ if (bits > 32) {
+ return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
+ }
+
const lhs = try self.resolveInst(op_lhs);
const rhs = try self.resolveInst(op_rhs);
const lhs_is_register = lhs == .register;
const rhs_is_register = rhs == .register;
- const lhs_should_be_register = try self.armOperandShouldBeRegister(lhs);
+ const lhs_should_be_register = switch (op) {
+ .shr, .shl => true,
+ else => try self.armOperandShouldBeRegister(lhs),
+ };
const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
+ const can_swap_lhs_and_rhs = switch (op) {
+ .shr, .shl => false,
+ else => true,
+ };
// Destination must be a register
var dst_mcv: MCValue = undefined;
@@ -1597,7 +1710,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
}
dst_mcv = lhs;
- } else if (reuse_rhs) {
+ } else if (reuse_rhs and can_swap_lhs_and_rhs) {
// Allocate 0 or 1 registers
if (!lhs_is_register and lhs_should_be_register) {
lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) };
@@ -1636,7 +1749,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) };
lhs_mcv = dst_mcv;
}
- } else if (rhs_should_be_register) {
+ } else if (rhs_should_be_register and can_swap_lhs_and_rhs) {
// LHS is immediate
if (rhs_is_register) {
dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) };
@@ -1663,6 +1776,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
rhs_mcv,
swap_lhs_and_rhs,
op,
+ signedness,
);
return dst_mcv;
}
@@ -1674,6 +1788,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
rhs_mcv: MCValue,
swap_lhs_and_rhs: bool,
op: Air.Inst.Tag,
+ signedness: std.builtin.Signedness,
) !void {
assert(lhs_mcv == .register or rhs_mcv == .register);
@@ -1719,6 +1834,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.cmp_eq => {
writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, op1, operand).toU32());
},
+ .shl => {
+ assert(!swap_lhs_and_rhs);
+ const shift_amout = switch (operand) {
+ .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
+ .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
+ };
+ writeInt(u32, try self.code.addManyAsArray(4), Instruction.lsl(.al, dst_reg, op1, shift_amout).toU32());
+ },
+ .shr => {
+ assert(!swap_lhs_and_rhs);
+ const shift_amout = switch (operand) {
+ .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
+ .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
+ };
+
+ const shr = switch (signedness) {
+ .signed => Instruction.asr,
+ .unsigned => Instruction.lsr,
+ };
+ writeInt(u32, try self.code.addManyAsArray(4), shr(.al, dst_reg, op1, shift_amout).toU32());
+ },
else => unreachable, // not a binary instruction
}
}
@@ -2969,7 +3105,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
// The destination register is not present in the cmp instruction
- try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq);
+ // The signedness of the integer does not matter for the cmp instruction
+ try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq, undefined);
break :result switch (ty.isSignedInt()) {
true => MCValue{ .compare_flags_signed = op },
@@ -3792,15 +3929,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
else => return self.fail("TODO implement memset", .{}),
}
},
- .compare_flags_unsigned => |op| {
- _ = op;
- return self.fail("TODO implement set stack variable with compare flags value (unsigned)", .{});
- },
- .compare_flags_signed => |op| {
- _ = op;
- return self.fail("TODO implement set stack variable with compare flags value (signed)", .{});
- },
- .immediate => {
+ .compare_flags_unsigned,
+ .compare_flags_signed,
+ .immediate,
+ => {
const reg = try self.copyToTmpRegister(ty, mcv);
return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
},
@@ -3968,15 +4100,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
else => return self.fail("TODO implement memset", .{}),
}
},
- .compare_flags_unsigned => |op| {
- _ = op;
- return self.fail("TODO implement set stack variable with compare flags value (unsigned)", .{});
- },
- .compare_flags_signed => |op| {
- _ = op;
- return self.fail("TODO implement set stack variable with compare flags value (signed)", .{});
- },
- .immediate => {
+ .compare_flags_unsigned,
+ .compare_flags_signed,
+ .immediate,
+ => {
const reg = try self.copyToTmpRegister(ty, mcv);
return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
},
diff --git a/src/codegen/arm.zig b/src/codegen/arm.zig
index 891a9e100b..3743afd50f 100644
--- a/src/codegen/arm.zig
+++ b/src/codegen/arm.zig
@@ -192,7 +192,7 @@ pub const c_abi_int_return_regs = [_]Register{ .r0, .r1 };
/// Represents an instruction in the ARM instruction set architecture
pub const Instruction = union(enum) {
- DataProcessing: packed struct {
+ data_processing: packed struct {
// Note to self: The order of the fields top-to-bottom is
// right-to-left in the actual 32-bit int representation
op2: u12,
@@ -204,7 +204,7 @@ pub const Instruction = union(enum) {
fixed: u2 = 0b00,
cond: u4,
},
- Multiply: packed struct {
+ multiply: packed struct {
rn: u4,
fixed_1: u4 = 0b1001,
rm: u4,
@@ -215,7 +215,7 @@ pub const Instruction = union(enum) {
fixed_2: u6 = 0b000000,
cond: u4,
},
- MultiplyLong: packed struct {
+ multiply_long: packed struct {
rn: u4,
fixed_1: u4 = 0b1001,
rm: u4,
@@ -227,7 +227,17 @@ pub const Instruction = union(enum) {
fixed_2: u5 = 0b00001,
cond: u4,
},
- SingleDataTransfer: packed struct {
+ integer_saturating_arithmetic: packed struct {
+ rm: u4,
+ fixed_1: u8 = 0b0000_0101,
+ rd: u4,
+ rn: u4,
+ fixed_2: u1 = 0b0,
+ opc: u2,
+ fixed_3: u5 = 0b00010,
+ cond: u4,
+ },
+ single_data_transfer: packed struct {
offset: u12,
rd: u4,
rn: u4,
@@ -240,7 +250,7 @@ pub const Instruction = union(enum) {
fixed: u2 = 0b01,
cond: u4,
},
- ExtraLoadStore: packed struct {
+ extra_load_store: packed struct {
imm4l: u4,
fixed_1: u1 = 0b1,
op2: u2,
@@ -256,7 +266,7 @@ pub const Instruction = union(enum) {
fixed_3: u3 = 0b000,
cond: u4,
},
- BlockDataTransfer: packed struct {
+ block_data_transfer: packed struct {
register_list: u16,
rn: u4,
load_store: u1,
@@ -267,25 +277,25 @@ pub const Instruction = union(enum) {
fixed: u3 = 0b100,
cond: u4,
},
- Branch: packed struct {
+ branch: packed struct {
offset: u24,
link: u1,
fixed: u3 = 0b101,
cond: u4,
},
- BranchExchange: packed struct {
+ branch_exchange: packed struct {
rn: u4,
fixed_1: u1 = 0b1,
link: u1,
fixed_2: u22 = 0b0001_0010_1111_1111_1111_00,
cond: u4,
},
- SupervisorCall: packed struct {
+ supervisor_call: packed struct {
comment: u24,
fixed: u4 = 0b1111,
cond: u4,
},
- Breakpoint: packed struct {
+ breakpoint: packed struct {
imm4: u4,
fixed_1: u4 = 0b0111,
imm12: u12,
@@ -293,7 +303,7 @@ pub const Instruction = union(enum) {
},
/// Represents the possible operations which can be performed by a
- /// DataProcessing instruction
+ /// Data Processing instruction
const Opcode = enum(u4) {
// Rd := Op1 AND Op2
@"and",
@@ -530,16 +540,17 @@ pub const Instruction = union(enum) {
pub fn toU32(self: Instruction) u32 {
return switch (self) {
- .DataProcessing => |v| @bitCast(u32, v),
- .Multiply => |v| @bitCast(u32, v),
- .MultiplyLong => |v| @bitCast(u32, v),
- .SingleDataTransfer => |v| @bitCast(u32, v),
- .ExtraLoadStore => |v| @bitCast(u32, v),
- .BlockDataTransfer => |v| @bitCast(u32, v),
- .Branch => |v| @bitCast(u32, v),
- .BranchExchange => |v| @bitCast(u32, v),
- .SupervisorCall => |v| @bitCast(u32, v),
- .Breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20),
+ .data_processing => |v| @bitCast(u32, v),
+ .multiply => |v| @bitCast(u32, v),
+ .multiply_long => |v| @bitCast(u32, v),
+ .integer_saturating_arithmetic => |v| @bitCast(u32, v),
+ .single_data_transfer => |v| @bitCast(u32, v),
+ .extra_load_store => |v| @bitCast(u32, v),
+ .block_data_transfer => |v| @bitCast(u32, v),
+ .branch => |v| @bitCast(u32, v),
+ .branch_exchange => |v| @bitCast(u32, v),
+ .supervisor_call => |v| @bitCast(u32, v),
+ .breakpoint => |v| @intCast(u32, v.imm4) | (@intCast(u32, v.fixed_1) << 4) | (@intCast(u32, v.imm12) << 8) | (@intCast(u32, v.fixed_2_and_cond) << 20),
};
}
@@ -554,7 +565,7 @@ pub const Instruction = union(enum) {
op2: Operand,
) Instruction {
return Instruction{
- .DataProcessing = .{
+ .data_processing = .{
.cond = @enumToInt(cond),
.i = @boolToInt(op2 == .Immediate),
.opcode = @enumToInt(opcode),
@@ -573,7 +584,7 @@ pub const Instruction = union(enum) {
top: bool,
) Instruction {
return Instruction{
- .DataProcessing = .{
+ .data_processing = .{
.cond = @enumToInt(cond),
.i = 1,
.opcode = if (top) 0b1010 else 0b1000,
@@ -594,7 +605,7 @@ pub const Instruction = union(enum) {
ra: ?Register,
) Instruction {
return Instruction{
- .Multiply = .{
+ .multiply = .{
.cond = @enumToInt(cond),
.accumulate = @boolToInt(ra != null),
.set_cond = set_cond,
@@ -617,7 +628,7 @@ pub const Instruction = union(enum) {
rn: Register,
) Instruction {
return Instruction{
- .MultiplyLong = .{
+ .multiply_long = .{
.cond = @enumToInt(cond),
.unsigned = signed,
.accumulate = accumulate,
@@ -630,6 +641,24 @@ pub const Instruction = union(enum) {
};
}
+ fn integerSaturationArithmetic(
+ cond: Condition,
+ rd: Register,
+ rm: Register,
+ rn: Register,
+ opc: u2,
+ ) Instruction {
+ return Instruction{
+ .integer_saturating_arithmetic = .{
+ .rm = rm.id(),
+ .rd = rd.id(),
+ .rn = rn.id(),
+ .opc = opc,
+ .cond = @enumToInt(cond),
+ },
+ };
+ }
+
fn singleDataTransfer(
cond: Condition,
rd: Register,
@@ -642,7 +671,7 @@ pub const Instruction = union(enum) {
load_store: u1,
) Instruction {
return Instruction{
- .SingleDataTransfer = .{
+ .single_data_transfer = .{
.cond = @enumToInt(cond),
.rn = rn.id(),
.rd = rd.id(),
@@ -678,7 +707,7 @@ pub const Instruction = union(enum) {
};
return Instruction{
- .ExtraLoadStore = .{
+ .extra_load_store = .{
.imm4l = imm4l,
.op2 = op2,
.imm4h = imm4h,
@@ -705,7 +734,7 @@ pub const Instruction = union(enum) {
load_store: u1,
) Instruction {
return Instruction{
- .BlockDataTransfer = .{
+ .block_data_transfer = .{
.register_list = @bitCast(u16, reg_list),
.rn = rn.id(),
.load_store = load_store,
@@ -720,7 +749,7 @@ pub const Instruction = union(enum) {
fn branch(cond: Condition, offset: i26, link: u1) Instruction {
return Instruction{
- .Branch = .{
+ .branch = .{
.cond = @enumToInt(cond),
.link = link,
.offset = @bitCast(u24, @intCast(i24, offset >> 2)),
@@ -730,7 +759,7 @@ pub const Instruction = union(enum) {
fn branchExchange(cond: Condition, rn: Register, link: u1) Instruction {
return Instruction{
- .BranchExchange = .{
+ .branch_exchange = .{
.cond = @enumToInt(cond),
.link = link,
.rn = rn.id(),
@@ -740,7 +769,7 @@ pub const Instruction = union(enum) {
fn supervisorCall(cond: Condition, comment: u24) Instruction {
return Instruction{
- .SupervisorCall = .{
+ .supervisor_call = .{
.cond = @enumToInt(cond),
.comment = comment,
},
@@ -749,7 +778,7 @@ pub const Instruction = union(enum) {
fn breakpoint(imm: u16) Instruction {
return Instruction{
- .Breakpoint = .{
+ .breakpoint = .{
.imm12 = @truncate(u12, imm >> 4),
.imm4 = @truncate(u4, imm),
},
@@ -873,6 +902,24 @@ pub const Instruction = union(enum) {
return dataProcessing(cond, .mvn, 1, rd, .r0, op2);
}
+ // Integer Saturating Arithmetic
+
+ pub fn qadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
+ return integerSaturationArithmetic(cond, rd, rm, rn, 0b00);
+ }
+
+ pub fn qsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
+ return integerSaturationArithmetic(cond, rd, rm, rn, 0b01);
+ }
+
+ pub fn qdadd(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
+ return integerSaturationArithmetic(cond, rd, rm, rn, 0b10);
+ }
+
+ pub fn qdsub(cond: Condition, rd: Register, rm: Register, rn: Register) Instruction {
+ return integerSaturationArithmetic(cond, rd, rm, rn, 0b11);
+ }
+
// movw and movt
pub fn movw(cond: Condition, rd: Register, imm: u16) Instruction {
@@ -887,7 +934,7 @@ pub const Instruction = union(enum) {
pub fn mrs(cond: Condition, rd: Register, psr: Psr) Instruction {
return Instruction{
- .DataProcessing = .{
+ .data_processing = .{
.cond = @enumToInt(cond),
.i = 0,
.opcode = if (psr == .spsr) 0b1010 else 0b1000,
@@ -901,7 +948,7 @@ pub const Instruction = union(enum) {
pub fn msr(cond: Condition, psr: Psr, op: Operand) Instruction {
return Instruction{
- .DataProcessing = .{
+ .data_processing = .{
.cond = @enumToInt(cond),
.i = 0,
.opcode = if (psr == .spsr) 0b1011 else 0b1001,
@@ -1142,6 +1189,79 @@ pub const Instruction = union(enum) {
return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list));
}
}
+
+ pub const ShiftAmount = union(enum) {
+ immediate: u5,
+ register: Register,
+
+ pub fn imm(immediate: u5) ShiftAmount {
+ return .{
+ .immediate = immediate,
+ };
+ }
+
+ pub fn reg(register: Register) ShiftAmount {
+ return .{
+ .register = register,
+ };
+ }
+ };
+
+ pub fn lsl(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))),
+ .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))),
+ };
+ }
+
+ pub fn lsr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))),
+ .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))),
+ };
+ }
+
+ pub fn asr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))),
+ .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))),
+ };
+ }
+
+ pub fn ror(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))),
+ .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))),
+ };
+ }
+
+ pub fn lsls(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))),
+ .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))),
+ };
+ }
+
+ pub fn lsrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))),
+ .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))),
+ };
+ }
+
+ pub fn asrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))),
+ .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))),
+ };
+ }
+
+ pub fn rors(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction {
+ return switch (shift) {
+ .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))),
+ .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))),
+ };
+ }
};
test "serialize instructions" {
@@ -1221,6 +1341,10 @@ test "serialize instructions" {
.inst = Instruction.ldmea(.al, .r4, true, .{ .r2 = true, .r5 = true }),
.expected = 0b1110_100_1_0_0_1_1_0100_0000000000100100,
},
+ .{ // qadd r0, r7, r8
+ .inst = Instruction.qadd(.al, .r0, .r7, .r8),
+ .expected = 0b1110_00010_00_0_1000_0000_0000_0101_0111,
+ },
};
for (testcases) |case| {
@@ -1262,6 +1386,20 @@ test "aliases" {
.actual = Instruction.push(.al, .{ .r0, .r2 }),
.expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }),
},
+ .{ // lsl r4, r5, #5
+ .actual = Instruction.lsl(.al, .r4, .r5, Instruction.ShiftAmount.imm(5)),
+ .expected = Instruction.mov(.al, .r4, Instruction.Operand.reg(
+ .r5,
+ Instruction.Operand.Shift.imm(5, .logical_left),
+ )),
+ },
+ .{ // asrs r1, r1, r3
+ .actual = Instruction.asrs(.al, .r1, .r1, Instruction.ShiftAmount.reg(.r3)),
+ .expected = Instruction.movs(.al, .r1, Instruction.Operand.reg(
+ .r1,
+ Instruction.Operand.Shift.reg(.r3, .arithmetic_right),
+ )),
+ },
};
for (testcases) |case| {
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index a67e2438c2..2084b1e1ce 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -319,18 +319,20 @@ pub const DeclGen = struct {
.Bool => return writer.print("{}", .{val.toBool()}),
.Optional => {
var opt_buf: Type.Payload.ElemType = undefined;
- const child_type = t.optionalChild(&opt_buf);
+ const payload_type = t.optionalChild(&opt_buf);
if (t.isPtrLikeOptional()) {
- return dg.renderValue(writer, child_type, val);
+ return dg.renderValue(writer, payload_type, val);
}
try writer.writeByte('(');
try dg.renderType(writer, t);
- if (val.tag() == .null_value) {
- try writer.writeAll("){ .is_null = true }");
- } else {
- try writer.writeAll("){ .is_null = false, .payload = ");
- try dg.renderValue(writer, child_type, val);
+ try writer.writeAll("){");
+ if (val.castTag(.opt_payload)) |pl| {
+ const payload_val = pl.data;
+ try writer.writeAll(" .is_null = false, .payload = ");
+ try dg.renderValue(writer, payload_type, payload_val);
try writer.writeAll(" }");
+ } else {
+ try writer.writeAll(" .is_null = true }");
}
},
.ErrorSet => {
@@ -871,6 +873,9 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
.bit_or => try airBinOp(o, inst, " | "),
.xor => try airBinOp(o, inst, " ^ "),
+ .shr => try airBinOp(o, inst, " >> "),
+ .shl => try airBinOp(o, inst, " << "),
+
.not => try airNot( o, inst),
.optional_payload => try airOptionalPayload(o, inst),
@@ -904,12 +909,19 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
.switch_br => try airSwitchBr(o, inst),
.wrap_optional => try airWrapOptional(o, inst),
.struct_field_ptr => try airStructFieldPtr(o, inst),
+
+ .struct_field_ptr_index_0 => try airStructFieldPtrIndex(o, inst, 0),
+ .struct_field_ptr_index_1 => try airStructFieldPtrIndex(o, inst, 1),
+ .struct_field_ptr_index_2 => try airStructFieldPtrIndex(o, inst, 2),
+ .struct_field_ptr_index_3 => try airStructFieldPtrIndex(o, inst, 3),
+
.struct_field_val => try airStructFieldVal(o, inst),
.slice_ptr => try airSliceField(o, inst, ".ptr;\n"),
.slice_len => try airSliceField(o, inst, ".len;\n"),
.ptr_elem_val => try airPtrElemVal(o, inst, "["),
.ptr_ptr_elem_val => try airPtrElemVal(o, inst, "[0]["),
+ .ptr_elem_ptr => try airPtrElemPtr(o, inst),
.slice_elem_val => try airSliceElemVal(o, inst, "["),
.ptr_slice_elem_val => try airSliceElemVal(o, inst, "[0]["),
@@ -957,6 +969,13 @@ fn airPtrElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue {
return o.dg.fail("TODO: C backend: airPtrElemVal", .{});
}
+fn airPtrElemPtr(o: *Object, inst: Air.Inst.Index) !CValue {
+ if (o.liveness.isUnused(inst))
+ return CValue.none;
+
+ return o.dg.fail("TODO: C backend: airPtrElemPtr", .{});
+}
+
fn airSliceElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue {
const is_volatile = false; // TODO
if (!is_volatile and o.liveness.isUnused(inst))
@@ -1638,15 +1657,31 @@ fn airOptionalPayload(o: *Object, inst: Air.Inst.Index) !CValue {
fn airStructFieldPtr(o: *Object, inst: Air.Inst.Index) !CValue {
if (o.liveness.isUnused(inst))
- return CValue.none;
+ // TODO this @as is needed because of a stage1 bug
+ return @as(CValue, CValue.none);
const ty_pl = o.air.instructions.items(.data)[inst].ty_pl;
const extra = o.air.extraData(Air.StructField, ty_pl.payload).data;
- const writer = o.writer();
const struct_ptr = try o.resolveInst(extra.struct_operand);
const struct_ptr_ty = o.air.typeOf(extra.struct_operand);
+ return structFieldPtr(o, inst, struct_ptr_ty, struct_ptr, extra.field_index);
+}
+
+fn airStructFieldPtrIndex(o: *Object, inst: Air.Inst.Index, index: u8) !CValue {
+ if (o.liveness.isUnused(inst))
+ // TODO this @as is needed because of a stage1 bug
+ return @as(CValue, CValue.none);
+
+ const ty_op = o.air.instructions.items(.data)[inst].ty_op;
+ const struct_ptr = try o.resolveInst(ty_op.operand);
+ const struct_ptr_ty = o.air.typeOf(ty_op.operand);
+ return structFieldPtr(o, inst, struct_ptr_ty, struct_ptr, index);
+}
+
+fn structFieldPtr(o: *Object, inst: Air.Inst.Index, struct_ptr_ty: Type, struct_ptr: CValue, index: u32) !CValue {
+ const writer = o.writer();
const struct_obj = struct_ptr_ty.elemType().castTag(.@"struct").?.data;
- const field_name = struct_obj.fields.keys()[extra.field_index];
+ const field_name = struct_obj.fields.keys()[index];
const inst_ty = o.air.typeOfIndex(inst);
const local = try o.allocLocal(inst_ty, .Const);
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 2b678b0a69..0a715edbfd 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -434,6 +434,8 @@ pub const Object = struct {
},
else => |e| return e,
};
+ const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
+ try self.updateDeclExports(module, decl, decl_exports);
}
pub fn updateDeclExports(
@@ -442,7 +444,9 @@ pub const Object = struct {
decl: *const Module.Decl,
exports: []const *Module.Export,
) !void {
- const llvm_fn = self.llvm_module.getNamedFunction(decl.name).?;
+ // If the module does not already have the function, we ignore this function call
+ // because we call `updateDeclExports` at the end of `updateFunc` and `updateDecl`.
+ const llvm_fn = self.llvm_module.getNamedFunction(decl.name) orelse return;
const is_extern = decl.val.tag() == .extern_fn;
if (is_extern or exports.len != 0) {
llvm_fn.setLinkage(.External);
@@ -808,27 +812,22 @@ pub const DeclGen = struct {
return self.todo("handle more array values", .{});
},
.Optional => {
- if (!tv.ty.isPtrLikeOptional()) {
- var buf: Type.Payload.ElemType = undefined;
- const child_type = tv.ty.optionalChild(&buf);
- const llvm_child_type = try self.llvmType(child_type);
-
- if (tv.val.tag() == .null_value) {
- var optional_values: [2]*const llvm.Value = .{
- llvm_child_type.constNull(),
- self.context.intType(1).constNull(),
- };
- return self.context.constStruct(&optional_values, optional_values.len, .False);
- } else {
- var optional_values: [2]*const llvm.Value = .{
- try self.genTypedValue(.{ .ty = child_type, .val = tv.val }),
- self.context.intType(1).constAllOnes(),
- };
- return self.context.constStruct(&optional_values, optional_values.len, .False);
- }
- } else {
+ if (tv.ty.isPtrLikeOptional()) {
return self.todo("implement const of optional pointer", .{});
}
+ var buf: Type.Payload.ElemType = undefined;
+ const payload_type = tv.ty.optionalChild(&buf);
+ const is_pl = !tv.val.isNull();
+ const llvm_i1 = self.context.intType(1);
+
+ const fields: [2]*const llvm.Value = .{
+ try self.genTypedValue(.{
+ .ty = payload_type,
+ .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
+ }),
+ if (is_pl) llvm_i1.constAllOnes() else llvm_i1.constNull(),
+ };
+ return self.context.constStruct(&fields, fields.len, .False);
},
.Fn => {
const fn_decl = switch (tv.val.tag()) {
@@ -995,6 +994,9 @@ pub const FuncGen = struct {
.bit_or, .bool_or => try self.airOr(inst),
.xor => try self.airXor(inst),
+ .shl => try self.airShl(inst),
+ .shr => try self.airShr(inst),
+
.cmp_eq => try self.airCmp(inst, .eq),
.cmp_gt => try self.airCmp(inst, .gt),
.cmp_gte => try self.airCmp(inst, .gte),
@@ -1037,9 +1039,15 @@ pub const FuncGen = struct {
.struct_field_ptr => try self.airStructFieldPtr(inst),
.struct_field_val => try self.airStructFieldVal(inst),
+ .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
+ .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
+ .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
+ .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
+
.slice_elem_val => try self.airSliceElemVal(inst),
.ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
.ptr_elem_val => try self.airPtrElemVal(inst),
+ .ptr_elem_ptr => try self.airPtrElemPtr(inst),
.ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst),
.optional_payload => try self.airOptionalPayload(inst, false),
@@ -1295,11 +1303,35 @@ pub const FuncGen = struct {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const base_ptr = try self.resolveInst(bin_op.lhs);
const rhs = try self.resolveInst(bin_op.rhs);
- const indices: [1]*const llvm.Value = .{rhs};
- const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
+ const ptr = if (self.air.typeOf(bin_op.lhs).isSinglePointer()) ptr: {
+ // If this is a single-item pointer to an array, we need another index in the GEP.
+ const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };
+ break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
+ } else ptr: {
+ const indices: [1]*const llvm.Value = .{rhs};
+ break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
+ };
return self.builder.buildLoad(ptr, "");
}
+ fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst))
+ return null;
+
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
+ const base_ptr = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ if (self.air.typeOf(bin_op.lhs).isSinglePointer()) {
+ // If this is a single-item pointer to an array, we need another index in the GEP.
+ const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs };
+ return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
+ } else {
+ const indices: [1]*const llvm.Value = .{rhs};
+ return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, "");
+ }
+ }
+
fn airPtrPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
const is_volatile = false; // TODO
if (!is_volatile and self.liveness.isUnused(inst))
@@ -1325,6 +1357,15 @@ pub const FuncGen = struct {
return self.builder.buildStructGEP(struct_ptr, field_index, "");
}
+ fn airStructFieldPtrIndex(self: *FuncGen, inst: Air.Inst.Index, field_index: c_uint) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst))
+ return null;
+
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const struct_ptr = try self.resolveInst(ty_op.operand);
+ return self.builder.buildStructGEP(struct_ptr, field_index, "");
+ }
+
fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
if (self.liveness.isUnused(inst))
return null;
@@ -1739,6 +1780,41 @@ pub const FuncGen = struct {
return self.builder.buildXor(lhs, rhs, "");
}
+ fn airShl(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst))
+ return null;
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ const lhs_type = self.air.typeOf(bin_op.lhs);
+ const tg = self.dg.module.getTarget();
+ const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg))
+ self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "")
+ else
+ rhs;
+ return self.builder.buildShl(lhs, casted_rhs, "");
+ }
+
+ fn airShr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst))
+ return null;
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const lhs = try self.resolveInst(bin_op.lhs);
+ const rhs = try self.resolveInst(bin_op.rhs);
+ const lhs_type = self.air.typeOf(bin_op.lhs);
+ const tg = self.dg.module.getTarget();
+ const casted_rhs = if (self.air.typeOf(bin_op.rhs).bitSize(tg) < lhs_type.bitSize(tg))
+ self.builder.buildZExt(rhs, try self.dg.llvmType(lhs_type), "")
+ else
+ rhs;
+
+ if (self.air.typeOfIndex(inst).isSignedInt()) {
+ return self.builder.buildAShr(lhs, casted_rhs, "");
+ } else {
+ return self.builder.buildLShr(lhs, casted_rhs, "");
+ }
+ }
+
fn airIntCast(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
if (self.liveness.isUnused(inst))
return null;
diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig
index fc97dfb81f..fe0c211df3 100644
--- a/src/codegen/llvm/bindings.zig
+++ b/src/codegen/llvm/bindings.zig
@@ -291,6 +291,14 @@ pub const Builder = opaque {
pub const getInsertBlock = LLVMGetInsertBlock;
extern fn LLVMGetInsertBlock(Builder: *const Builder) *const BasicBlock;
+ pub const buildZExt = LLVMBuildZExt;
+ extern fn LLVMBuildZExt(
+ *const Builder,
+ Value: *const Value,
+ DestTy: *const Type,
+ Name: [*:0]const u8,
+ ) *const Value;
+
pub const buildCall = LLVMBuildCall;
extern fn LLVMBuildCall(
*const Builder,
@@ -382,6 +390,15 @@ pub const Builder = opaque {
pub const buildAnd = LLVMBuildAnd;
extern fn LLVMBuildAnd(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+ pub const buildLShr = LLVMBuildLShr;
+ extern fn LLVMBuildLShr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+
+ pub const buildAShr = LLVMBuildAShr;
+ extern fn LLVMBuildAShr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+
+ pub const buildShl = LLVMBuildShl;
+ extern fn LLVMBuildShl(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
+
pub const buildOr = LLVMBuildOr;
extern fn LLVMBuildOr(*const Builder, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig
index 4814ba0b55..bb05567236 100644
--- a/src/codegen/wasm.zig
+++ b/src/codegen/wasm.zig
@@ -862,6 +862,10 @@ pub const Context = struct {
.ret => self.airRet(inst),
.store => self.airStore(inst),
.struct_field_ptr => self.airStructFieldPtr(inst),
+ .struct_field_ptr_index_0 => self.airStructFieldPtrIndex(inst, 0),
+ .struct_field_ptr_index_1 => self.airStructFieldPtrIndex(inst, 1),
+ .struct_field_ptr_index_2 => self.airStructFieldPtrIndex(inst, 2),
+ .struct_field_ptr_index_3 => self.airStructFieldPtrIndex(inst, 3),
.switch_br => self.airSwitchBr(inst),
.unreach => self.airUnreachable(inst),
.wrap_optional => self.airWrapOptional(inst),
@@ -1198,7 +1202,12 @@ pub const Context = struct {
// When constant has value 'null', set is_null local to '1'
// and payload to '0'
- if (val.tag() == .null_value) {
+ if (val.castTag(.opt_payload)) |pl| {
+ const payload_val = pl.data;
+ try writer.writeByte(wasm.opcode(.i32_const));
+ try leb.writeILEB128(writer, @as(i32, 0));
+ try self.emitConstant(payload_val, payload_type);
+ } else {
try writer.writeByte(wasm.opcode(.i32_const));
try leb.writeILEB128(writer, @as(i32, 1));
@@ -1208,10 +1217,6 @@ pub const Context = struct {
});
try writer.writeByte(wasm.opcode(opcode));
try leb.writeULEB128(writer, @as(u32, 0));
- } else {
- try writer.writeByte(wasm.opcode(.i32_const));
- try leb.writeILEB128(writer, @as(i32, 0));
- try self.emitConstant(val, payload_type);
}
},
else => |zig_type| return self.fail("Wasm TODO: emitConstant for zigTypeTag {s}", .{zig_type}),
@@ -1440,8 +1445,15 @@ pub const Context = struct {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.StructField, ty_pl.payload);
const struct_ptr = self.resolveInst(extra.data.struct_operand);
-
- return WValue{ .local = struct_ptr.multi_value.index + @intCast(u32, extra.data.field_index) };
+ return structFieldPtr(struct_ptr, extra.data.field_index);
+ }
+ fn airStructFieldPtrIndex(self: *Context, inst: Air.Inst.Index, index: u32) InnerError!WValue {
+ const ty_op = self.air.instructions.items(.data)[inst].ty_op;
+ const struct_ptr = self.resolveInst(ty_op.operand);
+ return structFieldPtr(struct_ptr, index);
+ }
+ fn structFieldPtr(struct_ptr: WValue, index: u32) InnerError!WValue {
+ return WValue{ .local = struct_ptr.multi_value.index + index };
}
fn airSwitchBr(self: *Context, inst: Air.Inst.Index) InnerError!WValue {
diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig
index 3c3cd4eef3..fc559948c4 100644
--- a/src/link/Wasm.zig
+++ b/src/link/Wasm.zig
@@ -765,12 +765,8 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
if (self.base.options.wasi_exec_model == .reactor) {
// Reactor execution model does not have _start so lld doesn't look for it.
try argv.append("--no-entry");
- // Make sure "_initialize" is exported even if this is pure Zig WASI reactor
- // where WASM_SYMBOL_EXPORTED flag in LLVM is not set on _initialize.
- try argv.appendSlice(&[_][]const u8{
- "--export",
- "_initialize",
- });
+ // Make sure "_initialize" and other used-defined functions are exported if this is WASI reactor.
+ try argv.append("--export-dynamic");
}
} else {
try argv.append("--no-entry"); // So lld doesn't look for _start.
diff --git a/src/main.zig b/src/main.zig
index 285b6d2316..44b3a0515c 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -2492,6 +2492,7 @@ fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !voi
const digest = if (try man.hit()) man.final() else digest: {
var argv = std.ArrayList([]const u8).init(arena);
+ try argv.append(""); // argv[0] is program name, actual args start at [1]
var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
defer zig_cache_tmp_dir.close();
diff --git a/src/mingw.zig b/src/mingw.zig
index 42d1ac47db..529025c517 100644
--- a/src/mingw.zig
+++ b/src/mingw.zig
@@ -187,27 +187,25 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
};
}
} else if (target.cpu.arch.isARM()) {
- if (target.cpu.arch.ptrBitWidth() == 32) {
- for (mingwex_arm32_src) |dep| {
- (try c_source_files.addOne()).* = .{
- .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
- "libc", "mingw", dep,
- }),
- .extra_flags = extra_flags,
- };
- }
- } else {
- for (mingwex_arm64_src) |dep| {
- (try c_source_files.addOne()).* = .{
- .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
- "libc", "mingw", dep,
- }),
- .extra_flags = extra_flags,
- };
- }
+ for (mingwex_arm32_src) |dep| {
+ (try c_source_files.addOne()).* = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "mingw", dep,
+ }),
+ .extra_flags = extra_flags,
+ };
+ }
+ } else if (target.cpu.arch.isAARCH64()) {
+ for (mingwex_arm64_src) |dep| {
+ (try c_source_files.addOne()).* = .{
+ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
+ "libc", "mingw", dep,
+ }),
+ .extra_flags = extra_flags,
+ };
}
} else {
- unreachable;
+ @panic("unsupported arch");
}
return comp.build_crt_file("mingwex", .Lib, c_source_files.items);
},
diff --git a/src/print_air.zig b/src/print_air.zig
index 66490b6512..276158f720 100644
--- a/src/print_air.zig
+++ b/src/print_air.zig
@@ -127,6 +127,8 @@ const Writer = struct {
.ptr_slice_elem_val,
.ptr_elem_val,
.ptr_ptr_elem_val,
+ .shl,
+ .shr,
=> try w.writeBinOp(s, inst),
.is_null,
@@ -167,12 +169,17 @@ const Writer = struct {
.wrap_errunion_err,
.slice_ptr,
.slice_len,
+ .struct_field_ptr_index_0,
+ .struct_field_ptr_index_1,
+ .struct_field_ptr_index_2,
+ .struct_field_ptr_index_3,
=> try w.writeTyOp(s, inst),
.block,
.loop,
=> try w.writeBlock(s, inst),
+ .ptr_elem_ptr => try w.writePtrElemPtr(s, inst),
.struct_field_ptr => try w.writeStructField(s, inst),
.struct_field_val => try w.writeStructField(s, inst),
.constant => try w.writeConstant(s, inst),
@@ -237,10 +244,19 @@ const Writer = struct {
fn writeStructField(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
- const extra = w.air.extraData(Air.StructField, ty_pl.payload);
+ const extra = w.air.extraData(Air.StructField, ty_pl.payload).data;
- try w.writeOperand(s, inst, 0, extra.data.struct_operand);
- try s.print(", {d}", .{extra.data.field_index});
+ try w.writeOperand(s, inst, 0, extra.struct_operand);
+ try s.print(", {d}", .{extra.field_index});
+ }
+
+ fn writePtrElemPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
+ const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
+ const extra = w.air.extraData(Air.Bin, ty_pl.payload).data;
+
+ try w.writeOperand(s, inst, 0, extra.lhs);
+ try s.writeAll(", ");
+ try w.writeOperand(s, inst, 0, extra.rhs);
}
fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp
index dfebc66cfd..fc3b00d6db 100644
--- a/src/stage1/all_types.hpp
+++ b/src/stage1/all_types.hpp
@@ -1125,6 +1125,7 @@ struct AstNodeContainerInitExpr {
struct AstNodeIdentifier {
Buf *name;
+ bool is_at_syntax;
};
struct AstNodeEnumLiteral {
diff --git a/src/stage1/analyze.cpp b/src/stage1/analyze.cpp
index cf61bf79b7..2eb609ef1a 100644
--- a/src/stage1/analyze.cpp
+++ b/src/stage1/analyze.cpp
@@ -3918,12 +3918,6 @@ static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
add_error_note(g, msg, other_tld->source_node, buf_sprintf("previous definition here"));
return;
}
-
- ZigType *type;
- if (get_primitive_type(g, tld->name, &type) != ErrorPrimitiveTypeNotFound) {
- add_node_error(g, tld->source_node,
- buf_sprintf("declaration shadows primitive type '%s'", buf_ptr(tld->name)));
- }
}
}
@@ -4170,48 +4164,6 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf
variable_entry->var_type = g->builtin_types.entry_invalid;
} else {
variable_entry->align_bytes = get_abi_alignment(g, var_type);
-
- ZigVar *existing_var = find_variable(g, parent_scope, name, nullptr);
- if (existing_var && !existing_var->shadowable) {
- if (existing_var->var_type == nullptr || !type_is_invalid(existing_var->var_type)) {
- ErrorMsg *msg = add_node_error(g, source_node,
- buf_sprintf("redeclaration of variable '%s'", buf_ptr(name)));
- add_error_note(g, msg, existing_var->decl_node, buf_sprintf("previous declaration here"));
- }
- variable_entry->var_type = g->builtin_types.entry_invalid;
- } else {
- ZigType *type;
- if (get_primitive_type(g, name, &type) != ErrorPrimitiveTypeNotFound) {
- add_node_error(g, source_node,
- buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name)));
- variable_entry->var_type = g->builtin_types.entry_invalid;
- } else {
- Scope *search_scope = nullptr;
- if (src_tld == nullptr) {
- search_scope = parent_scope;
- } else if (src_tld->parent_scope != nullptr && src_tld->parent_scope->parent != nullptr) {
- search_scope = src_tld->parent_scope->parent;
- }
- if (search_scope != nullptr) {
- Tld *tld = find_decl(g, search_scope, name);
- if (tld != nullptr && tld != src_tld) {
- bool want_err_msg = true;
- if (tld->id == TldIdVar) {
- ZigVar *var = reinterpret_cast<TldVar *>(tld)->var;
- if (var != nullptr && var->var_type != nullptr && type_is_invalid(var->var_type)) {
- want_err_msg = false;
- }
- }
- if (want_err_msg) {
- ErrorMsg *msg = add_node_error(g, source_node,
- buf_sprintf("redefinition of '%s'", buf_ptr(name)));
- add_error_note(g, msg, tld->source_node, buf_sprintf("previous definition here"));
- }
- variable_entry->var_type = g->builtin_types.entry_invalid;
- }
- }
- }
- }
}
Scope *child_scope;
diff --git a/src/stage1/astgen.cpp b/src/stage1/astgen.cpp
index 44dc1080c2..367fd8ac08 100644
--- a/src/stage1/astgen.cpp
+++ b/src/stage1/astgen.cpp
@@ -3194,30 +3194,6 @@ ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope,
add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration here"));
}
variable_entry->var_type = codegen->builtin_types.entry_invalid;
- } else {
- ZigType *type;
- if (get_primitive_type(codegen, name, &type) != ErrorPrimitiveTypeNotFound) {
- add_node_error(codegen, node,
- buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name)));
- variable_entry->var_type = codegen->builtin_types.entry_invalid;
- } else {
- Tld *tld = find_decl(codegen, parent_scope, name);
- if (tld != nullptr) {
- bool want_err_msg = true;
- if (tld->id == TldIdVar) {
- ZigVar *var = reinterpret_cast<TldVar *>(tld)->var;
- if (var != nullptr && var->var_type != nullptr && type_is_invalid(var->var_type)) {
- want_err_msg = false;
- }
- }
- if (want_err_msg) {
- ErrorMsg *msg = add_node_error(codegen, node,
- buf_sprintf("redefinition of '%s'", buf_ptr(name)));
- add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition here"));
- }
- variable_entry->var_type = codegen->builtin_types.entry_invalid;
- }
- }
}
}
} else {
@@ -3832,35 +3808,38 @@ static Stage1ZirInst *astgen_identifier(Stage1AstGen *ag, Scope *scope, AstNode
Error err;
assert(node->type == NodeTypeIdentifier);
- Buf *variable_name = node_identifier_buf(node);
-
- if (buf_eql_str(variable_name, "_")) {
- if (lval == LValAssign) {
- Stage1ZirInstConst *const_instruction = ir_build_instruction<Stage1ZirInstConst>(ag, scope, node);
- const_instruction->value = ag->codegen->pass1_arena->create<ZigValue>();
- const_instruction->value->type = get_pointer_to_type(ag->codegen,
- ag->codegen->builtin_types.entry_void, false);
- const_instruction->value->special = ConstValSpecialStatic;
- const_instruction->value->data.x_ptr.special = ConstPtrSpecialDiscard;
- return &const_instruction->base;
+ bool is_at_syntax;
+ Buf *variable_name = node_identifier_buf2(node, &is_at_syntax);
+
+ if (!is_at_syntax) {
+ if (buf_eql_str(variable_name, "_")) {
+ if (lval == LValAssign) {
+ Stage1ZirInstConst *const_instruction = ir_build_instruction<Stage1ZirInstConst>(ag, scope, node);
+ const_instruction->value = ag->codegen->pass1_arena->create<ZigValue>();
+ const_instruction->value->type = get_pointer_to_type(ag->codegen,
+ ag->codegen->builtin_types.entry_void, false);
+ const_instruction->value->special = ConstValSpecialStatic;
+ const_instruction->value->data.x_ptr.special = ConstPtrSpecialDiscard;
+ return &const_instruction->base;
+ }
}
- }
- ZigType *primitive_type;
- if ((err = get_primitive_type(ag->codegen, variable_name, &primitive_type))) {
- if (err == ErrorOverflow) {
- add_node_error(ag->codegen, node,
- buf_sprintf("primitive integer type '%s' exceeds maximum bit width of 65535",
- buf_ptr(variable_name)));
- return ag->codegen->invalid_inst_src;
- }
- assert(err == ErrorPrimitiveTypeNotFound);
- } else {
- Stage1ZirInst *value = ir_build_const_type(ag, scope, node, primitive_type);
- if (lval == LValPtr || lval == LValAssign) {
- return ir_build_ref_src(ag, scope, node, value);
+ ZigType *primitive_type;
+ if ((err = get_primitive_type(ag->codegen, variable_name, &primitive_type))) {
+ if (err == ErrorOverflow) {
+ add_node_error(ag->codegen, node,
+ buf_sprintf("primitive integer type '%s' exceeds maximum bit width of 65535",
+ buf_ptr(variable_name)));
+ return ag->codegen->invalid_inst_src;
+ }
+ assert(err == ErrorPrimitiveTypeNotFound);
} else {
- return ir_expr_wrap(ag, scope, value, result_loc);
+ Stage1ZirInst *value = ir_build_const_type(ag, scope, node, primitive_type);
+ if (lval == LValPtr || lval == LValAssign) {
+ return ir_build_ref_src(ag, scope, node, value);
+ } else {
+ return ir_expr_wrap(ag, scope, value, result_loc);
+ }
}
}
@@ -3875,7 +3854,31 @@ static Stage1ZirInst *astgen_identifier(Stage1AstGen *ag, Scope *scope, AstNode
}
}
- Tld *tld = find_decl(ag->codegen, scope, variable_name);
+ Tld *tld = nullptr;
+ {
+ Scope *s = scope;
+ while (s) {
+ if (s->id == ScopeIdDecls) {
+ ScopeDecls *decls_scope = (ScopeDecls *)s;
+
+ Tld *result = find_container_decl(ag->codegen, decls_scope, variable_name);
+ if (result != nullptr) {
+ if (tld != nullptr && tld != result) {
+ ErrorMsg *msg = add_node_error(ag->codegen, node,
+ buf_sprintf("ambiguous reference"));
+ add_error_note(ag->codegen, msg, tld->source_node,
+ buf_sprintf("declared here"));
+ add_error_note(ag->codegen, msg, result->source_node,
+ buf_sprintf("also declared here"));
+ return ag->codegen->invalid_inst_src;
+ }
+ tld = result;
+ }
+ }
+ s = s->parent;
+ }
+ }
+
if (tld) {
Stage1ZirInst *decl_ref = ir_build_decl_ref(ag, scope, node, tld, lval);
if (lval == LValPtr || lval == LValAssign) {
@@ -4653,17 +4656,17 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
Stage1ZirInst *arg1_value = astgen_node(ag, arg1_node, scope);
- if (arg0_value == ag->codegen->invalid_inst_src)
+ if (arg1_value == ag->codegen->invalid_inst_src)
return arg1_value;
AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
Stage1ZirInst *arg2_value = astgen_node(ag, arg2_node, scope);
- if (arg1_value == ag->codegen->invalid_inst_src)
+ if (arg2_value == ag->codegen->invalid_inst_src)
return arg2_value;
AstNode *arg3_node = node->data.fn_call_expr.params.at(3);
Stage1ZirInst *arg3_value = astgen_node(ag, arg3_node, scope);
- if (arg2_value == ag->codegen->invalid_inst_src)
+ if (arg3_value == ag->codegen->invalid_inst_src)
return arg3_value;
Stage1ZirInst *select = ir_build_select(ag, scope, node,
diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp
index b1583cc6b4..830ce76708 100644
--- a/src/stage1/ir.cpp
+++ b/src/stage1/ir.cpp
@@ -20007,29 +20007,24 @@ static Stage1AirInst *ir_analyze_instruction_truncate(IrAnalyze *ira, Stage1ZirI
return ir_build_truncate_gen(ira, instruction->base.scope, instruction->base.source_node, dest_type, target);
}
-static Stage1AirInst *ir_analyze_instruction_int_cast(IrAnalyze *ira, Stage1ZirInstIntCast *instruction) {
- ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child);
- if (type_is_invalid(dest_type))
- return ira->codegen->invalid_inst_gen;
-
+static Stage1AirInst *ir_analyze_int_cast(IrAnalyze *ira, Scope *scope, AstNode *source_node,
+ ZigType *dest_type, AstNode *dest_type_src_node,
+ Stage1AirInst *target, AstNode *target_src_node)
+{
ZigType *scalar_dest_type = (dest_type->id == ZigTypeIdVector) ?
dest_type->data.vector.elem_type : dest_type;
if (scalar_dest_type->id != ZigTypeIdInt && scalar_dest_type->id != ZigTypeIdComptimeInt) {
- ir_add_error_node(ira, instruction->dest_type->source_node,
+ ir_add_error_node(ira, dest_type_src_node,
buf_sprintf("expected integer type, found '%s'", buf_ptr(&scalar_dest_type->name)));
return ira->codegen->invalid_inst_gen;
}
- Stage1AirInst *target = instruction->target->child;
- if (type_is_invalid(target->value->type))
- return ira->codegen->invalid_inst_gen;
-
ZigType *scalar_target_type = (target->value->type->id == ZigTypeIdVector) ?
target->value->type->data.vector.elem_type : target->value->type;
if (scalar_target_type->id != ZigTypeIdInt && scalar_target_type->id != ZigTypeIdComptimeInt) {
- ir_add_error_node(ira, instruction->target->source_node, buf_sprintf("expected integer type, found '%s'",
+ ir_add_error_node(ira, target_src_node, buf_sprintf("expected integer type, found '%s'",
buf_ptr(&scalar_target_type->name)));
return ira->codegen->invalid_inst_gen;
}
@@ -20039,10 +20034,24 @@ static Stage1AirInst *ir_analyze_instruction_int_cast(IrAnalyze *ira, Stage1ZirI
if (val == nullptr)
return ira->codegen->invalid_inst_gen;
- return ir_implicit_cast2(ira, instruction->target->scope, instruction->target->source_node, target, dest_type);
+ return ir_implicit_cast2(ira, scope, target_src_node, target, dest_type);
}
- return ir_analyze_widen_or_shorten(ira, instruction->base.scope, instruction->base.source_node, target, dest_type);
+ return ir_analyze_widen_or_shorten(ira, scope, source_node, target, dest_type);
+}
+
+static Stage1AirInst *ir_analyze_instruction_int_cast(IrAnalyze *ira, Stage1ZirInstIntCast *instruction) {
+ ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child);
+ if (type_is_invalid(dest_type))
+ return ira->codegen->invalid_inst_gen;
+
+ Stage1AirInst *target = instruction->target->child;
+ if (type_is_invalid(target->value->type))
+ return ira->codegen->invalid_inst_gen;
+
+ return ir_analyze_int_cast(ira, instruction->base.scope, instruction->base.source_node,
+ dest_type, instruction->dest_type->source_node,
+ target, instruction->target->source_node);
}
static Stage1AirInst *ir_analyze_instruction_float_cast(IrAnalyze *ira, Stage1ZirInstFloatCast *instruction) {
@@ -24282,7 +24291,9 @@ static Stage1AirInst *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, Stage1Z
if (type_is_invalid(target->value->type))
return ira->codegen->invalid_inst_gen;
- Stage1AirInst *casted_target = ir_implicit_cast(ira, target, tag_type);
+ Stage1AirInst *casted_target = ir_analyze_int_cast(ira, instruction->base.scope,
+ instruction->base.source_node, tag_type, instruction->dest_type->source_node,
+ target, instruction->target->source_node);
if (type_is_invalid(casted_target->value->type))
return ira->codegen->invalid_inst_gen;
diff --git a/src/stage1/parser.cpp b/src/stage1/parser.cpp
index 9a429364b1..b06a944172 100644
--- a/src/stage1/parser.cpp
+++ b/src/stage1/parser.cpp
@@ -3482,8 +3482,7 @@ Error source_char_literal(const char *source, uint32_t *result, size_t *bad_inde
}
}
-
-Buf *token_identifier_buf(RootStruct *root_struct, TokenIndex token) {
+static Buf *token_identifier_buf2(RootStruct *root_struct, TokenIndex token, bool *is_at_syntax) {
Error err;
const char *source = buf_ptr(root_struct->source_code);
size_t byte_offset = root_struct->token_locs[token].offset;
@@ -3495,6 +3494,7 @@ Buf *token_identifier_buf(RootStruct *root_struct, TokenIndex token) {
assert(source[byte_offset] != '.'); // wrong token index
if (source[byte_offset] == '@') {
+ *is_at_syntax = true;
size_t bad_index;
Buf *str = buf_alloc();
if ((err = source_string_literal_buf(source + byte_offset + 1, str, &bad_index))) {
@@ -3503,6 +3503,7 @@ Buf *token_identifier_buf(RootStruct *root_struct, TokenIndex token) {
}
return str;
} else {
+ *is_at_syntax = false;
size_t start = byte_offset;
for (;; byte_offset += 1) {
if (source[byte_offset] == 0) break;
@@ -3519,7 +3520,17 @@ Buf *token_identifier_buf(RootStruct *root_struct, TokenIndex token) {
}
}
+Buf *token_identifier_buf(RootStruct *root_struct, TokenIndex token) {
+ bool trash;
+ return token_identifier_buf2(root_struct, token, &trash);
+}
+
Buf *node_identifier_buf(AstNode *node) {
+ bool trash;
+ return node_identifier_buf2(node, &trash);
+}
+
+Buf *node_identifier_buf2(AstNode *node, bool *is_at_syntax) {
assert(node->type == NodeTypeIdentifier);
// Currently, stage1 runs astgen for every comptime function call,
// resulting the allocation here wasting memory. As a workaround until
@@ -3527,8 +3538,10 @@ Buf *node_identifier_buf(AstNode *node) {
// we memoize the result into the AST here.
if (node->data.identifier.name == nullptr) {
RootStruct *root_struct = node->owner->data.structure.root_struct;
- node->data.identifier.name = token_identifier_buf(root_struct, node->main_token);
+ node->data.identifier.name = token_identifier_buf2(root_struct, node->main_token,
+ &node->data.identifier.is_at_syntax);
}
+ *is_at_syntax = node->data.identifier.is_at_syntax;
return node->data.identifier.name;
}
diff --git a/src/stage1/parser.hpp b/src/stage1/parser.hpp
index 9f73444cb8..8ac8ce6de1 100644
--- a/src/stage1/parser.hpp
+++ b/src/stage1/parser.hpp
@@ -19,6 +19,7 @@ void ast_print(AstNode *node, int indent);
void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context);
Buf *node_identifier_buf(AstNode *node);
+Buf *node_identifier_buf2(AstNode *node, bool *is_at_syntax);
Buf *token_identifier_buf(RootStruct *root_struct, TokenIndex token);
diff --git a/src/stage1/target.cpp b/src/stage1/target.cpp
index 9c95be3d5a..85ea2f1d3d 100644
--- a/src/stage1/target.cpp
+++ b/src/stage1/target.cpp
@@ -925,7 +925,7 @@ bool target_has_valgrind_support(const ZigTarget *target) {
case ZigLLVM_UnknownArch:
zig_unreachable();
case ZigLLVM_x86_64:
- return (target->os == OsLinux || target_os_is_darwin(target->os) || target->os == OsSolaris ||
+ return (target->os == OsLinux || target->os == OsSolaris ||
(target->os == OsWindows && target->abi != ZigLLVM_MSVC));
default:
return false;
diff --git a/src/target.zig b/src/target.zig
index 0958f1d00f..6f50c8d5e9 100644
--- a/src/target.zig
+++ b/src/target.zig
@@ -166,7 +166,7 @@ pub fn isSingleThreaded(target: std.Target) bool {
pub fn hasValgrindSupport(target: std.Target) bool {
switch (target.cpu.arch) {
.x86_64 => {
- return target.os.tag == .linux or target.isDarwin() or target.os.tag == .solaris or
+ return target.os.tag == .linux or target.os.tag == .solaris or
(target.os.tag == .windows and target.abi != .msvc);
},
else => return false,
diff --git a/src/translate_c.zig b/src/translate_c.zig
index e11fc5b736..0cc40cdfd4 100644
--- a/src/translate_c.zig
+++ b/src/translate_c.zig
@@ -719,6 +719,30 @@ fn transQualTypeMaybeInitialized(c: *Context, scope: *Scope, qt: clang.QualType,
transQualType(c, scope, qt, loc);
}
+/// This is used in global scope to convert a string literal `S` to [*c]u8:
+/// &(struct {
+/// var static = S.*;
+/// }).static;
+fn stringLiteralToCharStar(c: *Context, str: Node) Error!Node {
+ const var_name = Scope.Block.StaticInnerName;
+
+ const variables = try c.arena.alloc(Node, 1);
+ variables[0] = try Tag.mut_str.create(c.arena, .{ .name = var_name, .init = str });
+
+ const anon_struct = try Tag.@"struct".create(c.arena, .{
+ .layout = .none,
+ .fields = &.{},
+ .functions = &.{},
+ .variables = variables,
+ });
+
+ const member_access = try Tag.field_access.create(c.arena, .{
+ .lhs = anon_struct,
+ .field_name = var_name,
+ });
+ return Tag.address_of.create(c.arena, member_access);
+}
+
/// if mangled_name is not null, this var decl was declared in a block scope.
fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]const u8) Error!void {
const var_name = mangled_name orelse try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin());
@@ -779,6 +803,8 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
};
if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node.?)) {
init_node = try Tag.bool_to_int.create(c.arena, init_node.?);
+ } else if (init_node.?.tag() == .string_literal and qualTypeIsCharStar(qual_type)) {
+ init_node = try stringLiteralToCharStar(c, init_node.?);
}
} else {
init_node = Tag.undefined_literal.init();
@@ -1101,9 +1127,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
record_payload.* = .{
.base = .{ .tag = ([2]Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] },
.data = .{
- .is_packed = is_packed,
+ .layout = if (is_packed) .@"packed" else .@"extern",
.fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items),
.functions = try c.arena.dupe(Node, functions.items),
+ .variables = &.{},
},
};
break :blk Node.initPayload(&record_payload.base);
@@ -1805,6 +1832,9 @@ fn transDeclStmtOne(
Tag.undefined_literal.init();
if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) {
init_node = try Tag.bool_to_int.create(c.arena, init_node);
+ } else if (init_node.tag() == .string_literal and qualTypeIsCharStar(qual_type)) {
+ const dst_type_node = try transQualType(c, scope, qual_type, loc);
+ init_node = try removeCVQualifiers(c, dst_type_node, init_node);
}
const var_name: []const u8 = if (is_static_local) Scope.Block.StaticInnerName else mangled_name;
@@ -2522,9 +2552,19 @@ fn transInitListExprRecord(
raw_name = try mem.dupe(c.arena, u8, name);
}
+ var init_expr = try transExpr(c, scope, elem_expr, .used);
+ const field_qt = field_decl.getType();
+ if (init_expr.tag() == .string_literal and qualTypeIsCharStar(field_qt)) {
+ if (scope.id == .root) {
+ init_expr = try stringLiteralToCharStar(c, init_expr);
+ } else {
+ const dst_type_node = try transQualType(c, scope, field_qt, loc);
+ init_expr = try removeCVQualifiers(c, dst_type_node, init_expr);
+ }
+ }
try field_inits.append(.{
.name = raw_name,
- .value = try transExpr(c, scope, elem_expr, .used),
+ .value = init_expr,
});
}
if (ty_node.castTag(.identifier)) |ident_node| {
@@ -3459,6 +3499,10 @@ fn transCallExpr(c: *Context, scope: *Scope, stmt: *const clang.CallExpr, result
const param_qt = fn_proto.getParamType(@intCast(c_uint, i));
if (isBoolRes(arg) and cIsNativeInt(param_qt)) {
arg = try Tag.bool_to_int.create(c.arena, arg);
+ } else if (arg.tag() == .string_literal and qualTypeIsCharStar(param_qt)) {
+ const loc = @ptrCast(*const clang.Stmt, stmt).getBeginLoc();
+ const dst_type_node = try transQualType(c, scope, param_qt, loc);
+ arg = try removeCVQualifiers(c, dst_type_node, arg);
}
}
},
@@ -3835,6 +3879,12 @@ fn transCreateCompoundAssign(
return block_scope.complete(c);
}
+// Casting away const or volatile requires us to use @intToPtr
+fn removeCVQualifiers(c: *Context, dst_type_node: Node, expr: Node) Error!Node {
+ const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr);
+ return Tag.int_to_ptr.create(c.arena, .{ .lhs = dst_type_node, .rhs = ptr_to_int });
+}
+
fn transCPtrCast(
c: *Context,
scope: *Scope,
@@ -3854,10 +3904,7 @@ fn transCPtrCast(
(src_child_type.isVolatileQualified() and
!child_type.isVolatileQualified())))
{
- // Casting away const or volatile requires us to use @intToPtr
- const ptr_to_int = try Tag.ptr_to_int.create(c.arena, expr);
- const int_to_ptr = try Tag.int_to_ptr.create(c.arena, .{ .lhs = dst_type_node, .rhs = ptr_to_int });
- return int_to_ptr;
+ return removeCVQualifiers(c, dst_type_node, expr);
} else {
// Implicit downcasting from higher to lower alignment values is forbidden,
// use @alignCast to side-step this problem
@@ -4217,6 +4264,26 @@ fn typeIsOpaque(c: *Context, ty: *const clang.Type, loc: clang.SourceLocation) b
}
}
+/// plain `char *` (not const; not explicitly signed or unsigned)
+fn qualTypeIsCharStar(qt: clang.QualType) bool {
+ if (qualTypeIsPtr(qt)) {
+ const child_qt = qualTypeCanon(qt).getPointeeType();
+ return cIsUnqualifiedChar(child_qt) and !child_qt.isConstQualified();
+ }
+ return false;
+}
+
+/// C `char` without explicit signed or unsigned qualifier
+fn cIsUnqualifiedChar(qt: clang.QualType) bool {
+ const c_type = qualTypeCanon(qt);
+ if (c_type.getTypeClass() != .Builtin) return false;
+ const builtin_ty = @ptrCast(*const clang.BuiltinType, c_type);
+ return switch (builtin_ty.getKind()) {
+ .Char_S, .Char_U => true,
+ else => false,
+ };
+}
+
fn cIsInteger(qt: clang.QualType) bool {
return cIsSignedInteger(qt) or cIsUnsignedInteger(qt);
}
diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig
index fa6b749589..1058936191 100644
--- a/src/translate_c/ast.zig
+++ b/src/translate_c/ast.zig
@@ -62,6 +62,8 @@ pub const Node = extern union {
var_decl,
/// const name = struct { init }
static_local_var,
+ /// var name = init.*
+ mut_str,
func,
warning,
@"struct",
@@ -361,7 +363,7 @@ pub const Node = extern union {
.array_type, .null_sentinel_array_type => Payload.Array,
.arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
.log2_int_type => Payload.Log2IntType,
- .var_simple, .pub_var_simple, .static_local_var => Payload.SimpleVarDecl,
+ .var_simple, .pub_var_simple, .static_local_var, .mut_str => Payload.SimpleVarDecl,
.enum_constant => Payload.EnumConstant,
.array_filler => Payload.ArrayFiller,
.pub_inline_fn => Payload.PubInlineFn,
@@ -558,9 +560,10 @@ pub const Payload = struct {
pub const Record = struct {
base: Payload,
data: struct {
- is_packed: bool,
+ layout: enum { @"packed", @"extern", none },
fields: []Field,
functions: []Node,
+ variables: []Node,
},
pub const Field = struct {
@@ -925,23 +928,23 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
return renderCall(c, lhs, payload.args);
},
.null_literal => return c.addNode(.{
- .tag = .null_literal,
- .main_token = try c.addToken(.keyword_null, "null"),
+ .tag = .identifier,
+ .main_token = try c.addToken(.identifier, "null"),
.data = undefined,
}),
.undefined_literal => return c.addNode(.{
- .tag = .undefined_literal,
- .main_token = try c.addToken(.keyword_undefined, "undefined"),
+ .tag = .identifier,
+ .main_token = try c.addToken(.identifier, "undefined"),
.data = undefined,
}),
.true_literal => return c.addNode(.{
- .tag = .true_literal,
- .main_token = try c.addToken(.keyword_true, "true"),
+ .tag = .identifier,
+ .main_token = try c.addToken(.identifier, "true"),
.data = undefined,
}),
.false_literal => return c.addNode(.{
- .tag = .false_literal,
- .main_token = try c.addToken(.keyword_false, "false"),
+ .tag = .identifier,
+ .main_token = try c.addToken(.identifier, "false"),
.data = undefined,
}),
.zero_literal => return c.addNode(.{
@@ -1229,6 +1232,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
},
});
_ = try c.addToken(.r_brace, "}");
+ _ = try c.addToken(.semicolon, ";");
return c.addNode(.{
.tag = .simple_var_decl,
@@ -1239,6 +1243,29 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
},
});
},
+ .mut_str => {
+ const payload = node.castTag(.mut_str).?.data;
+
+ const var_tok = try c.addToken(.keyword_var, "var");
+ _ = try c.addIdentifier(payload.name);
+ _ = try c.addToken(.equal, "=");
+
+ const deref = try c.addNode(.{
+ .tag = .deref,
+ .data = .{
+ .lhs = try renderNodeGrouped(c, payload.init),
+ .rhs = undefined,
+ },
+ .main_token = try c.addToken(.period_asterisk, ".*"),
+ });
+ _ = try c.addToken(.semicolon, ";");
+
+ return c.addNode(.{
+ .tag = .simple_var_decl,
+ .main_token = var_tok,
+ .data = .{ .lhs = 0, .rhs = deref },
+ });
+ },
.var_decl => return renderVar(c, node),
.arg_redecl, .alias => {
const payload = @fieldParentPtr(Payload.ArgRedecl, "base", node.ptr_otherwise).data;
@@ -1572,8 +1599,8 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
const while_tok = try c.addToken(.keyword_while, "while");
_ = try c.addToken(.l_paren, "(");
const cond = try c.addNode(.{
- .tag = .true_literal,
- .main_token = try c.addToken(.keyword_true, "true"),
+ .tag = .identifier,
+ .main_token = try c.addToken(.identifier, "true"),
.data = undefined,
});
_ = try c.addToken(.r_paren, ")");
@@ -1952,9 +1979,9 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
fn renderRecord(c: *Context, node: Node) !NodeIndex {
const payload = @fieldParentPtr(Payload.Record, "base", node.ptr_otherwise).data;
- if (payload.is_packed)
+ if (payload.layout == .@"packed")
_ = try c.addToken(.keyword_packed, "packed")
- else
+ else if (payload.layout == .@"extern")
_ = try c.addToken(.keyword_extern, "extern");
const kind_tok = if (node.tag() == .@"struct")
try c.addToken(.keyword_struct, "struct")
@@ -1963,8 +1990,9 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {
_ = try c.addToken(.l_brace, "{");
+ const num_vars = payload.variables.len;
const num_funcs = payload.functions.len;
- const total_members = payload.fields.len + num_funcs;
+ const total_members = payload.fields.len + num_vars + num_funcs;
const members = try c.gpa.alloc(NodeIndex, std.math.max(total_members, 2));
defer c.gpa.free(members);
members[0] = 0;
@@ -2006,8 +2034,11 @@ fn renderRecord(c: *Context, node: Node) !NodeIndex {
});
_ = try c.addToken(.comma, ",");
}
+ for (payload.variables) |variable, i| {
+ members[payload.fields.len + i] = try renderNode(c, variable);
+ }
for (payload.functions) |function, i| {
- members[payload.fields.len + i] = try renderNode(c, function);
+ members[payload.fields.len + num_vars + i] = try renderNode(c, function);
}
_ = try c.addToken(.r_brace, "}");
@@ -2140,7 +2171,7 @@ fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIn
fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
switch (node.tag()) {
.warning => unreachable,
- .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch" => {},
+ .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .static_local_var, .mut_str => {},
.while_true => {
const payload = node.castTag(.while_true).?.data;
return addSemicolonIfNotBlock(c, payload);
@@ -2235,6 +2266,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
.offset_of,
.shuffle,
.static_local_var,
+ .mut_str,
=> {
// no grouping needed
return renderNode(c, node);
diff --git a/src/type.zig b/src/type.zig
index 41f392c04a..467e9c931b 100644
--- a/src/type.zig
+++ b/src/type.zig
@@ -133,6 +133,7 @@ pub const Type = extern union {
.@"union",
.union_tagged,
+ .type_info,
=> return .Union,
.var_args_param => unreachable, // can be any type
@@ -248,6 +249,30 @@ pub const Type = extern union {
};
}
+ pub fn ptrIsMutable(ty: Type) bool {
+ return switch (ty.tag()) {
+ .single_const_pointer_to_comptime_int,
+ .const_slice_u8,
+ .single_const_pointer,
+ .many_const_pointer,
+ .manyptr_const_u8,
+ .c_const_pointer,
+ .const_slice,
+ => false,
+
+ .single_mut_pointer,
+ .many_mut_pointer,
+ .manyptr_u8,
+ .c_mut_pointer,
+ .mut_slice,
+ => true,
+
+ .pointer => ty.castTag(.pointer).?.data.mutable,
+
+ else => unreachable,
+ };
+ }
+
pub fn ptrInfo(self: Type) Payload.Pointer {
switch (self.tag()) {
.single_const_pointer_to_comptime_int => return .{ .data = .{
@@ -717,6 +742,7 @@ pub const Type = extern union {
.call_options,
.export_options,
.extern_options,
+ .type_info,
.@"anyframe",
.generic_poison,
=> unreachable,
@@ -928,6 +954,7 @@ pub const Type = extern union {
.call_options => return writer.writeAll("std.builtin.CallOptions"),
.export_options => return writer.writeAll("std.builtin.ExportOptions"),
.extern_options => return writer.writeAll("std.builtin.ExternOptions"),
+ .type_info => return writer.writeAll("std.builtin.TypeInfo"),
.function => {
const payload = ty.castTag(.function).?.data;
try writer.writeAll("fn(");
@@ -1178,6 +1205,7 @@ pub const Type = extern union {
.comptime_int,
.comptime_float,
.enum_literal,
+ .type_info,
=> true,
.var_args_param => unreachable,
@@ -1269,6 +1297,7 @@ pub const Type = extern union {
.call_options => return Value.initTag(.call_options_type),
.export_options => return Value.initTag(.export_options_type),
.extern_options => return Value.initTag(.extern_options_type),
+ .type_info => return Value.initTag(.type_info_type),
.inferred_alloc_const => unreachable,
.inferred_alloc_mut => unreachable,
else => return Value.Tag.ty.create(allocator, self),
@@ -1409,6 +1438,7 @@ pub const Type = extern union {
.empty_struct,
.empty_struct_literal,
.@"opaque",
+ .type_info,
=> false,
.inferred_alloc_const => unreachable,
@@ -1636,6 +1666,7 @@ pub const Type = extern union {
.inferred_alloc_mut,
.@"opaque",
.var_args_param,
+ .type_info,
=> unreachable,
.generic_poison => unreachable,
@@ -1667,6 +1698,7 @@ pub const Type = extern union {
.@"opaque" => unreachable,
.var_args_param => unreachable,
.generic_poison => unreachable,
+ .type_info => unreachable,
.@"struct" => {
const s = self.castTag(.@"struct").?.data;
@@ -1978,6 +2010,7 @@ pub const Type = extern union {
.call_options,
.export_options,
.extern_options,
+ .type_info,
=> @panic("TODO at some point we gotta resolve builtin types"),
};
}
@@ -2691,6 +2724,7 @@ pub const Type = extern union {
.call_options,
.export_options,
.extern_options,
+ .type_info,
.@"anyframe",
.anyframe_T,
.many_const_pointer,
@@ -2778,6 +2812,7 @@ pub const Type = extern union {
return switch (self.tag()) {
.@"struct" => &self.castTag(.@"struct").?.data.namespace,
.enum_full => &self.castTag(.enum_full).?.data.namespace,
+ .enum_nonexhaustive => &self.castTag(.enum_nonexhaustive).?.data.namespace,
.empty_struct => self.castTag(.empty_struct).?.data,
.@"opaque" => &self.castTag(.@"opaque").?.data,
.@"union" => &self.castTag(.@"union").?.data.namespace,
@@ -3022,6 +3057,7 @@ pub const Type = extern union {
.call_options,
.export_options,
.extern_options,
+ .type_info,
=> @panic("TODO resolve std.builtin types"),
else => unreachable,
}
@@ -3058,6 +3094,7 @@ pub const Type = extern union {
.call_options,
.export_options,
.extern_options,
+ .type_info,
=> @panic("TODO resolve std.builtin types"),
else => unreachable,
}
@@ -3167,6 +3204,7 @@ pub const Type = extern union {
call_options,
export_options,
extern_options,
+ type_info,
manyptr_u8,
manyptr_const_u8,
fn_noreturn_no_args,
@@ -3289,6 +3327,7 @@ pub const Type = extern union {
.call_options,
.export_options,
.extern_options,
+ .type_info,
.@"anyframe",
=> @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
diff --git a/src/value.zig b/src/value.zig
index 562d7171e8..5ac9f142c4 100644
--- a/src/value.zig
+++ b/src/value.zig
@@ -68,6 +68,7 @@ pub const Value = extern union {
call_options_type,
export_options_type,
extern_options_type,
+ type_info_type,
manyptr_u8_type,
manyptr_const_u8_type,
fn_noreturn_no_args_type,
@@ -132,12 +133,21 @@ pub const Value = extern union {
/// When the type is error union:
/// * If the tag is `.@"error"`, the error union is an error.
/// * If the tag is `.eu_payload`, the error union is a payload.
- /// * A nested error such as `((anyerror!T1)!T2)` in which the the outer error union
+ /// * A nested error such as `anyerror!(anyerror!T)` in which the the outer error union
/// is non-error, but the inner error union is an error, is represented as
/// a tag of `.eu_payload`, with a sub-tag of `.@"error"`.
eu_payload,
/// A pointer to the payload of an error union, based on a pointer to an error union.
eu_payload_ptr,
+ /// When the type is optional:
+ /// * If the tag is `.null_value`, the optional is null.
+ /// * If the tag is `.opt_payload`, the optional is a payload.
+ /// * A nested optional such as `??T` in which the the outer optional
+ /// is non-null, but the inner optional is null, is represented as
+ /// a tag of `.opt_payload`, with a sub-tag of `.null_value`.
+ opt_payload,
+ /// A pointer to the payload of an optional, based on a pointer to an optional.
+ opt_payload_ptr,
/// An instance of a struct.
@"struct",
/// An instance of a union.
@@ -221,6 +231,7 @@ pub const Value = extern union {
.call_options_type,
.export_options_type,
.extern_options_type,
+ .type_info_type,
.generic_poison,
=> @compileError("Value Tag " ++ @tagName(t) ++ " has no payload"),
@@ -236,6 +247,8 @@ pub const Value = extern union {
.repeated,
.eu_payload,
.eu_payload_ptr,
+ .opt_payload,
+ .opt_payload_ptr,
=> Payload.SubValue,
.bytes,
@@ -402,6 +415,7 @@ pub const Value = extern union {
.call_options_type,
.export_options_type,
.extern_options_type,
+ .type_info_type,
.generic_poison,
=> unreachable,
@@ -456,7 +470,12 @@ pub const Value = extern union {
return Value{ .ptr_otherwise = &new_payload.base };
},
.bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
- .repeated, .eu_payload, .eu_payload_ptr => {
+ .repeated,
+ .eu_payload,
+ .eu_payload_ptr,
+ .opt_payload,
+ .opt_payload_ptr,
+ => {
const payload = self.cast(Payload.SubValue).?;
const new_payload = try allocator.create(Payload.SubValue);
new_payload.* = .{
@@ -585,6 +604,7 @@ pub const Value = extern union {
.call_options_type => return out_stream.writeAll("std.builtin.CallOptions"),
.export_options_type => return out_stream.writeAll("std.builtin.ExportOptions"),
.extern_options_type => return out_stream.writeAll("std.builtin.ExternOptions"),
+ .type_info_type => return out_stream.writeAll("std.builtin.TypeInfo"),
.abi_align_default => return out_stream.writeAll("(default ABI alignment)"),
.empty_struct_value => return out_stream.writeAll("struct {}{}"),
@@ -652,12 +672,20 @@ pub const Value = extern union {
try out_stream.writeAll("(eu_payload) ");
val = val.castTag(.eu_payload).?.data;
},
+ .opt_payload => {
+ try out_stream.writeAll("(opt_payload) ");
+ val = val.castTag(.opt_payload).?.data;
+ },
.inferred_alloc => return out_stream.writeAll("(inferred allocation value)"),
.inferred_alloc_comptime => return out_stream.writeAll("(inferred comptime allocation value)"),
.eu_payload_ptr => {
try out_stream.writeAll("(eu_payload_ptr)");
val = val.castTag(.eu_payload_ptr).?.data;
},
+ .opt_payload_ptr => {
+ try out_stream.writeAll("(opt_payload_ptr)");
+ val = val.castTag(.opt_payload_ptr).?.data;
+ },
};
}
@@ -743,6 +771,7 @@ pub const Value = extern union {
.call_options_type => Type.initTag(.call_options),
.export_options_type => Type.initTag(.export_options),
.extern_options_type => Type.initTag(.extern_options),
+ .type_info_type => Type.initTag(.type_info),
.int_type => {
const payload = self.castTag(.int_type).?.data;
@@ -771,6 +800,38 @@ pub const Value = extern union {
}
}
+ pub fn enumToInt(val: Value, ty: Type, buffer: *Payload.U64) Value {
+ if (val.castTag(.enum_field_index)) |enum_field_payload| {
+ const field_index = enum_field_payload.data;
+ switch (ty.tag()) {
+ .enum_full, .enum_nonexhaustive => {
+ const enum_full = ty.cast(Type.Payload.EnumFull).?.data;
+ if (enum_full.values.count() != 0) {
+ return enum_full.values.keys()[field_index];
+ } else {
+ // Field index and integer values are the same.
+ buffer.* = .{
+ .base = .{ .tag = .int_u64 },
+ .data = field_index,
+ };
+ return Value.initPayload(&buffer.base);
+ }
+ },
+ .enum_simple => {
+ // Field index and integer values are the same.
+ buffer.* = .{
+ .base = .{ .tag = .int_u64 },
+ .data = field_index,
+ };
+ return Value.initPayload(&buffer.base);
+ },
+ else => unreachable,
+ }
+ }
+ // Assume it is already an integer and return it directly.
+ return val;
+ }
+
/// Asserts the value is an integer.
pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst {
switch (self.tag()) {
@@ -1127,7 +1188,10 @@ pub const Value = extern union {
}
pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash) void {
- switch (ty.zigTypeTag()) {
+ const zig_ty_tag = ty.zigTypeTag();
+ std.hash.autoHash(hasher, zig_ty_tag);
+
+ switch (zig_ty_tag) {
.BoundFn => unreachable, // TODO remove this from the language
.Void,
@@ -1152,7 +1216,10 @@ pub const Value = extern union {
}
},
.Float, .ComptimeFloat => {
- @panic("TODO implement hashing float values");
+ // TODO double check the lang spec. should we to bitwise hashing here,
+ // or a hash that normalizes the float value?
+ const float = val.toFloat(f128);
+ std.hash.autoHash(hasher, @bitCast(u128, float));
},
.Pointer => {
@panic("TODO implement hashing pointer values");
@@ -1164,7 +1231,15 @@ pub const Value = extern union {
@panic("TODO implement hashing struct values");
},
.Optional => {
- @panic("TODO implement hashing optional values");
+ if (val.castTag(.opt_payload)) |payload| {
+ std.hash.autoHash(hasher, true); // non-null
+ const sub_val = payload.data;
+ var buffer: Type.Payload.ElemType = undefined;
+ const sub_ty = ty.optionalChild(&buffer);
+ sub_val.hash(sub_ty, hasher);
+ } else {
+ std.hash.autoHash(hasher, false); // non-null
+ }
},
.ErrorUnion => {
@panic("TODO implement hashing error union values");
@@ -1173,7 +1248,16 @@ pub const Value = extern union {
@panic("TODO implement hashing error set values");
},
.Enum => {
- @panic("TODO implement hashing enum values");
+ var enum_space: Payload.U64 = undefined;
+ const int_val = val.enumToInt(ty, &enum_space);
+
+ var space: BigIntSpace = undefined;
+ const big = int_val.toBigInt(&space);
+
+ std.hash.autoHash(hasher, big.positive);
+ for (big.limbs) |limb| {
+ std.hash.autoHash(hasher, limb);
+ }
},
.Union => {
@panic("TODO implement hashing union values");
@@ -1252,6 +1336,11 @@ pub const Value = extern union {
const err_union_val = (try err_union_ptr.pointerDeref(allocator)) orelse return null;
break :blk err_union_val.castTag(.eu_payload).?.data;
},
+ .opt_payload_ptr => blk: {
+ const opt_ptr = self.castTag(.opt_payload_ptr).?.data;
+ const opt_val = (try opt_ptr.pointerDeref(allocator)) orelse return null;
+ break :blk opt_val.castTag(.opt_payload).?.data;
+ },
.zero,
.one,
@@ -1349,13 +1438,14 @@ 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()) {
+ .null_value => true,
+ .opt_payload => false,
+
.undef => unreachable,
.unreachable_value => unreachable,
.inferred_alloc => unreachable,
.inferred_alloc_comptime => unreachable,
- .null_value => true,
-
- else => false,
+ else => unreachable,
};
}
@@ -1385,6 +1475,10 @@ pub const Value = extern union {
return switch (val.tag()) {
.eu_payload => true,
else => false,
+
+ .undef => unreachable,
+ .inferred_alloc => unreachable,
+ .inferred_alloc_comptime => unreachable,
};
}
@@ -1514,6 +1608,31 @@ pub const Value = extern union {
return Tag.int_u64.create(arena, truncated);
}
+ pub fn shr(lhs: Value, rhs: Value, allocator: *Allocator) !Value {
+ // TODO is this a performance issue? maybe we should try the operation without
+ // resorting to BigInt first.
+ var lhs_space: Value.BigIntSpace = undefined;
+ const lhs_bigint = lhs.toBigInt(&lhs_space);
+ const shift = rhs.toUnsignedInt();
+ const limbs = try allocator.alloc(
+ std.math.big.Limb,
+ lhs_bigint.limbs.len - (shift / (@sizeOf(std.math.big.Limb) * 8)),
+ );
+ var result_bigint = BigIntMutable{
+ .limbs = limbs,
+ .positive = undefined,
+ .len = undefined,
+ };
+ result_bigint.shiftRight(lhs_bigint, shift);
+ const result_limbs = result_bigint.limbs[0..result_bigint.len];
+
+ if (result_bigint.positive) {
+ return Value.Tag.int_big_positive.create(allocator, result_limbs);
+ } else {
+ return Value.Tag.int_big_negative.create(allocator, result_limbs);
+ }
+ }
+
pub fn floatAdd(
lhs: Value,
rhs: Value,