mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
stage2: field type expressions support referencing locals
The big change in this commit is making `semaDecl` resolve the fields if
the Decl ends up being a struct or union. It needs to do this while
the `Sema` is still in scope, because it will have the resolved AIR
instructions that the field type expressions possibly reference. We do
this after the decl is populated and set to `complete` so that a `Decl`
may reference itself.
Everything else is fixes and improvements to make the test suite pass
again after making this change.
* New AIR instruction: `ptr_elem_ptr`
- Implemented for LLVM backend
* New Type tag: `type_info` which represents `std.builtin.TypeInfo`. It
is used by AstGen for the operand type of `@Type`.
* ZIR instruction `set_float_mode` uses `coerced_ty` to avoid
superfluous `as` instruction on operand.
* ZIR instruction `Type` uses `coerced_ty` to properly handle result
location type of operand.
* Fix two instances of `enum_nonexhaustive` Value Tag not handled
properly - it should generally be handled the same as `enum_full`.
* Fix struct and union field resolution not copying Type and Value
objects into its Decl arena.
* Fix enum tag value resolution discarding the ZIR=>AIR instruction map
for the child Sema, when they still needed to be accessed.
* Fix `zirResolveInferredAlloc` use-after-free in the AIR instructions
data array.
* Fix `elemPtrArray` not respecting const/mutable attribute of pointer
in the result type.
* Fix LLVM backend crashing when `updateDeclExports` is called before
`updateDecl`/`updateFunc` (which is, according to the API, perfectly
legal for the frontend to do).
* Fix LLVM backend handling element pointer of pointer-to-array. It
needed another index in the GEP otherwise LLVM saw the wrong type.
* Fix LLVM test cases not returning 0 from main, causing test failures.
Fixes a regression introduced in
6a5094872f10acc629543cc7f10533b438d0283a.
* Implement comptime shift-right.
* Implement `@Type` for integers and `@TypeInfo` for integers.
* Implement union initialization syntax.
* Implement `zirFieldType` for unions.
* Implement `elemPtrArray` for a runtime-known operand.
* Make `zirLog2IntType` support RHS of shift being `comptime_int`. In
this case it returns `comptime_int`.
The motivating test case for this commit was originally:
```zig
test "example" {
var l: List(10) = undefined;
l.array[1] = 1;
}
fn List(comptime L: usize) type {
var T = u8;
return struct {
array: [L]T,
};
}
```
However I changed it to:
```zig
test "example" {
var l: List = undefined;
l.array[1] = 1;
}
const List = blk: {
const T = [10]u8;
break :blk struct {
array: T,
};
};
```
Which ended up being a similar, smaller problem. The former test case
will require a similar solution in the implementation of comptime
function calls - checking if the result of the function call is a struct
or union, and using the child `Sema` before it is destroyed to resolve
the fields.
This commit is contained in:
parent
2f1abd919a
commit
0cd361219c
@ -237,7 +237,7 @@ pub const TypeInfo = union(enum) {
|
||||
/// This field is an optional type.
|
||||
/// The type of the sentinel is the element type of the pointer, which is
|
||||
/// the value of the `child` field in this struct. However there is no way
|
||||
/// to refer to that type here, so we use `var`.
|
||||
/// to refer to that type here, so we use `anytype`.
|
||||
sentinel: anytype,
|
||||
|
||||
/// This data structure is used by the Zig language code generation and
|
||||
|
||||
14
src/Air.zig
14
src/Air.zig
@ -286,6 +286,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.
|
||||
@ -410,6 +414,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
|
||||
@ -482,6 +491,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,
|
||||
@ -527,8 +537,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);
|
||||
|
||||
@ -7102,38 +7102,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),
|
||||
|
||||
@ -330,6 +330,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 });
|
||||
|
||||
320
src/Module.zig
320
src/Module.zig
@ -554,8 +554,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 +660,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`.
|
||||
@ -3091,6 +3092,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 +3197,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) {
|
||||
@ -4450,309 +4463,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 {
|
||||
|
||||
731
src/Sema.zig
731
src/Sema.zig
@ -1032,25 +1032,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 +1061,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;
|
||||
@ -1466,8 +1465,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 +1479,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, .{
|
||||
@ -5329,10 +5328,16 @@ fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A
|
||||
|
||||
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(sema.typeOf(lhs));
|
||||
return sema.addConstUndef(lhs_ty);
|
||||
}
|
||||
return sema.mod.fail(&block.base, src, "TODO implement comptime shr", .{});
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6008,6 +6013,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),
|
||||
}),
|
||||
@ -6047,20 +6074,24 @@ fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compil
|
||||
}
|
||||
|
||||
fn log2IntType(sema: *Sema, block: *Scope.Block, operand: Type, src: LazySrcLoc) CompileError!Air.Inst.Ref {
|
||||
if (operand.zigTypeTag() != .Int) return sema.mod.fail(
|
||||
&block.base,
|
||||
src,
|
||||
"bit shifting operation expected integer type, found '{}'",
|
||||
.{operand},
|
||||
);
|
||||
|
||||
var count: u16 = 0;
|
||||
var s = operand.bitSize(sema.mod.getTarget()) - 1;
|
||||
while (s != 0) : (s >>= 1) {
|
||||
count += 1;
|
||||
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},
|
||||
),
|
||||
}
|
||||
const res = try Module.makeIntType(sema.arena, .unsigned, count);
|
||||
return sema.addType(res);
|
||||
}
|
||||
|
||||
fn zirTypeofPeer(
|
||||
@ -6517,99 +6548,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;
|
||||
const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type);
|
||||
|
||||
// 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);
|
||||
if (resolved_ty.castTag(.@"struct")) |struct_payload| {
|
||||
const struct_obj = struct_payload.data;
|
||||
|
||||
// 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);
|
||||
// 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);
|
||||
|
||||
var field_i: u32 = 0;
|
||||
var extra_index = extra.end;
|
||||
// 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);
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
var root_msg: ?*Module.ErrorMsg = null;
|
||||
|
||||
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);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
|
||||
if (is_ref) {
|
||||
return mod.fail(&block.base, src, "TODO: Sema.zirStructInit is_ref=true", .{});
|
||||
}
|
||||
|
||||
const is_comptime = for (field_inits) |field_init| {
|
||||
if (!(try sema.isComptimeKnown(block, src, field_init))) {
|
||||
break false;
|
||||
}
|
||||
} 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));
|
||||
}
|
||||
|
||||
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 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 = 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);
|
||||
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", .{});
|
||||
}
|
||||
found_fields[field_index] = item.data.field_type;
|
||||
field_inits[field_index] = sema.resolveInst(item.data.init);
|
||||
}
|
||||
|
||||
var root_msg: ?*Module.ErrorMsg = null;
|
||||
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
field_inits[i] = try sema.addConstant(field.ty, field.default_val);
|
||||
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 mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known union values", .{});
|
||||
}
|
||||
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", .{});
|
||||
}
|
||||
|
||||
const is_comptime = for (field_inits) |field_init| {
|
||||
if (!(try sema.isComptimeKnown(block, src, field_init))) {
|
||||
break false;
|
||||
}
|
||||
} 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(struct_ty, try Value.Tag.@"struct".create(sema.arena, values.ptr));
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -6647,17 +6713,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(
|
||||
@ -6732,7 +6806,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 {
|
||||
@ -8152,24 +8273,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(
|
||||
@ -9177,22 +9309,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,
|
||||
}
|
||||
const prev_namespace = sema.namespace;
|
||||
sema.namespace = &struct_obj.namespace;
|
||||
defer sema.namespace = prev_namespace;
|
||||
|
||||
struct_obj.status = .field_types_wip;
|
||||
try sema.analyzeStructFields(block, struct_obj);
|
||||
struct_obj.status = .have_field_types;
|
||||
},
|
||||
.@"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,
|
||||
}
|
||||
struct_obj.status = .field_types_wip;
|
||||
try sema.mod.analyzeStructFields(struct_obj);
|
||||
struct_obj.status = .have_field_types;
|
||||
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"),
|
||||
@ -9205,18 +9377,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,
|
||||
}
|
||||
@ -9232,6 +9398,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,
|
||||
@ -9344,6 +9769,7 @@ fn typeHasOnePossibleValue(
|
||||
.call_options,
|
||||
.export_options,
|
||||
.extern_options,
|
||||
.type_info,
|
||||
.@"anyframe",
|
||||
.anyframe_T,
|
||||
.many_const_pointer,
|
||||
@ -9528,6 +9954,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,
|
||||
|
||||
13
src/Zir.zig
13
src/Zir.zig
@ -687,14 +687,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 +1703,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 +1974,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"),
|
||||
|
||||
@ -862,6 +862,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
|
||||
@ -1419,6 +1420,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;
|
||||
|
||||
@ -913,6 +913,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM
|
||||
|
||||
.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]["),
|
||||
|
||||
@ -960,6 +961,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))
|
||||
|
||||
@ -432,6 +432,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(
|
||||
@ -440,7 +442,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);
|
||||
@ -1041,6 +1045,7 @@ pub const FuncGen = struct {
|
||||
.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),
|
||||
@ -1296,11 +1301,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))
|
||||
|
||||
@ -175,6 +175,7 @@ const Writer = struct {
|
||||
.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),
|
||||
@ -239,10 +240,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 {
|
||||
|
||||
39
src/type.zig
39
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"),
|
||||
|
||||
|
||||
@ -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,
|
||||
@ -221,6 +222,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"),
|
||||
|
||||
@ -402,6 +404,7 @@ pub const Value = extern union {
|
||||
.call_options_type,
|
||||
.export_options_type,
|
||||
.extern_options_type,
|
||||
.type_info_type,
|
||||
.generic_poison,
|
||||
=> unreachable,
|
||||
|
||||
@ -585,6 +588,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 {}{}"),
|
||||
@ -743,6 +747,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;
|
||||
@ -1514,6 +1519,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,
|
||||
|
||||
@ -9,12 +9,13 @@ test {
|
||||
_ = @import("behavior/pointers.zig");
|
||||
_ = @import("behavior/if.zig");
|
||||
_ = @import("behavior/cast.zig");
|
||||
_ = @import("behavior/array.zig");
|
||||
|
||||
if (!builtin.zig_is_stage2) {
|
||||
// Tests that only pass for stage1.
|
||||
_ = @import("behavior/align.zig");
|
||||
_ = @import("behavior/alignof.zig");
|
||||
_ = @import("behavior/array.zig");
|
||||
_ = @import("behavior/array_stage1.zig");
|
||||
if (builtin.os.tag != .wasi) {
|
||||
_ = @import("behavior/asm.zig");
|
||||
_ = @import("behavior/async_fn.zig");
|
||||
|
||||
@ -3,487 +3,3 @@ const testing = std.testing;
|
||||
const mem = std.mem;
|
||||
const expect = testing.expect;
|
||||
const expectEqual = testing.expectEqual;
|
||||
|
||||
test "arrays" {
|
||||
var array: [5]u32 = undefined;
|
||||
|
||||
var i: u32 = 0;
|
||||
while (i < 5) {
|
||||
array[i] = i + 1;
|
||||
i = array[i];
|
||||
}
|
||||
|
||||
i = 0;
|
||||
var accumulator = @as(u32, 0);
|
||||
while (i < 5) {
|
||||
accumulator += array[i];
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
try expect(accumulator == 15);
|
||||
try expect(getArrayLen(&array) == 5);
|
||||
}
|
||||
fn getArrayLen(a: []const u32) usize {
|
||||
return a.len;
|
||||
}
|
||||
|
||||
test "array with sentinels" {
|
||||
const S = struct {
|
||||
fn doTheTest(is_ct: bool) !void {
|
||||
if (is_ct) {
|
||||
var zero_sized: [0:0xde]u8 = [_:0xde]u8{};
|
||||
// Disabled at runtime because of
|
||||
// https://github.com/ziglang/zig/issues/4372
|
||||
try expectEqual(@as(u8, 0xde), zero_sized[0]);
|
||||
var reinterpreted = @ptrCast(*[1]u8, &zero_sized);
|
||||
try expectEqual(@as(u8, 0xde), reinterpreted[0]);
|
||||
}
|
||||
var arr: [3:0x55]u8 = undefined;
|
||||
// Make sure the sentinel pointer is pointing after the last element
|
||||
if (!is_ct) {
|
||||
const sentinel_ptr = @ptrToInt(&arr[3]);
|
||||
const last_elem_ptr = @ptrToInt(&arr[2]);
|
||||
try expectEqual(@as(usize, 1), sentinel_ptr - last_elem_ptr);
|
||||
}
|
||||
// Make sure the sentinel is writeable
|
||||
arr[3] = 0x55;
|
||||
}
|
||||
};
|
||||
|
||||
try S.doTheTest(false);
|
||||
comptime try S.doTheTest(true);
|
||||
}
|
||||
|
||||
test "void arrays" {
|
||||
var array: [4]void = undefined;
|
||||
array[0] = void{};
|
||||
array[1] = array[2];
|
||||
try expect(@sizeOf(@TypeOf(array)) == 0);
|
||||
try expect(array.len == 4);
|
||||
}
|
||||
|
||||
test "array literal" {
|
||||
const hex_mult = [_]u16{
|
||||
4096,
|
||||
256,
|
||||
16,
|
||||
1,
|
||||
};
|
||||
|
||||
try expect(hex_mult.len == 4);
|
||||
try expect(hex_mult[1] == 256);
|
||||
}
|
||||
|
||||
test "array dot len const expr" {
|
||||
try expect(comptime x: {
|
||||
break :x some_array.len == 4;
|
||||
});
|
||||
}
|
||||
|
||||
const ArrayDotLenConstExpr = struct {
|
||||
y: [some_array.len]u8,
|
||||
};
|
||||
const some_array = [_]u8{
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
};
|
||||
|
||||
test "nested arrays" {
|
||||
const array_of_strings = [_][]const u8{
|
||||
"hello",
|
||||
"this",
|
||||
"is",
|
||||
"my",
|
||||
"thing",
|
||||
};
|
||||
for (array_of_strings) |s, i| {
|
||||
if (i == 0) try expect(mem.eql(u8, s, "hello"));
|
||||
if (i == 1) try expect(mem.eql(u8, s, "this"));
|
||||
if (i == 2) try expect(mem.eql(u8, s, "is"));
|
||||
if (i == 3) try expect(mem.eql(u8, s, "my"));
|
||||
if (i == 4) try expect(mem.eql(u8, s, "thing"));
|
||||
}
|
||||
}
|
||||
|
||||
var s_array: [8]Sub = undefined;
|
||||
const Sub = struct {
|
||||
b: u8,
|
||||
};
|
||||
const Str = struct {
|
||||
a: []Sub,
|
||||
};
|
||||
test "set global var array via slice embedded in struct" {
|
||||
var s = Str{ .a = s_array[0..] };
|
||||
|
||||
s.a[0].b = 1;
|
||||
s.a[1].b = 2;
|
||||
s.a[2].b = 3;
|
||||
|
||||
try expect(s_array[0].b == 1);
|
||||
try expect(s_array[1].b == 2);
|
||||
try expect(s_array[2].b == 3);
|
||||
}
|
||||
|
||||
test "array literal with specified size" {
|
||||
var array = [2]u8{
|
||||
1,
|
||||
2,
|
||||
};
|
||||
try expect(array[0] == 1);
|
||||
try expect(array[1] == 2);
|
||||
}
|
||||
|
||||
test "array len field" {
|
||||
var arr = [4]u8{ 0, 0, 0, 0 };
|
||||
var ptr = &arr;
|
||||
try expect(arr.len == 4);
|
||||
comptime try expect(arr.len == 4);
|
||||
try expect(ptr.len == 4);
|
||||
comptime try expect(ptr.len == 4);
|
||||
}
|
||||
|
||||
test "single-item pointer to array indexing and slicing" {
|
||||
try testSingleItemPtrArrayIndexSlice();
|
||||
comptime try testSingleItemPtrArrayIndexSlice();
|
||||
}
|
||||
|
||||
fn testSingleItemPtrArrayIndexSlice() !void {
|
||||
{
|
||||
var array: [4]u8 = "aaaa".*;
|
||||
doSomeMangling(&array);
|
||||
try expect(mem.eql(u8, "azya", &array));
|
||||
}
|
||||
{
|
||||
var array = "aaaa".*;
|
||||
doSomeMangling(&array);
|
||||
try expect(mem.eql(u8, "azya", &array));
|
||||
}
|
||||
}
|
||||
|
||||
fn doSomeMangling(array: *[4]u8) void {
|
||||
array[1] = 'z';
|
||||
array[2..3][0] = 'y';
|
||||
}
|
||||
|
||||
test "implicit cast single-item pointer" {
|
||||
try testImplicitCastSingleItemPtr();
|
||||
comptime try testImplicitCastSingleItemPtr();
|
||||
}
|
||||
|
||||
fn testImplicitCastSingleItemPtr() !void {
|
||||
var byte: u8 = 100;
|
||||
const slice = @as(*[1]u8, &byte)[0..];
|
||||
slice[0] += 1;
|
||||
try expect(byte == 101);
|
||||
}
|
||||
|
||||
fn testArrayByValAtComptime(b: [2]u8) u8 {
|
||||
return b[0];
|
||||
}
|
||||
|
||||
test "comptime evalutating function that takes array by value" {
|
||||
const arr = [_]u8{ 0, 1 };
|
||||
_ = comptime testArrayByValAtComptime(arr);
|
||||
_ = comptime testArrayByValAtComptime(arr);
|
||||
}
|
||||
|
||||
test "implicit comptime in array type size" {
|
||||
var arr: [plusOne(10)]bool = undefined;
|
||||
try expect(arr.len == 11);
|
||||
}
|
||||
|
||||
fn plusOne(x: u32) u32 {
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
test "runtime initialize array elem and then implicit cast to slice" {
|
||||
var two: i32 = 2;
|
||||
const x: []const i32 = &[_]i32{two};
|
||||
try expect(x[0] == 2);
|
||||
}
|
||||
|
||||
test "array literal as argument to function" {
|
||||
const S = struct {
|
||||
fn entry(two: i32) !void {
|
||||
try foo(&[_]i32{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
});
|
||||
try foo(&[_]i32{
|
||||
1,
|
||||
two,
|
||||
3,
|
||||
});
|
||||
try foo2(true, &[_]i32{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
});
|
||||
try foo2(true, &[_]i32{
|
||||
1,
|
||||
two,
|
||||
3,
|
||||
});
|
||||
}
|
||||
fn foo(x: []const i32) !void {
|
||||
try expect(x[0] == 1);
|
||||
try expect(x[1] == 2);
|
||||
try expect(x[2] == 3);
|
||||
}
|
||||
fn foo2(trash: bool, x: []const i32) !void {
|
||||
try expect(trash);
|
||||
try expect(x[0] == 1);
|
||||
try expect(x[1] == 2);
|
||||
try expect(x[2] == 3);
|
||||
}
|
||||
};
|
||||
try S.entry(2);
|
||||
comptime try S.entry(2);
|
||||
}
|
||||
|
||||
test "double nested array to const slice cast in array literal" {
|
||||
const S = struct {
|
||||
fn entry(two: i32) !void {
|
||||
const cases = [_][]const []const i32{
|
||||
&[_][]const i32{&[_]i32{1}},
|
||||
&[_][]const i32{&[_]i32{ 2, 3 }},
|
||||
&[_][]const i32{
|
||||
&[_]i32{4},
|
||||
&[_]i32{ 5, 6, 7 },
|
||||
},
|
||||
};
|
||||
try check(&cases);
|
||||
|
||||
const cases2 = [_][]const i32{
|
||||
&[_]i32{1},
|
||||
&[_]i32{ two, 3 },
|
||||
};
|
||||
try expect(cases2.len == 2);
|
||||
try expect(cases2[0].len == 1);
|
||||
try expect(cases2[0][0] == 1);
|
||||
try expect(cases2[1].len == 2);
|
||||
try expect(cases2[1][0] == 2);
|
||||
try expect(cases2[1][1] == 3);
|
||||
|
||||
const cases3 = [_][]const []const i32{
|
||||
&[_][]const i32{&[_]i32{1}},
|
||||
&[_][]const i32{&[_]i32{ two, 3 }},
|
||||
&[_][]const i32{
|
||||
&[_]i32{4},
|
||||
&[_]i32{ 5, 6, 7 },
|
||||
},
|
||||
};
|
||||
try check(&cases3);
|
||||
}
|
||||
|
||||
fn check(cases: []const []const []const i32) !void {
|
||||
try expect(cases.len == 3);
|
||||
try expect(cases[0].len == 1);
|
||||
try expect(cases[0][0].len == 1);
|
||||
try expect(cases[0][0][0] == 1);
|
||||
try expect(cases[1].len == 1);
|
||||
try expect(cases[1][0].len == 2);
|
||||
try expect(cases[1][0][0] == 2);
|
||||
try expect(cases[1][0][1] == 3);
|
||||
try expect(cases[2].len == 2);
|
||||
try expect(cases[2][0].len == 1);
|
||||
try expect(cases[2][0][0] == 4);
|
||||
try expect(cases[2][1].len == 3);
|
||||
try expect(cases[2][1][0] == 5);
|
||||
try expect(cases[2][1][1] == 6);
|
||||
try expect(cases[2][1][2] == 7);
|
||||
}
|
||||
};
|
||||
try S.entry(2);
|
||||
comptime try S.entry(2);
|
||||
}
|
||||
|
||||
test "read/write through global variable array of struct fields initialized via array mult" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try expect(storage[0].term == 1);
|
||||
storage[0] = MyStruct{ .term = 123 };
|
||||
try expect(storage[0].term == 123);
|
||||
}
|
||||
|
||||
pub const MyStruct = struct {
|
||||
term: usize,
|
||||
};
|
||||
|
||||
var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1;
|
||||
};
|
||||
try S.doTheTest();
|
||||
}
|
||||
|
||||
test "implicit cast zero sized array ptr to slice" {
|
||||
{
|
||||
var b = "".*;
|
||||
const c: []const u8 = &b;
|
||||
try expect(c.len == 0);
|
||||
}
|
||||
{
|
||||
var b: [0]u8 = "".*;
|
||||
const c: []const u8 = &b;
|
||||
try expect(c.len == 0);
|
||||
}
|
||||
}
|
||||
|
||||
test "anonymous list literal syntax" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array: [4]u8 = .{ 1, 2, 3, 4 };
|
||||
try expect(array[0] == 1);
|
||||
try expect(array[1] == 2);
|
||||
try expect(array[2] == 3);
|
||||
try expect(array[3] == 4);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "anonymous literal in array" {
|
||||
const S = struct {
|
||||
const Foo = struct {
|
||||
a: usize = 2,
|
||||
b: usize = 4,
|
||||
};
|
||||
fn doTheTest() !void {
|
||||
var array: [2]Foo = .{
|
||||
.{ .a = 3 },
|
||||
.{ .b = 3 },
|
||||
};
|
||||
try expect(array[0].a == 3);
|
||||
try expect(array[0].b == 4);
|
||||
try expect(array[1].a == 2);
|
||||
try expect(array[1].b == 3);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "access the null element of a null terminated array" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' };
|
||||
try expect(array[4] == 0);
|
||||
var len: usize = 4;
|
||||
try expect(array[len] == 0);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "type deduction for array subscript expression" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array = [_]u8{ 0x55, 0xAA };
|
||||
var v0 = true;
|
||||
try expectEqual(@as(u8, 0xAA), array[if (v0) 1 else 0]);
|
||||
var v1 = false;
|
||||
try expectEqual(@as(u8, 0x55), array[if (v1) 1 else 0]);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "sentinel element count towards the ABI size calculation" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
const T = packed struct {
|
||||
fill_pre: u8 = 0x55,
|
||||
data: [0:0]u8 = undefined,
|
||||
fill_post: u8 = 0xAA,
|
||||
};
|
||||
var x = T{};
|
||||
var as_slice = mem.asBytes(&x);
|
||||
try expectEqual(@as(usize, 3), as_slice.len);
|
||||
try expectEqual(@as(u8, 0x55), as_slice[0]);
|
||||
try expectEqual(@as(u8, 0xAA), as_slice[2]);
|
||||
}
|
||||
};
|
||||
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "zero-sized array with recursive type definition" {
|
||||
const U = struct {
|
||||
fn foo(comptime T: type, comptime n: usize) type {
|
||||
return struct {
|
||||
s: [n]T,
|
||||
x: usize = n,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const S = struct {
|
||||
list: U.foo(@This(), 0),
|
||||
};
|
||||
|
||||
var t: S = .{ .list = .{ .s = undefined } };
|
||||
try expectEqual(@as(usize, 0), t.list.x);
|
||||
}
|
||||
|
||||
test "type coercion of anon struct literal to array" {
|
||||
const S = struct {
|
||||
const U = union {
|
||||
a: u32,
|
||||
b: bool,
|
||||
c: []const u8,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var x1: u8 = 42;
|
||||
const t1 = .{ x1, 56, 54 };
|
||||
var arr1: [3]u8 = t1;
|
||||
try expect(arr1[0] == 42);
|
||||
try expect(arr1[1] == 56);
|
||||
try expect(arr1[2] == 54);
|
||||
|
||||
var x2: U = .{ .a = 42 };
|
||||
const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } };
|
||||
var arr2: [3]U = t2;
|
||||
try expect(arr2[0].a == 42);
|
||||
try expect(arr2[1].b == true);
|
||||
try expect(mem.eql(u8, arr2[2].c, "hello"));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "type coercion of pointer to anon struct literal to pointer to array" {
|
||||
const S = struct {
|
||||
const U = union {
|
||||
a: u32,
|
||||
b: bool,
|
||||
c: []const u8,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var x1: u8 = 42;
|
||||
const t1 = &.{ x1, 56, 54 };
|
||||
var arr1: *const [3]u8 = t1;
|
||||
try expect(arr1[0] == 42);
|
||||
try expect(arr1[1] == 56);
|
||||
try expect(arr1[2] == 54);
|
||||
|
||||
var x2: U = .{ .a = 42 };
|
||||
const t2 = &.{ x2, .{ .b = true }, .{ .c = "hello" } };
|
||||
var arr2: *const [3]U = t2;
|
||||
try expect(arr2[0].a == 42);
|
||||
try expect(arr2[1].b == true);
|
||||
try expect(mem.eql(u8, arr2[2].c, "hello"));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
489
test/behavior/array_stage1.zig
Normal file
489
test/behavior/array_stage1.zig
Normal file
@ -0,0 +1,489 @@
|
||||
const std = @import("std");
|
||||
const testing = std.testing;
|
||||
const mem = std.mem;
|
||||
const expect = testing.expect;
|
||||
const expectEqual = testing.expectEqual;
|
||||
|
||||
test "arrays" {
|
||||
var array: [5]u32 = undefined;
|
||||
|
||||
var i: u32 = 0;
|
||||
while (i < 5) {
|
||||
array[i] = i + 1;
|
||||
i = array[i];
|
||||
}
|
||||
|
||||
i = 0;
|
||||
var accumulator = @as(u32, 0);
|
||||
while (i < 5) {
|
||||
accumulator += array[i];
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
try expect(accumulator == 15);
|
||||
try expect(getArrayLen(&array) == 5);
|
||||
}
|
||||
fn getArrayLen(a: []const u32) usize {
|
||||
return a.len;
|
||||
}
|
||||
|
||||
test "array with sentinels" {
|
||||
const S = struct {
|
||||
fn doTheTest(is_ct: bool) !void {
|
||||
if (is_ct) {
|
||||
var zero_sized: [0:0xde]u8 = [_:0xde]u8{};
|
||||
// Disabled at runtime because of
|
||||
// https://github.com/ziglang/zig/issues/4372
|
||||
try expectEqual(@as(u8, 0xde), zero_sized[0]);
|
||||
var reinterpreted = @ptrCast(*[1]u8, &zero_sized);
|
||||
try expectEqual(@as(u8, 0xde), reinterpreted[0]);
|
||||
}
|
||||
var arr: [3:0x55]u8 = undefined;
|
||||
// Make sure the sentinel pointer is pointing after the last element
|
||||
if (!is_ct) {
|
||||
const sentinel_ptr = @ptrToInt(&arr[3]);
|
||||
const last_elem_ptr = @ptrToInt(&arr[2]);
|
||||
try expectEqual(@as(usize, 1), sentinel_ptr - last_elem_ptr);
|
||||
}
|
||||
// Make sure the sentinel is writeable
|
||||
arr[3] = 0x55;
|
||||
}
|
||||
};
|
||||
|
||||
try S.doTheTest(false);
|
||||
comptime try S.doTheTest(true);
|
||||
}
|
||||
|
||||
test "void arrays" {
|
||||
var array: [4]void = undefined;
|
||||
array[0] = void{};
|
||||
array[1] = array[2];
|
||||
try expect(@sizeOf(@TypeOf(array)) == 0);
|
||||
try expect(array.len == 4);
|
||||
}
|
||||
|
||||
test "array literal" {
|
||||
const hex_mult = [_]u16{
|
||||
4096,
|
||||
256,
|
||||
16,
|
||||
1,
|
||||
};
|
||||
|
||||
try expect(hex_mult.len == 4);
|
||||
try expect(hex_mult[1] == 256);
|
||||
}
|
||||
|
||||
test "array dot len const expr" {
|
||||
try expect(comptime x: {
|
||||
break :x some_array.len == 4;
|
||||
});
|
||||
}
|
||||
|
||||
const ArrayDotLenConstExpr = struct {
|
||||
y: [some_array.len]u8,
|
||||
};
|
||||
const some_array = [_]u8{
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
};
|
||||
|
||||
test "nested arrays" {
|
||||
const array_of_strings = [_][]const u8{
|
||||
"hello",
|
||||
"this",
|
||||
"is",
|
||||
"my",
|
||||
"thing",
|
||||
};
|
||||
for (array_of_strings) |s, i| {
|
||||
if (i == 0) try expect(mem.eql(u8, s, "hello"));
|
||||
if (i == 1) try expect(mem.eql(u8, s, "this"));
|
||||
if (i == 2) try expect(mem.eql(u8, s, "is"));
|
||||
if (i == 3) try expect(mem.eql(u8, s, "my"));
|
||||
if (i == 4) try expect(mem.eql(u8, s, "thing"));
|
||||
}
|
||||
}
|
||||
|
||||
var s_array: [8]Sub = undefined;
|
||||
const Sub = struct {
|
||||
b: u8,
|
||||
};
|
||||
const Str = struct {
|
||||
a: []Sub,
|
||||
};
|
||||
test "set global var array via slice embedded in struct" {
|
||||
var s = Str{ .a = s_array[0..] };
|
||||
|
||||
s.a[0].b = 1;
|
||||
s.a[1].b = 2;
|
||||
s.a[2].b = 3;
|
||||
|
||||
try expect(s_array[0].b == 1);
|
||||
try expect(s_array[1].b == 2);
|
||||
try expect(s_array[2].b == 3);
|
||||
}
|
||||
|
||||
test "array literal with specified size" {
|
||||
var array = [2]u8{
|
||||
1,
|
||||
2,
|
||||
};
|
||||
try expect(array[0] == 1);
|
||||
try expect(array[1] == 2);
|
||||
}
|
||||
|
||||
test "array len field" {
|
||||
var arr = [4]u8{ 0, 0, 0, 0 };
|
||||
var ptr = &arr;
|
||||
try expect(arr.len == 4);
|
||||
comptime try expect(arr.len == 4);
|
||||
try expect(ptr.len == 4);
|
||||
comptime try expect(ptr.len == 4);
|
||||
}
|
||||
|
||||
test "single-item pointer to array indexing and slicing" {
|
||||
try testSingleItemPtrArrayIndexSlice();
|
||||
comptime try testSingleItemPtrArrayIndexSlice();
|
||||
}
|
||||
|
||||
fn testSingleItemPtrArrayIndexSlice() !void {
|
||||
{
|
||||
var array: [4]u8 = "aaaa".*;
|
||||
doSomeMangling(&array);
|
||||
try expect(mem.eql(u8, "azya", &array));
|
||||
}
|
||||
{
|
||||
var array = "aaaa".*;
|
||||
doSomeMangling(&array);
|
||||
try expect(mem.eql(u8, "azya", &array));
|
||||
}
|
||||
}
|
||||
|
||||
fn doSomeMangling(array: *[4]u8) void {
|
||||
array[1] = 'z';
|
||||
array[2..3][0] = 'y';
|
||||
}
|
||||
|
||||
test "implicit cast single-item pointer" {
|
||||
try testImplicitCastSingleItemPtr();
|
||||
comptime try testImplicitCastSingleItemPtr();
|
||||
}
|
||||
|
||||
fn testImplicitCastSingleItemPtr() !void {
|
||||
var byte: u8 = 100;
|
||||
const slice = @as(*[1]u8, &byte)[0..];
|
||||
slice[0] += 1;
|
||||
try expect(byte == 101);
|
||||
}
|
||||
|
||||
fn testArrayByValAtComptime(b: [2]u8) u8 {
|
||||
return b[0];
|
||||
}
|
||||
|
||||
test "comptime evalutating function that takes array by value" {
|
||||
const arr = [_]u8{ 0, 1 };
|
||||
_ = comptime testArrayByValAtComptime(arr);
|
||||
_ = comptime testArrayByValAtComptime(arr);
|
||||
}
|
||||
|
||||
test "implicit comptime in array type size" {
|
||||
var arr: [plusOne(10)]bool = undefined;
|
||||
try expect(arr.len == 11);
|
||||
}
|
||||
|
||||
fn plusOne(x: u32) u32 {
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
test "runtime initialize array elem and then implicit cast to slice" {
|
||||
var two: i32 = 2;
|
||||
const x: []const i32 = &[_]i32{two};
|
||||
try expect(x[0] == 2);
|
||||
}
|
||||
|
||||
test "array literal as argument to function" {
|
||||
const S = struct {
|
||||
fn entry(two: i32) !void {
|
||||
try foo(&[_]i32{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
});
|
||||
try foo(&[_]i32{
|
||||
1,
|
||||
two,
|
||||
3,
|
||||
});
|
||||
try foo2(true, &[_]i32{
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
});
|
||||
try foo2(true, &[_]i32{
|
||||
1,
|
||||
two,
|
||||
3,
|
||||
});
|
||||
}
|
||||
fn foo(x: []const i32) !void {
|
||||
try expect(x[0] == 1);
|
||||
try expect(x[1] == 2);
|
||||
try expect(x[2] == 3);
|
||||
}
|
||||
fn foo2(trash: bool, x: []const i32) !void {
|
||||
try expect(trash);
|
||||
try expect(x[0] == 1);
|
||||
try expect(x[1] == 2);
|
||||
try expect(x[2] == 3);
|
||||
}
|
||||
};
|
||||
try S.entry(2);
|
||||
comptime try S.entry(2);
|
||||
}
|
||||
|
||||
test "double nested array to const slice cast in array literal" {
|
||||
const S = struct {
|
||||
fn entry(two: i32) !void {
|
||||
const cases = [_][]const []const i32{
|
||||
&[_][]const i32{&[_]i32{1}},
|
||||
&[_][]const i32{&[_]i32{ 2, 3 }},
|
||||
&[_][]const i32{
|
||||
&[_]i32{4},
|
||||
&[_]i32{ 5, 6, 7 },
|
||||
},
|
||||
};
|
||||
try check(&cases);
|
||||
|
||||
const cases2 = [_][]const i32{
|
||||
&[_]i32{1},
|
||||
&[_]i32{ two, 3 },
|
||||
};
|
||||
try expect(cases2.len == 2);
|
||||
try expect(cases2[0].len == 1);
|
||||
try expect(cases2[0][0] == 1);
|
||||
try expect(cases2[1].len == 2);
|
||||
try expect(cases2[1][0] == 2);
|
||||
try expect(cases2[1][1] == 3);
|
||||
|
||||
const cases3 = [_][]const []const i32{
|
||||
&[_][]const i32{&[_]i32{1}},
|
||||
&[_][]const i32{&[_]i32{ two, 3 }},
|
||||
&[_][]const i32{
|
||||
&[_]i32{4},
|
||||
&[_]i32{ 5, 6, 7 },
|
||||
},
|
||||
};
|
||||
try check(&cases3);
|
||||
}
|
||||
|
||||
fn check(cases: []const []const []const i32) !void {
|
||||
try expect(cases.len == 3);
|
||||
try expect(cases[0].len == 1);
|
||||
try expect(cases[0][0].len == 1);
|
||||
try expect(cases[0][0][0] == 1);
|
||||
try expect(cases[1].len == 1);
|
||||
try expect(cases[1][0].len == 2);
|
||||
try expect(cases[1][0][0] == 2);
|
||||
try expect(cases[1][0][1] == 3);
|
||||
try expect(cases[2].len == 2);
|
||||
try expect(cases[2][0].len == 1);
|
||||
try expect(cases[2][0][0] == 4);
|
||||
try expect(cases[2][1].len == 3);
|
||||
try expect(cases[2][1][0] == 5);
|
||||
try expect(cases[2][1][1] == 6);
|
||||
try expect(cases[2][1][2] == 7);
|
||||
}
|
||||
};
|
||||
try S.entry(2);
|
||||
comptime try S.entry(2);
|
||||
}
|
||||
|
||||
test "read/write through global variable array of struct fields initialized via array mult" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
try expect(storage[0].term == 1);
|
||||
storage[0] = MyStruct{ .term = 123 };
|
||||
try expect(storage[0].term == 123);
|
||||
}
|
||||
|
||||
pub const MyStruct = struct {
|
||||
term: usize,
|
||||
};
|
||||
|
||||
var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1;
|
||||
};
|
||||
try S.doTheTest();
|
||||
}
|
||||
|
||||
test "implicit cast zero sized array ptr to slice" {
|
||||
{
|
||||
var b = "".*;
|
||||
const c: []const u8 = &b;
|
||||
try expect(c.len == 0);
|
||||
}
|
||||
{
|
||||
var b: [0]u8 = "".*;
|
||||
const c: []const u8 = &b;
|
||||
try expect(c.len == 0);
|
||||
}
|
||||
}
|
||||
|
||||
test "anonymous list literal syntax" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array: [4]u8 = .{ 1, 2, 3, 4 };
|
||||
try expect(array[0] == 1);
|
||||
try expect(array[1] == 2);
|
||||
try expect(array[2] == 3);
|
||||
try expect(array[3] == 4);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "anonymous literal in array" {
|
||||
const S = struct {
|
||||
const Foo = struct {
|
||||
a: usize = 2,
|
||||
b: usize = 4,
|
||||
};
|
||||
fn doTheTest() !void {
|
||||
var array: [2]Foo = .{
|
||||
.{ .a = 3 },
|
||||
.{ .b = 3 },
|
||||
};
|
||||
try expect(array[0].a == 3);
|
||||
try expect(array[0].b == 4);
|
||||
try expect(array[1].a == 2);
|
||||
try expect(array[1].b == 3);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "access the null element of a null terminated array" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' };
|
||||
try expect(array[4] == 0);
|
||||
var len: usize = 4;
|
||||
try expect(array[len] == 0);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "type deduction for array subscript expression" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
var array = [_]u8{ 0x55, 0xAA };
|
||||
var v0 = true;
|
||||
try expectEqual(@as(u8, 0xAA), array[if (v0) 1 else 0]);
|
||||
var v1 = false;
|
||||
try expectEqual(@as(u8, 0x55), array[if (v1) 1 else 0]);
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "sentinel element count towards the ABI size calculation" {
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
const T = packed struct {
|
||||
fill_pre: u8 = 0x55,
|
||||
data: [0:0]u8 = undefined,
|
||||
fill_post: u8 = 0xAA,
|
||||
};
|
||||
var x = T{};
|
||||
var as_slice = mem.asBytes(&x);
|
||||
try expectEqual(@as(usize, 3), as_slice.len);
|
||||
try expectEqual(@as(u8, 0x55), as_slice[0]);
|
||||
try expectEqual(@as(u8, 0xAA), as_slice[2]);
|
||||
}
|
||||
};
|
||||
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "zero-sized array with recursive type definition" {
|
||||
const U = struct {
|
||||
fn foo(comptime T: type, comptime n: usize) type {
|
||||
return struct {
|
||||
s: [n]T,
|
||||
x: usize = n,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const S = struct {
|
||||
list: U.foo(@This(), 0),
|
||||
};
|
||||
|
||||
var t: S = .{ .list = .{ .s = undefined } };
|
||||
try expectEqual(@as(usize, 0), t.list.x);
|
||||
}
|
||||
|
||||
test "type coercion of anon struct literal to array" {
|
||||
const S = struct {
|
||||
const U = union {
|
||||
a: u32,
|
||||
b: bool,
|
||||
c: []const u8,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var x1: u8 = 42;
|
||||
const t1 = .{ x1, 56, 54 };
|
||||
var arr1: [3]u8 = t1;
|
||||
try expect(arr1[0] == 42);
|
||||
try expect(arr1[1] == 56);
|
||||
try expect(arr1[2] == 54);
|
||||
|
||||
var x2: U = .{ .a = 42 };
|
||||
const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } };
|
||||
var arr2: [3]U = t2;
|
||||
try expect(arr2[0].a == 42);
|
||||
try expect(arr2[1].b == true);
|
||||
try expect(mem.eql(u8, arr2[2].c, "hello"));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
|
||||
test "type coercion of pointer to anon struct literal to pointer to array" {
|
||||
const S = struct {
|
||||
const U = union {
|
||||
a: u32,
|
||||
b: bool,
|
||||
c: []const u8,
|
||||
};
|
||||
|
||||
fn doTheTest() !void {
|
||||
var x1: u8 = 42;
|
||||
const t1 = &.{ x1, 56, 54 };
|
||||
var arr1: *const [3]u8 = t1;
|
||||
try expect(arr1[0] == 42);
|
||||
try expect(arr1[1] == 56);
|
||||
try expect(arr1[2] == 54);
|
||||
|
||||
var x2: U = .{ .a = 42 };
|
||||
const t2 = &.{ x2, .{ .b = true }, .{ .c = "hello" } };
|
||||
var arr2: *const [3]U = t2;
|
||||
try expect(arr2[0].a == 42);
|
||||
try expect(arr2[1].b == true);
|
||||
try expect(mem.eql(u8, arr2[2].c, "hello"));
|
||||
}
|
||||
};
|
||||
try S.doTheTest();
|
||||
comptime try S.doTheTest();
|
||||
}
|
||||
@ -130,3 +130,21 @@ test "no undeclared identifier error in unanalyzed branches" {
|
||||
lol_this_doesnt_exist = nonsense;
|
||||
}
|
||||
}
|
||||
|
||||
test "a type constructed in a global expression" {
|
||||
var l: List = undefined;
|
||||
l.array[0] = 10;
|
||||
l.array[1] = 11;
|
||||
l.array[2] = 12;
|
||||
const ptr = @ptrCast([*]u8, &l.array);
|
||||
try expect(ptr[0] == 10);
|
||||
try expect(ptr[1] == 11);
|
||||
try expect(ptr[2] == 12);
|
||||
}
|
||||
|
||||
const List = blk: {
|
||||
const T = [10]u8;
|
||||
break :blk struct {
|
||||
array: T,
|
||||
};
|
||||
};
|
||||
|
||||
@ -32,18 +32,20 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
var case = ctx.exeUsingLlvmBackend("shift right + left", linux_x64);
|
||||
|
||||
case.addCompareOutput(
|
||||
\\pub export fn main() void {
|
||||
\\pub export fn main() c_int {
|
||||
\\ var i: u32 = 16;
|
||||
\\ assert(i >> 1, 8);
|
||||
\\ return 0;
|
||||
\\}
|
||||
\\fn assert(a: u32, b: u32) void {
|
||||
\\ if (a != b) unreachable;
|
||||
\\}
|
||||
, "");
|
||||
case.addCompareOutput(
|
||||
\\pub export fn main() void {
|
||||
\\pub export fn main() c_int {
|
||||
\\ var i: u32 = 16;
|
||||
\\ assert(i << 1, 32);
|
||||
\\ return 0;
|
||||
\\}
|
||||
\\fn assert(a: u32, b: u32) void {
|
||||
\\ if (a != b) unreachable;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user