| author | |
| committer | |
| log | 0cd361219c107bce48f2d7b44c6f3dd05ea6ccf4 |
| tree | c3de2205e0bb525d61ee78800c118b13efbf1029 |
| parent | 2f1abd919a8465e515875cd08816778df795aabe |
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.18 files changed, 1294 insertions(+), 986 deletions(-)
lib/std/builtin.zig+1-1| ... | @@ -237,7 +237,7 @@ pub const TypeInfo = union(enum) { | ... | @@ -237,7 +237,7 @@ pub const TypeInfo = union(enum) { |
| 237 | /// This field is an optional type. | 237 | /// This field is an optional type. |
| 238 | /// The type of the sentinel is the element type of the pointer, which is | 238 | /// The type of the sentinel is the element type of the pointer, which is |
| 239 | /// the value of the `child` field in this struct. However there is no way | 239 | /// the value of the `child` field in this struct. However there is no way |
| 240 | /// to refer to that type here, so we use `var`. | 240 | /// to refer to that type here, so we use `anytype`. |
| 241 | sentinel: anytype, | 241 | sentinel: anytype, |
| 242 | 242 | ||
| 243 | /// This data structure is used by the Zig language code generation and | 243 | /// This data structure is used by the Zig language code generation and |
src/Air.zig+12-2| ... | @@ -286,6 +286,10 @@ pub const Inst = struct { | ... | @@ -286,6 +286,10 @@ pub const Inst = struct { |
| 286 | /// Result type is the element type of the pointer operand. | 286 | /// Result type is the element type of the pointer operand. |
| 287 | /// Uses the `bin_op` field. | 287 | /// Uses the `bin_op` field. |
| 288 | ptr_elem_val, | 288 | ptr_elem_val, |
| 289 | /// Given a pointer value, and element index, return the element pointer at that index. | ||
| 290 | /// Result type is pointer to the element type of the pointer operand. | ||
| 291 | /// Uses the `ty_pl` field with payload `Bin`. | ||
| 292 | ptr_elem_ptr, | ||
| 289 | /// Given a pointer to a pointer, and element index, return the element value of the inner | 293 | /// Given a pointer to a pointer, and element index, return the element value of the inner |
| 290 | /// pointer at that index. | 294 | /// pointer at that index. |
| 291 | /// Result type is the element type of the inner pointer operand. | 295 | /// Result type is the element type of the inner pointer operand. |
| ... | @@ -410,6 +414,11 @@ pub const StructField = struct { | ... | @@ -410,6 +414,11 @@ pub const StructField = struct { |
| 410 | field_index: u32, | 414 | field_index: u32, |
| 411 | }; | 415 | }; |
| 412 | 416 | ||
| 417 | pub const Bin = struct { | ||
| 418 | lhs: Inst.Ref, | ||
| 419 | rhs: Inst.Ref, | ||
| 420 | }; | ||
| 421 | |||
| 413 | /// Trailing: | 422 | /// Trailing: |
| 414 | /// 0. `Inst.Ref` for every outputs_len | 423 | /// 0. `Inst.Ref` for every outputs_len |
| 415 | /// 1. `Inst.Ref` for every inputs_len | 424 | /// 1. `Inst.Ref` for every inputs_len |
| ... | @@ -482,6 +491,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -482,6 +491,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 482 | .constant, | 491 | .constant, |
| 483 | .struct_field_ptr, | 492 | .struct_field_ptr, |
| 484 | .struct_field_val, | 493 | .struct_field_val, |
| 494 | .ptr_elem_ptr, | ||
| 485 | => return air.getRefType(datas[inst].ty_pl.ty), | 495 | => return air.getRefType(datas[inst].ty_pl.ty), |
| 486 | 496 | ||
| 487 | .not, | 497 | .not, |
| ... | @@ -527,8 +537,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -527,8 +537,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 527 | }, | 537 | }, |
| 528 | 538 | ||
| 529 | .slice_elem_val, .ptr_elem_val => { | 539 | .slice_elem_val, .ptr_elem_val => { |
| 530 | const slice_ty = air.typeOf(datas[inst].bin_op.lhs); | 540 | const ptr_ty = air.typeOf(datas[inst].bin_op.lhs); |
| 531 | return slice_ty.elemType(); | 541 | return ptr_ty.elemType(); |
| 532 | }, | 542 | }, |
| 533 | .ptr_slice_elem_val, .ptr_ptr_elem_val => { | 543 | .ptr_slice_elem_val, .ptr_ptr_elem_val => { |
| 534 | const outer_ptr_ty = air.typeOf(datas[inst].bin_op.lhs); | 544 | const outer_ptr_ty = air.typeOf(datas[inst].bin_op.lhs); |
src/AstGen.zig+32-32| ... | @@ -7102,38 +7102,38 @@ fn builtinCall( | ... | @@ -7102,38 +7102,38 @@ fn builtinCall( |
| 7102 | .bit_size_of => return simpleUnOpType(gz, scope, rl, node, params[0], .bit_size_of), | 7102 | .bit_size_of => return simpleUnOpType(gz, scope, rl, node, params[0], .bit_size_of), |
| 7103 | .align_of => return simpleUnOpType(gz, scope, rl, node, params[0], .align_of), | 7103 | .align_of => return simpleUnOpType(gz, scope, rl, node, params[0], .align_of), |
| 7104 | 7104 | ||
| 7105 | .ptr_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ptr_to_int), | 7105 | .ptr_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ptr_to_int), |
| 7106 | .error_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .error_to_int), | 7106 | .error_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .error_to_int), |
| 7107 | .int_to_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u16_type }, params[0], .int_to_error), | 7107 | .int_to_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u16_type }, params[0], .int_to_error), |
| 7108 | .compile_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .compile_error), | 7108 | .compile_error => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .compile_error), |
| 7109 | .set_eval_branch_quota => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u32_type }, params[0], .set_eval_branch_quota), | 7109 | .set_eval_branch_quota => return simpleUnOp(gz, scope, rl, node, .{ .ty = .u32_type }, params[0], .set_eval_branch_quota), |
| 7110 | .enum_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .enum_to_int), | 7110 | .enum_to_int => return simpleUnOp(gz, scope, rl, node, .none, params[0], .enum_to_int), |
| 7111 | .bool_to_int => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .bool_to_int), | 7111 | .bool_to_int => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .bool_to_int), |
| 7112 | .embed_file => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .embed_file), | 7112 | .embed_file => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .embed_file), |
| 7113 | .error_name => return simpleUnOp(gz, scope, rl, node, .{ .ty = .anyerror_type }, params[0], .error_name), | 7113 | .error_name => return simpleUnOp(gz, scope, rl, node, .{ .ty = .anyerror_type }, params[0], .error_name), |
| 7114 | .panic => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .panic), | 7114 | .panic => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type }, params[0], .panic), |
| 7115 | .set_align_stack => return simpleUnOp(gz, scope, rl, node, align_rl, params[0], .set_align_stack), | 7115 | .set_align_stack => return simpleUnOp(gz, scope, rl, node, align_rl, params[0], .set_align_stack), |
| 7116 | .set_cold => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_cold), | 7116 | .set_cold => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_cold), |
| 7117 | .set_float_mode => return simpleUnOp(gz, scope, rl, node, .{ .ty = .float_mode_type }, params[0], .set_float_mode), | 7117 | .set_float_mode => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .float_mode_type }, params[0], .set_float_mode), |
| 7118 | .set_runtime_safety => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_runtime_safety), | 7118 | .set_runtime_safety => return simpleUnOp(gz, scope, rl, node, bool_rl, params[0], .set_runtime_safety), |
| 7119 | .sqrt => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sqrt), | 7119 | .sqrt => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sqrt), |
| 7120 | .sin => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sin), | 7120 | .sin => return simpleUnOp(gz, scope, rl, node, .none, params[0], .sin), |
| 7121 | .cos => return simpleUnOp(gz, scope, rl, node, .none, params[0], .cos), | 7121 | .cos => return simpleUnOp(gz, scope, rl, node, .none, params[0], .cos), |
| 7122 | .exp => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp), | 7122 | .exp => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp), |
| 7123 | .exp2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp2), | 7123 | .exp2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .exp2), |
| 7124 | .log => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log), | 7124 | .log => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log), |
| 7125 | .log2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log2), | 7125 | .log2 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log2), |
| 7126 | .log10 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log10), | 7126 | .log10 => return simpleUnOp(gz, scope, rl, node, .none, params[0], .log10), |
| 7127 | .fabs => return simpleUnOp(gz, scope, rl, node, .none, params[0], .fabs), | 7127 | .fabs => return simpleUnOp(gz, scope, rl, node, .none, params[0], .fabs), |
| 7128 | .floor => return simpleUnOp(gz, scope, rl, node, .none, params[0], .floor), | 7128 | .floor => return simpleUnOp(gz, scope, rl, node, .none, params[0], .floor), |
| 7129 | .ceil => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ceil), | 7129 | .ceil => return simpleUnOp(gz, scope, rl, node, .none, params[0], .ceil), |
| 7130 | .trunc => return simpleUnOp(gz, scope, rl, node, .none, params[0], .trunc), | 7130 | .trunc => return simpleUnOp(gz, scope, rl, node, .none, params[0], .trunc), |
| 7131 | .round => return simpleUnOp(gz, scope, rl, node, .none, params[0], .round), | 7131 | .round => return simpleUnOp(gz, scope, rl, node, .none, params[0], .round), |
| 7132 | .tag_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .tag_name), | 7132 | .tag_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .tag_name), |
| 7133 | .Type => return simpleUnOp(gz, scope, rl, node, .none, params[0], .reify), | 7133 | .Type => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .type_info_type }, params[0], .reify), |
| 7134 | .type_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .type_name), | 7134 | .type_name => return simpleUnOp(gz, scope, rl, node, .none, params[0], .type_name), |
| 7135 | .Frame => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_type), | 7135 | .Frame => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_type), |
| 7136 | .frame_size => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_size), | 7136 | .frame_size => return simpleUnOp(gz, scope, rl, node, .none, params[0], .frame_size), |
| 7137 | 7137 | ||
| 7138 | .float_to_int => return typeCast(gz, scope, rl, node, params[0], params[1], .float_to_int), | 7138 | .float_to_int => return typeCast(gz, scope, rl, node, params[0], params[1], .float_to_int), |
| 7139 | .int_to_float => return typeCast(gz, scope, rl, node, params[0], params[1], .int_to_float), | 7139 | .int_to_float => return typeCast(gz, scope, rl, node, params[0], params[1], .int_to_float), |
src/Liveness.zig+4| ... | @@ -330,6 +330,10 @@ fn analyzeInst( | ... | @@ -330,6 +330,10 @@ fn analyzeInst( |
| 330 | const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data; | 330 | const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data; |
| 331 | return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_operand, .none, .none }); | 331 | return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_operand, .none, .none }); |
| 332 | }, | 332 | }, |
| 333 | .ptr_elem_ptr => { | ||
| 334 | const extra = a.air.extraData(Air.Bin, inst_datas[inst].ty_pl.payload).data; | ||
| 335 | return trackOperands(a, new_set, inst, main_tomb, .{ extra.lhs, extra.rhs, .none }); | ||
| 336 | }, | ||
| 333 | .br => { | 337 | .br => { |
| 334 | const br = inst_datas[inst].br; | 338 | const br = inst_datas[inst].br; |
| 335 | return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none }); | 339 | return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none }); |
src/Module.zig+15-305| ... | @@ -554,8 +554,8 @@ pub const Decl = struct { | ... | @@ -554,8 +554,8 @@ pub const Decl = struct { |
| 554 | assert(struct_obj.owner_decl == decl); | 554 | assert(struct_obj.owner_decl == decl); |
| 555 | return &struct_obj.namespace; | 555 | return &struct_obj.namespace; |
| 556 | }, | 556 | }, |
| 557 | .enum_full => { | 557 | .enum_full, .enum_nonexhaustive => { |
| 558 | const enum_obj = ty.castTag(.enum_full).?.data; | 558 | const enum_obj = ty.cast(Type.Payload.EnumFull).?.data; |
| 559 | assert(enum_obj.owner_decl == decl); | 559 | assert(enum_obj.owner_decl == decl); |
| 560 | return &enum_obj.namespace; | 560 | return &enum_obj.namespace; |
| 561 | }, | 561 | }, |
| ... | @@ -660,6 +660,7 @@ pub const Struct = struct { | ... | @@ -660,6 +660,7 @@ pub const Struct = struct { |
| 660 | /// is necessary to determine whether it has bits at runtime. | 660 | /// is necessary to determine whether it has bits at runtime. |
| 661 | known_has_bits: bool, | 661 | known_has_bits: bool, |
| 662 | 662 | ||
| 663 | /// The `Type` and `Value` memory is owned by the arena of the Struct's owner_decl. | ||
| 663 | pub const Field = struct { | 664 | pub const Field = struct { |
| 664 | /// Uses `noreturn` to indicate `anytype`. | 665 | /// Uses `noreturn` to indicate `anytype`. |
| 665 | /// undefined until `status` is `have_field_types` or `have_layout`. | 666 | /// undefined until `status` is `have_field_types` or `have_layout`. |
| ... | @@ -3091,6 +3092,9 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { | ... | @@ -3091,6 +3092,9 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3091 | if (linksection_ref == .none) break :blk Value.initTag(.null_value); | 3092 | if (linksection_ref == .none) break :blk Value.initTag(.null_value); |
| 3092 | break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val; | 3093 | break :blk (try sema.resolveInstConst(&block_scope, src, linksection_ref)).val; |
| 3093 | }; | 3094 | }; |
| 3095 | // Note this resolves the type of the Decl, not the value; if this Decl | ||
| 3096 | // is a struct, for example, this resolves `type` (which needs no resolution), | ||
| 3097 | // not the struct itself. | ||
| 3094 | try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty); | 3098 | try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty); |
| 3095 | 3099 | ||
| 3096 | // We need the memory for the Type to go into the arena for the Decl | 3100 | // 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 { | ... | @@ -3193,6 +3197,15 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { |
| 3193 | if (type_changed and mod.emit_h != null) { | 3197 | if (type_changed and mod.emit_h != null) { |
| 3194 | try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); | 3198 | try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl }); |
| 3195 | } | 3199 | } |
| 3200 | } else if (decl_tv.ty.zigTypeTag() == .Type) { | ||
| 3201 | // In case this Decl is a struct or union, we need to resolve the fields | ||
| 3202 | // while we still have the `Sema` in scope, so that the field type expressions | ||
| 3203 | // can use the resolved AIR instructions that they possibly reference. | ||
| 3204 | // We do this after the decl is populated and set to `complete` so that a `Decl` | ||
| 3205 | // may reference itself. | ||
| 3206 | var buffer: Value.ToTypeBuffer = undefined; | ||
| 3207 | const ty = decl.val.toType(&buffer); | ||
| 3208 | try sema.resolveDeclFields(&block_scope, src, ty); | ||
| 3196 | } | 3209 | } |
| 3197 | 3210 | ||
| 3198 | if (decl.is_exported) { | 3211 | if (decl.is_exported) { |
| ... | @@ -4450,309 +4463,6 @@ pub const PeerTypeCandidateSrc = union(enum) { | ... | @@ -4450,309 +4463,6 @@ pub const PeerTypeCandidateSrc = union(enum) { |
| 4450 | } | 4463 | } |
| 4451 | }; | 4464 | }; |
| 4452 | 4465 | ||
| 4453 | pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) CompileError!void { | ||
| 4454 | const tracy = trace(@src()); | ||
| 4455 | defer tracy.end(); | ||
| 4456 | |||
| 4457 | const gpa = mod.gpa; | ||
| 4458 | const zir = struct_obj.owner_decl.namespace.file_scope.zir; | ||
| 4459 | const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; | ||
| 4460 | assert(extended.opcode == .struct_decl); | ||
| 4461 | const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); | ||
| 4462 | var extra_index: usize = extended.operand; | ||
| 4463 | |||
| 4464 | const src: LazySrcLoc = .{ .node_offset = struct_obj.node_offset }; | ||
| 4465 | extra_index += @boolToInt(small.has_src_node); | ||
| 4466 | |||
| 4467 | const body_len = if (small.has_body_len) blk: { | ||
| 4468 | const body_len = zir.extra[extra_index]; | ||
| 4469 | extra_index += 1; | ||
| 4470 | break :blk body_len; | ||
| 4471 | } else 0; | ||
| 4472 | |||
| 4473 | const fields_len = if (small.has_fields_len) blk: { | ||
| 4474 | const fields_len = zir.extra[extra_index]; | ||
| 4475 | extra_index += 1; | ||
| 4476 | break :blk fields_len; | ||
| 4477 | } else 0; | ||
| 4478 | |||
| 4479 | const decls_len = if (small.has_decls_len) decls_len: { | ||
| 4480 | const decls_len = zir.extra[extra_index]; | ||
| 4481 | extra_index += 1; | ||
| 4482 | break :decls_len decls_len; | ||
| 4483 | } else 0; | ||
| 4484 | |||
| 4485 | // Skip over decls. | ||
| 4486 | var decls_it = zir.declIteratorInner(extra_index, decls_len); | ||
| 4487 | while (decls_it.next()) |_| {} | ||
| 4488 | extra_index = decls_it.extra_index; | ||
| 4489 | |||
| 4490 | const body = zir.extra[extra_index..][0..body_len]; | ||
| 4491 | if (fields_len == 0) { | ||
| 4492 | assert(body.len == 0); | ||
| 4493 | return; | ||
| 4494 | } | ||
| 4495 | extra_index += body.len; | ||
| 4496 | |||
| 4497 | var decl_arena = struct_obj.owner_decl.value_arena.?.promote(gpa); | ||
| 4498 | defer struct_obj.owner_decl.value_arena.?.* = decl_arena.state; | ||
| 4499 | |||
| 4500 | try struct_obj.fields.ensureCapacity(&decl_arena.allocator, fields_len); | ||
| 4501 | |||
| 4502 | // We create a block for the field type instructions because they | ||
| 4503 | // may need to reference Decls from inside the struct namespace. | ||
| 4504 | // Within the field type, default value, and alignment expressions, the "owner decl" | ||
| 4505 | // should be the struct itself. Thus we need a new Sema. | ||
| 4506 | var sema: Sema = .{ | ||
| 4507 | .mod = mod, | ||
| 4508 | .gpa = gpa, | ||
| 4509 | .arena = &decl_arena.allocator, | ||
| 4510 | .code = zir, | ||
| 4511 | .owner_decl = struct_obj.owner_decl, | ||
| 4512 | .namespace = &struct_obj.namespace, | ||
| 4513 | .owner_func = null, | ||
| 4514 | .func = null, | ||
| 4515 | .fn_ret_ty = Type.initTag(.void), | ||
| 4516 | }; | ||
| 4517 | defer sema.deinit(); | ||
| 4518 | |||
| 4519 | var block: Scope.Block = .{ | ||
| 4520 | .parent = null, | ||
| 4521 | .sema = &sema, | ||
| 4522 | .src_decl = struct_obj.owner_decl, | ||
| 4523 | .instructions = .{}, | ||
| 4524 | .inlining = null, | ||
| 4525 | .is_comptime = true, | ||
| 4526 | }; | ||
| 4527 | defer assert(block.instructions.items.len == 0); // should all be comptime instructions | ||
| 4528 | |||
| 4529 | if (body.len != 0) { | ||
| 4530 | _ = try sema.analyzeBody(&block, body); | ||
| 4531 | } | ||
| 4532 | |||
| 4533 | const bits_per_field = 4; | ||
| 4534 | const fields_per_u32 = 32 / bits_per_field; | ||
| 4535 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; | ||
| 4536 | var bit_bag_index: usize = extra_index; | ||
| 4537 | extra_index += bit_bags_count; | ||
| 4538 | var cur_bit_bag: u32 = undefined; | ||
| 4539 | var field_i: u32 = 0; | ||
| 4540 | while (field_i < fields_len) : (field_i += 1) { | ||
| 4541 | if (field_i % fields_per_u32 == 0) { | ||
| 4542 | cur_bit_bag = zir.extra[bit_bag_index]; | ||
| 4543 | bit_bag_index += 1; | ||
| 4544 | } | ||
| 4545 | const has_align = @truncate(u1, cur_bit_bag) != 0; | ||
| 4546 | cur_bit_bag >>= 1; | ||
| 4547 | const has_default = @truncate(u1, cur_bit_bag) != 0; | ||
| 4548 | cur_bit_bag >>= 1; | ||
| 4549 | const is_comptime = @truncate(u1, cur_bit_bag) != 0; | ||
| 4550 | cur_bit_bag >>= 1; | ||
| 4551 | const unused = @truncate(u1, cur_bit_bag) != 0; | ||
| 4552 | cur_bit_bag >>= 1; | ||
| 4553 | |||
| 4554 | _ = unused; | ||
| 4555 | |||
| 4556 | const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); | ||
| 4557 | extra_index += 1; | ||
| 4558 | const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | ||
| 4559 | extra_index += 1; | ||
| 4560 | |||
| 4561 | // This string needs to outlive the ZIR code. | ||
| 4562 | const field_name = try decl_arena.allocator.dupe(u8, field_name_zir); | ||
| 4563 | if (field_type_ref == .none) { | ||
| 4564 | return mod.fail(&block.base, src, "TODO: implement anytype struct field", .{}); | ||
| 4565 | } | ||
| 4566 | const field_ty: Type = if (field_type_ref == .none) | ||
| 4567 | Type.initTag(.noreturn) | ||
| 4568 | else | ||
| 4569 | // TODO: if we need to report an error here, use a source location | ||
| 4570 | // that points to this type expression rather than the struct. | ||
| 4571 | // But only resolve the source location if we need to emit a compile error. | ||
| 4572 | try sema.resolveType(&block, src, field_type_ref); | ||
| 4573 | |||
| 4574 | const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); | ||
| 4575 | assert(!gop.found_existing); | ||
| 4576 | gop.value_ptr.* = .{ | ||
| 4577 | .ty = field_ty, | ||
| 4578 | .abi_align = Value.initTag(.abi_align_default), | ||
| 4579 | .default_val = Value.initTag(.unreachable_value), | ||
| 4580 | .is_comptime = is_comptime, | ||
| 4581 | .offset = undefined, | ||
| 4582 | }; | ||
| 4583 | |||
| 4584 | if (has_align) { | ||
| 4585 | const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | ||
| 4586 | extra_index += 1; | ||
| 4587 | // TODO: if we need to report an error here, use a source location | ||
| 4588 | // that points to this alignment expression rather than the struct. | ||
| 4589 | // But only resolve the source location if we need to emit a compile error. | ||
| 4590 | gop.value_ptr.abi_align = (try sema.resolveInstConst(&block, src, align_ref)).val; | ||
| 4591 | } | ||
| 4592 | if (has_default) { | ||
| 4593 | const default_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | ||
| 4594 | extra_index += 1; | ||
| 4595 | // TODO: if we need to report an error here, use a source location | ||
| 4596 | // that points to this default value expression rather than the struct. | ||
| 4597 | // But only resolve the source location if we need to emit a compile error. | ||
| 4598 | gop.value_ptr.default_val = (try sema.resolveInstConst(&block, src, default_ref)).val; | ||
| 4599 | } | ||
| 4600 | } | ||
| 4601 | } | ||
| 4602 | |||
| 4603 | pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) CompileError!void { | ||
| 4604 | const tracy = trace(@src()); | ||
| 4605 | defer tracy.end(); | ||
| 4606 | |||
| 4607 | const gpa = mod.gpa; | ||
| 4608 | const zir = union_obj.owner_decl.namespace.file_scope.zir; | ||
| 4609 | const extended = zir.instructions.items(.data)[union_obj.zir_index].extended; | ||
| 4610 | assert(extended.opcode == .union_decl); | ||
| 4611 | const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); | ||
| 4612 | var extra_index: usize = extended.operand; | ||
| 4613 | |||
| 4614 | const src: LazySrcLoc = .{ .node_offset = union_obj.node_offset }; | ||
| 4615 | extra_index += @boolToInt(small.has_src_node); | ||
| 4616 | |||
| 4617 | if (small.has_tag_type) { | ||
| 4618 | extra_index += 1; | ||
| 4619 | } | ||
| 4620 | |||
| 4621 | const body_len = if (small.has_body_len) blk: { | ||
| 4622 | const body_len = zir.extra[extra_index]; | ||
| 4623 | extra_index += 1; | ||
| 4624 | break :blk body_len; | ||
| 4625 | } else 0; | ||
| 4626 | |||
| 4627 | const fields_len = if (small.has_fields_len) blk: { | ||
| 4628 | const fields_len = zir.extra[extra_index]; | ||
| 4629 | extra_index += 1; | ||
| 4630 | break :blk fields_len; | ||
| 4631 | } else 0; | ||
| 4632 | |||
| 4633 | const decls_len = if (small.has_decls_len) decls_len: { | ||
| 4634 | const decls_len = zir.extra[extra_index]; | ||
| 4635 | extra_index += 1; | ||
| 4636 | break :decls_len decls_len; | ||
| 4637 | } else 0; | ||
| 4638 | |||
| 4639 | // Skip over decls. | ||
| 4640 | var decls_it = zir.declIteratorInner(extra_index, decls_len); | ||
| 4641 | while (decls_it.next()) |_| {} | ||
| 4642 | extra_index = decls_it.extra_index; | ||
| 4643 | |||
| 4644 | const body = zir.extra[extra_index..][0..body_len]; | ||
| 4645 | if (fields_len == 0) { | ||
| 4646 | assert(body.len == 0); | ||
| 4647 | return; | ||
| 4648 | } | ||
| 4649 | extra_index += body.len; | ||
| 4650 | |||
| 4651 | var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa); | ||
| 4652 | defer union_obj.owner_decl.value_arena.?.* = decl_arena.state; | ||
| 4653 | |||
| 4654 | try union_obj.fields.ensureCapacity(&decl_arena.allocator, fields_len); | ||
| 4655 | |||
| 4656 | // We create a block for the field type instructions because they | ||
| 4657 | // may need to reference Decls from inside the struct namespace. | ||
| 4658 | // Within the field type, default value, and alignment expressions, the "owner decl" | ||
| 4659 | // should be the struct itself. Thus we need a new Sema. | ||
| 4660 | var sema: Sema = .{ | ||
| 4661 | .mod = mod, | ||
| 4662 | .gpa = gpa, | ||
| 4663 | .arena = &decl_arena.allocator, | ||
| 4664 | .code = zir, | ||
| 4665 | .owner_decl = union_obj.owner_decl, | ||
| 4666 | .namespace = &union_obj.namespace, | ||
| 4667 | .owner_func = null, | ||
| 4668 | .func = null, | ||
| 4669 | .fn_ret_ty = Type.initTag(.void), | ||
| 4670 | }; | ||
| 4671 | defer sema.deinit(); | ||
| 4672 | |||
| 4673 | var block: Scope.Block = .{ | ||
| 4674 | .parent = null, | ||
| 4675 | .sema = &sema, | ||
| 4676 | .src_decl = union_obj.owner_decl, | ||
| 4677 | .instructions = .{}, | ||
| 4678 | .inlining = null, | ||
| 4679 | .is_comptime = true, | ||
| 4680 | }; | ||
| 4681 | defer assert(block.instructions.items.len == 0); // should all be comptime instructions | ||
| 4682 | |||
| 4683 | if (body.len != 0) { | ||
| 4684 | _ = try sema.analyzeBody(&block, body); | ||
| 4685 | } | ||
| 4686 | |||
| 4687 | const bits_per_field = 4; | ||
| 4688 | const fields_per_u32 = 32 / bits_per_field; | ||
| 4689 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; | ||
| 4690 | var bit_bag_index: usize = extra_index; | ||
| 4691 | extra_index += bit_bags_count; | ||
| 4692 | var cur_bit_bag: u32 = undefined; | ||
| 4693 | var field_i: u32 = 0; | ||
| 4694 | while (field_i < fields_len) : (field_i += 1) { | ||
| 4695 | if (field_i % fields_per_u32 == 0) { | ||
| 4696 | cur_bit_bag = zir.extra[bit_bag_index]; | ||
| 4697 | bit_bag_index += 1; | ||
| 4698 | } | ||
| 4699 | const has_type = @truncate(u1, cur_bit_bag) != 0; | ||
| 4700 | cur_bit_bag >>= 1; | ||
| 4701 | const has_align = @truncate(u1, cur_bit_bag) != 0; | ||
| 4702 | cur_bit_bag >>= 1; | ||
| 4703 | const has_tag = @truncate(u1, cur_bit_bag) != 0; | ||
| 4704 | cur_bit_bag >>= 1; | ||
| 4705 | const unused = @truncate(u1, cur_bit_bag) != 0; | ||
| 4706 | cur_bit_bag >>= 1; | ||
| 4707 | _ = unused; | ||
| 4708 | |||
| 4709 | const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); | ||
| 4710 | extra_index += 1; | ||
| 4711 | |||
| 4712 | const field_type_ref: Zir.Inst.Ref = if (has_type) blk: { | ||
| 4713 | const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | ||
| 4714 | extra_index += 1; | ||
| 4715 | break :blk field_type_ref; | ||
| 4716 | } else .none; | ||
| 4717 | |||
| 4718 | const align_ref: Zir.Inst.Ref = if (has_align) blk: { | ||
| 4719 | const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | ||
| 4720 | extra_index += 1; | ||
| 4721 | break :blk align_ref; | ||
| 4722 | } else .none; | ||
| 4723 | |||
| 4724 | if (has_tag) { | ||
| 4725 | extra_index += 1; | ||
| 4726 | } | ||
| 4727 | |||
| 4728 | // This string needs to outlive the ZIR code. | ||
| 4729 | const field_name = try decl_arena.allocator.dupe(u8, field_name_zir); | ||
| 4730 | const field_ty: Type = if (field_type_ref == .none) | ||
| 4731 | Type.initTag(.void) | ||
| 4732 | else | ||
| 4733 | // TODO: if we need to report an error here, use a source location | ||
| 4734 | // that points to this type expression rather than the union. | ||
| 4735 | // But only resolve the source location if we need to emit a compile error. | ||
| 4736 | try sema.resolveType(&block, src, field_type_ref); | ||
| 4737 | |||
| 4738 | const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); | ||
| 4739 | assert(!gop.found_existing); | ||
| 4740 | gop.value_ptr.* = .{ | ||
| 4741 | .ty = field_ty, | ||
| 4742 | .abi_align = Value.initTag(.abi_align_default), | ||
| 4743 | }; | ||
| 4744 | |||
| 4745 | if (align_ref != .none) { | ||
| 4746 | // TODO: if we need to report an error here, use a source location | ||
| 4747 | // that points to this alignment expression rather than the struct. | ||
| 4748 | // But only resolve the source location if we need to emit a compile error. | ||
| 4749 | gop.value_ptr.abi_align = (try sema.resolveInstConst(&block, src, align_ref)).val; | ||
| 4750 | } | ||
| 4751 | } | ||
| 4752 | |||
| 4753 | // TODO resolve the union tag_type_ref | ||
| 4754 | } | ||
| 4755 | |||
| 4756 | /// Called from `performAllTheWork`, after all AstGen workers have finished, | 4466 | /// Called from `performAllTheWork`, after all AstGen workers have finished, |
| 4757 | /// and before the main semantic analysis loop begins. | 4467 | /// and before the main semantic analysis loop begins. |
| 4758 | pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { | 4468 | pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { |
src/Sema.zig+576-149| ... | @@ -1032,25 +1032,27 @@ fn zirEnumDecl( | ... | @@ -1032,25 +1032,27 @@ fn zirEnumDecl( |
| 1032 | // We create a block for the field type instructions because they | 1032 | // We create a block for the field type instructions because they |
| 1033 | // may need to reference Decls from inside the enum namespace. | 1033 | // may need to reference Decls from inside the enum namespace. |
| 1034 | // Within the field type, default value, and alignment expressions, the "owner decl" | 1034 | // Within the field type, default value, and alignment expressions, the "owner decl" |
| 1035 | // should be the enum itself. Thus we need a new Sema. | 1035 | // should be the enum itself. |
| 1036 | var enum_sema: Sema = .{ | 1036 | |
| 1037 | .mod = mod, | 1037 | const prev_owner_decl = sema.owner_decl; |
| 1038 | .gpa = gpa, | 1038 | sema.owner_decl = new_decl; |
| 1039 | .arena = &new_decl_arena.allocator, | 1039 | defer sema.owner_decl = prev_owner_decl; |
| 1040 | .code = sema.code, | 1040 | |
| 1041 | .inst_map = sema.inst_map, | 1041 | const prev_namespace = sema.namespace; |
| 1042 | .owner_decl = new_decl, | 1042 | sema.namespace = &enum_obj.namespace; |
| 1043 | .namespace = &enum_obj.namespace, | 1043 | defer sema.namespace = prev_namespace; |
| 1044 | .owner_func = null, | 1044 | |
| 1045 | .func = null, | 1045 | const prev_owner_func = sema.owner_func; |
| 1046 | .fn_ret_ty = Type.initTag(.void), | 1046 | sema.owner_func = null; |
| 1047 | .branch_quota = sema.branch_quota, | 1047 | defer sema.owner_func = prev_owner_func; |
| 1048 | .branch_count = sema.branch_count, | 1048 | |
| 1049 | }; | 1049 | const prev_func = sema.func; |
| 1050 | sema.func = null; | ||
| 1051 | defer sema.func = prev_func; | ||
| 1050 | 1052 | ||
| 1051 | var enum_block: Scope.Block = .{ | 1053 | var enum_block: Scope.Block = .{ |
| 1052 | .parent = null, | 1054 | .parent = null, |
| 1053 | .sema = &enum_sema, | 1055 | .sema = sema, |
| 1054 | .src_decl = new_decl, | 1056 | .src_decl = new_decl, |
| 1055 | .instructions = .{}, | 1057 | .instructions = .{}, |
| 1056 | .inlining = null, | 1058 | .inlining = null, |
| ... | @@ -1059,11 +1061,8 @@ fn zirEnumDecl( | ... | @@ -1059,11 +1061,8 @@ fn zirEnumDecl( |
| 1059 | defer assert(enum_block.instructions.items.len == 0); // should all be comptime instructions | 1061 | defer assert(enum_block.instructions.items.len == 0); // should all be comptime instructions |
| 1060 | 1062 | ||
| 1061 | if (body.len != 0) { | 1063 | if (body.len != 0) { |
| 1062 | _ = try enum_sema.analyzeBody(&enum_block, body); | 1064 | _ = try sema.analyzeBody(&enum_block, body); |
| 1063 | } | 1065 | } |
| 1064 | |||
| 1065 | sema.branch_count = enum_sema.branch_count; | ||
| 1066 | sema.branch_quota = enum_sema.branch_quota; | ||
| 1067 | } | 1066 | } |
| 1068 | var bit_bag_index: usize = body_end; | 1067 | var bit_bag_index: usize = body_end; |
| 1069 | var cur_bit_bag: u32 = undefined; | 1068 | var cur_bit_bag: u32 = undefined; |
| ... | @@ -1466,8 +1465,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde | ... | @@ -1466,8 +1465,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde |
| 1466 | const ptr = sema.resolveInst(inst_data.operand); | 1465 | const ptr = sema.resolveInst(inst_data.operand); |
| 1467 | const ptr_inst = Air.refToIndex(ptr).?; | 1466 | const ptr_inst = Air.refToIndex(ptr).?; |
| 1468 | assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant); | 1467 | assert(sema.air_instructions.items(.tag)[ptr_inst] == .constant); |
| 1469 | const air_datas = sema.air_instructions.items(.data); | 1468 | const value_index = sema.air_instructions.items(.data)[ptr_inst].ty_pl.payload; |
| 1470 | const value_index = air_datas[ptr_inst].ty_pl.payload; | ||
| 1471 | const ptr_val = sema.air_values.items[value_index]; | 1469 | const ptr_val = sema.air_values.items[value_index]; |
| 1472 | const var_is_mut = switch (sema.typeOf(ptr).tag()) { | 1470 | const var_is_mut = switch (sema.typeOf(ptr).tag()) { |
| 1473 | .inferred_alloc_const => false, | 1471 | .inferred_alloc_const => false, |
| ... | @@ -1481,7 +1479,8 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde | ... | @@ -1481,7 +1479,8 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Inde |
| 1481 | 1479 | ||
| 1482 | const final_elem_ty = try decl.ty.copy(sema.arena); | 1480 | const final_elem_ty = try decl.ty.copy(sema.arena); |
| 1483 | const final_ptr_ty = try Module.simplePtrType(sema.arena, final_elem_ty, true, .One); | 1481 | const final_ptr_ty = try Module.simplePtrType(sema.arena, final_elem_ty, true, .One); |
| 1484 | air_datas[ptr_inst].ty_pl.ty = try sema.addType(final_ptr_ty); | 1482 | const final_ptr_ty_inst = try sema.addType(final_ptr_ty); |
| 1483 | sema.air_instructions.items(.data)[ptr_inst].ty_pl.ty = final_ptr_ty_inst; | ||
| 1485 | 1484 | ||
| 1486 | if (var_is_mut) { | 1485 | if (var_is_mut) { |
| 1487 | sema.air_values.items[value_index] = try Value.Tag.decl_ref_mut.create(sema.arena, .{ | 1486 | 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 | ... | @@ -5329,10 +5328,16 @@ fn zirShr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!A |
| 5329 | 5328 | ||
| 5330 | if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { | 5329 | if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { |
| 5331 | if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { | 5330 | if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { |
| 5331 | const lhs_ty = sema.typeOf(lhs); | ||
| 5332 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | 5332 | if (lhs_val.isUndef() or rhs_val.isUndef()) { |
| 5333 | return sema.addConstUndef(sema.typeOf(lhs)); | 5333 | return sema.addConstUndef(lhs_ty); |
| 5334 | } | ||
| 5335 | // If rhs is 0, return lhs without doing any calculations. | ||
| 5336 | if (rhs_val.compareWithZero(.eq)) { | ||
| 5337 | return sema.addConstant(lhs_ty, lhs_val); | ||
| 5334 | } | 5338 | } |
| 5335 | return sema.mod.fail(&block.base, src, "TODO implement comptime shr", .{}); | 5339 | const val = try lhs_val.shr(rhs_val, sema.arena); |
| 5340 | return sema.addConstant(lhs_ty, val); | ||
| 5336 | } | 5341 | } |
| 5337 | } | 5342 | } |
| 5338 | 5343 | ||
| ... | @@ -6008,6 +6013,28 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr | ... | @@ -6008,6 +6013,28 @@ fn zirTypeInfo(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 6008 | }), | 6013 | }), |
| 6009 | ); | 6014 | ); |
| 6010 | }, | 6015 | }, |
| 6016 | .Int => { | ||
| 6017 | const info = ty.intInfo(target); | ||
| 6018 | const field_values = try sema.arena.alloc(Value, 2); | ||
| 6019 | // signedness: Signedness, | ||
| 6020 | field_values[0] = try Value.Tag.enum_field_index.create( | ||
| 6021 | sema.arena, | ||
| 6022 | @enumToInt(info.signedness), | ||
| 6023 | ); | ||
| 6024 | // bits: comptime_int, | ||
| 6025 | field_values[1] = try Value.Tag.int_u64.create(sema.arena, info.bits); | ||
| 6026 | |||
| 6027 | return sema.addConstant( | ||
| 6028 | type_info_ty, | ||
| 6029 | try Value.Tag.@"union".create(sema.arena, .{ | ||
| 6030 | .tag = try Value.Tag.enum_field_index.create( | ||
| 6031 | sema.arena, | ||
| 6032 | @enumToInt(@typeInfo(std.builtin.TypeInfo).Union.tag_type.?.Int), | ||
| 6033 | ), | ||
| 6034 | .val = try Value.Tag.@"struct".create(sema.arena, field_values.ptr), | ||
| 6035 | }), | ||
| 6036 | ); | ||
| 6037 | }, | ||
| 6011 | else => |t| return sema.mod.fail(&block.base, src, "TODO: implement zirTypeInfo for {s}", .{ | 6038 | else => |t| return sema.mod.fail(&block.base, src, "TODO: implement zirTypeInfo for {s}", .{ |
| 6012 | @tagName(t), | 6039 | @tagName(t), |
| 6013 | }), | 6040 | }), |
| ... | @@ -6047,20 +6074,24 @@ fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compil | ... | @@ -6047,20 +6074,24 @@ fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compil |
| 6047 | } | 6074 | } |
| 6048 | 6075 | ||
| 6049 | fn log2IntType(sema: *Sema, block: *Scope.Block, operand: Type, src: LazySrcLoc) CompileError!Air.Inst.Ref { | 6076 | fn log2IntType(sema: *Sema, block: *Scope.Block, operand: Type, src: LazySrcLoc) CompileError!Air.Inst.Ref { |
| 6050 | if (operand.zigTypeTag() != .Int) return sema.mod.fail( | 6077 | switch (operand.zigTypeTag()) { |
| 6051 | &block.base, | 6078 | .ComptimeInt => return Air.Inst.Ref.comptime_int_type, |
| 6052 | src, | 6079 | .Int => { |
| 6053 | "bit shifting operation expected integer type, found '{}'", | 6080 | var count: u16 = 0; |
| 6054 | .{operand}, | 6081 | var s = operand.bitSize(sema.mod.getTarget()) - 1; |
| 6055 | ); | 6082 | while (s != 0) : (s >>= 1) { |
| 6056 | 6083 | count += 1; | |
| 6057 | var count: u16 = 0; | 6084 | } |
| 6058 | var s = operand.bitSize(sema.mod.getTarget()) - 1; | 6085 | const res = try Module.makeIntType(sema.arena, .unsigned, count); |
| 6059 | while (s != 0) : (s >>= 1) { | 6086 | return sema.addType(res); |
| 6060 | count += 1; | 6087 | }, |
| 6088 | else => return sema.mod.fail( | ||
| 6089 | &block.base, | ||
| 6090 | src, | ||
| 6091 | "bit shifting operation expected integer type, found '{}'", | ||
| 6092 | .{operand}, | ||
| 6093 | ), | ||
| 6061 | } | 6094 | } |
| 6062 | const res = try Module.makeIntType(sema.arena, .unsigned, count); | ||
| 6063 | return sema.addType(res); | ||
| 6064 | } | 6095 | } |
| 6065 | 6096 | ||
| 6066 | fn zirTypeofPeer( | 6097 | fn zirTypeofPeer( |
| ... | @@ -6517,99 +6548,134 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: | ... | @@ -6517,99 +6548,134 @@ fn zirStructInit(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: |
| 6517 | const first_field_type_data = zir_datas[first_item.field_type].pl_node; | 6548 | const first_field_type_data = zir_datas[first_item.field_type].pl_node; |
| 6518 | const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data; | 6549 | const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data; |
| 6519 | const unresolved_struct_type = try sema.resolveType(block, src, first_field_type_extra.container_type); | 6550 | const unresolved_struct_type = try sema.resolveType(block, src, first_field_type_extra.container_type); |
| 6520 | const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type); | 6551 | const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type); |
| 6521 | const struct_obj = struct_ty.castTag(.@"struct").?.data; | 6552 | |
| 6522 | 6553 | if (resolved_ty.castTag(.@"struct")) |struct_payload| { | |
| 6523 | // Maps field index to field_type index of where it was already initialized. | 6554 | const struct_obj = struct_payload.data; |
| 6524 | // For making sure all fields are accounted for and no fields are duplicated. | 6555 | |
| 6525 | const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count()); | 6556 | // Maps field index to field_type index of where it was already initialized. |
| 6526 | defer gpa.free(found_fields); | 6557 | // For making sure all fields are accounted for and no fields are duplicated. |
| 6527 | mem.set(Zir.Inst.Index, found_fields, 0); | 6558 | const found_fields = try gpa.alloc(Zir.Inst.Index, struct_obj.fields.count()); |
| 6528 | 6559 | defer gpa.free(found_fields); | |
| 6529 | // The init values to use for the struct instance. | 6560 | mem.set(Zir.Inst.Index, found_fields, 0); |
| 6530 | const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count()); | 6561 | |
| 6531 | defer gpa.free(field_inits); | 6562 | // The init values to use for the struct instance. |
| 6563 | const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count()); | ||
| 6564 | defer gpa.free(field_inits); | ||
| 6565 | |||
| 6566 | var field_i: u32 = 0; | ||
| 6567 | var extra_index = extra.end; | ||
| 6568 | |||
| 6569 | while (field_i < extra.data.fields_len) : (field_i += 1) { | ||
| 6570 | const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index); | ||
| 6571 | extra_index = item.end; | ||
| 6572 | |||
| 6573 | const field_type_data = zir_datas[item.data.field_type].pl_node; | ||
| 6574 | const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node }; | ||
| 6575 | const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; | ||
| 6576 | const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); | ||
| 6577 | const field_index = struct_obj.fields.getIndex(field_name) orelse | ||
| 6578 | return sema.failWithBadFieldAccess(block, struct_obj, field_src, field_name); | ||
| 6579 | if (found_fields[field_index] != 0) { | ||
| 6580 | const other_field_type = found_fields[field_index]; | ||
| 6581 | const other_field_type_data = zir_datas[other_field_type].pl_node; | ||
| 6582 | const other_field_src: LazySrcLoc = .{ .node_offset_back2tok = other_field_type_data.src_node }; | ||
| 6583 | const msg = msg: { | ||
| 6584 | const msg = try mod.errMsg(&block.base, field_src, "duplicate field", .{}); | ||
| 6585 | errdefer msg.destroy(gpa); | ||
| 6586 | try mod.errNote(&block.base, other_field_src, msg, "other field here", .{}); | ||
| 6587 | break :msg msg; | ||
| 6588 | }; | ||
| 6589 | return mod.failWithOwnedErrorMsg(&block.base, msg); | ||
| 6590 | } | ||
| 6591 | found_fields[field_index] = item.data.field_type; | ||
| 6592 | field_inits[field_index] = sema.resolveInst(item.data.init); | ||
| 6593 | } | ||
| 6532 | 6594 | ||
| 6533 | var field_i: u32 = 0; | 6595 | var root_msg: ?*Module.ErrorMsg = null; |
| 6534 | var extra_index = extra.end; | ||
| 6535 | 6596 | ||
| 6536 | while (field_i < extra.data.fields_len) : (field_i += 1) { | 6597 | for (found_fields) |field_type_inst, i| { |
| 6537 | const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index); | 6598 | if (field_type_inst != 0) continue; |
| 6538 | extra_index = item.end; | ||
| 6539 | 6599 | ||
| 6540 | const field_type_data = zir_datas[item.data.field_type].pl_node; | 6600 | // Check if the field has a default init. |
| 6541 | const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node }; | 6601 | const field = struct_obj.fields.values()[i]; |
| 6542 | const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; | 6602 | if (field.default_val.tag() == .unreachable_value) { |
| 6543 | const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); | 6603 | const field_name = struct_obj.fields.keys()[i]; |
| 6544 | const field_index = struct_obj.fields.getIndex(field_name) orelse | 6604 | const template = "missing struct field: {s}"; |
| 6545 | return sema.failWithBadFieldAccess(block, struct_obj, field_src, field_name); | 6605 | const args = .{field_name}; |
| 6546 | if (found_fields[field_index] != 0) { | 6606 | if (root_msg) |msg| { |
| 6547 | const other_field_type = found_fields[field_index]; | 6607 | try mod.errNote(&block.base, src, msg, template, args); |
| 6548 | const other_field_type_data = zir_datas[other_field_type].pl_node; | 6608 | } else { |
| 6549 | const other_field_src: LazySrcLoc = .{ .node_offset_back2tok = other_field_type_data.src_node }; | 6609 | root_msg = try mod.errMsg(&block.base, src, template, args); |
| 6550 | const msg = msg: { | 6610 | } |
| 6551 | const msg = try mod.errMsg(&block.base, field_src, "duplicate field", .{}); | 6611 | } else { |
| 6552 | errdefer msg.destroy(gpa); | 6612 | field_inits[i] = try sema.addConstant(field.ty, field.default_val); |
| 6553 | try mod.errNote(&block.base, other_field_src, msg, "other field here", .{}); | 6613 | } |
| 6554 | break :msg msg; | 6614 | } |
| 6555 | }; | 6615 | if (root_msg) |msg| { |
| 6616 | const fqn = try struct_obj.getFullyQualifiedName(gpa); | ||
| 6617 | defer gpa.free(fqn); | ||
| 6618 | try mod.errNoteNonLazy( | ||
| 6619 | struct_obj.srcLoc(), | ||
| 6620 | msg, | ||
| 6621 | "struct '{s}' declared here", | ||
| 6622 | .{fqn}, | ||
| 6623 | ); | ||
| 6556 | return mod.failWithOwnedErrorMsg(&block.base, msg); | 6624 | return mod.failWithOwnedErrorMsg(&block.base, msg); |
| 6557 | } | 6625 | } |
| 6558 | found_fields[field_index] = item.data.field_type; | ||
| 6559 | field_inits[field_index] = sema.resolveInst(item.data.init); | ||
| 6560 | } | ||
| 6561 | 6626 | ||
| 6562 | var root_msg: ?*Module.ErrorMsg = null; | 6627 | if (is_ref) { |
| 6628 | return mod.fail(&block.base, src, "TODO: Sema.zirStructInit is_ref=true", .{}); | ||
| 6629 | } | ||
| 6563 | 6630 | ||
| 6564 | for (found_fields) |field_type_inst, i| { | 6631 | const is_comptime = for (field_inits) |field_init| { |
| 6565 | if (field_type_inst != 0) continue; | 6632 | if (!(try sema.isComptimeKnown(block, src, field_init))) { |
| 6566 | 6633 | break false; | |
| 6567 | // Check if the field has a default init. | ||
| 6568 | const field = struct_obj.fields.values()[i]; | ||
| 6569 | if (field.default_val.tag() == .unreachable_value) { | ||
| 6570 | const field_name = struct_obj.fields.keys()[i]; | ||
| 6571 | const template = "missing struct field: {s}"; | ||
| 6572 | const args = .{field_name}; | ||
| 6573 | if (root_msg) |msg| { | ||
| 6574 | try mod.errNote(&block.base, src, msg, template, args); | ||
| 6575 | } else { | ||
| 6576 | root_msg = try mod.errMsg(&block.base, src, template, args); | ||
| 6577 | } | 6634 | } |
| 6578 | } else { | 6635 | } else true; |
| 6579 | field_inits[i] = try sema.addConstant(field.ty, field.default_val); | 6636 | |
| 6637 | if (is_comptime) { | ||
| 6638 | const values = try sema.arena.alloc(Value, field_inits.len); | ||
| 6639 | for (field_inits) |field_init, i| { | ||
| 6640 | values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?; | ||
| 6641 | } | ||
| 6642 | return sema.addConstant(resolved_ty, try Value.Tag.@"struct".create(sema.arena, values.ptr)); | ||
| 6580 | } | 6643 | } |
| 6581 | } | ||
| 6582 | if (root_msg) |msg| { | ||
| 6583 | const fqn = try struct_obj.getFullyQualifiedName(gpa); | ||
| 6584 | defer gpa.free(fqn); | ||
| 6585 | try mod.errNoteNonLazy( | ||
| 6586 | struct_obj.srcLoc(), | ||
| 6587 | msg, | ||
| 6588 | "struct '{s}' declared here", | ||
| 6589 | .{fqn}, | ||
| 6590 | ); | ||
| 6591 | return mod.failWithOwnedErrorMsg(&block.base, msg); | ||
| 6592 | } | ||
| 6593 | 6644 | ||
| 6594 | if (is_ref) { | 6645 | return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{}); |
| 6595 | return mod.fail(&block.base, src, "TODO: Sema.zirStructInit is_ref=true", .{}); | 6646 | } else if (resolved_ty.cast(Type.Payload.Union)) |union_payload| { |
| 6596 | } | 6647 | const union_obj = union_payload.data; |
| 6597 | 6648 | ||
| 6598 | const is_comptime = for (field_inits) |field_init| { | 6649 | if (extra.data.fields_len != 1) { |
| 6599 | if (!(try sema.isComptimeKnown(block, src, field_init))) { | 6650 | return sema.mod.fail(&block.base, src, "union initialization expects exactly one field", .{}); |
| 6600 | break false; | ||
| 6601 | } | 6651 | } |
| 6602 | } else true; | ||
| 6603 | 6652 | ||
| 6604 | if (is_comptime) { | 6653 | const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end); |
| 6605 | const values = try sema.arena.alloc(Value, field_inits.len); | 6654 | |
| 6606 | for (field_inits) |field_init, i| { | 6655 | const field_type_data = zir_datas[item.data.field_type].pl_node; |
| 6607 | values[i] = (sema.resolveMaybeUndefVal(block, src, field_init) catch unreachable).?; | 6656 | const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node }; |
| 6657 | const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; | ||
| 6658 | const field_name = sema.code.nullTerminatedString(field_type_extra.name_start); | ||
| 6659 | const field_index = union_obj.fields.getIndex(field_name) orelse | ||
| 6660 | return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); | ||
| 6661 | |||
| 6662 | if (is_ref) { | ||
| 6663 | return mod.fail(&block.base, src, "TODO: Sema.zirStructInit is_ref=true union", .{}); | ||
| 6608 | } | 6664 | } |
| 6609 | return sema.addConstant(struct_ty, try Value.Tag.@"struct".create(sema.arena, values.ptr)); | ||
| 6610 | } | ||
| 6611 | 6665 | ||
| 6612 | return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known struct values", .{}); | 6666 | const init_inst = sema.resolveInst(item.data.init); |
| 6667 | if (try sema.resolveMaybeUndefVal(block, field_src, init_inst)) |val| { | ||
| 6668 | return sema.addConstant( | ||
| 6669 | resolved_ty, | ||
| 6670 | try Value.Tag.@"union".create(sema.arena, .{ | ||
| 6671 | .tag = try Value.Tag.int_u64.create(sema.arena, field_index), | ||
| 6672 | .val = val, | ||
| 6673 | }), | ||
| 6674 | ); | ||
| 6675 | } | ||
| 6676 | return mod.fail(&block.base, src, "TODO: Sema.zirStructInit for runtime-known union values", .{}); | ||
| 6677 | } | ||
| 6678 | unreachable; | ||
| 6613 | } | 6679 | } |
| 6614 | 6680 | ||
| 6615 | fn zirStructInitAnon(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { | 6681 | 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 | ... | @@ -6647,17 +6713,25 @@ fn zirFieldType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE |
| 6647 | const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; | 6713 | const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; |
| 6648 | const src = inst_data.src(); | 6714 | const src = inst_data.src(); |
| 6649 | const field_name = sema.code.nullTerminatedString(extra.name_start); | 6715 | const field_name = sema.code.nullTerminatedString(extra.name_start); |
| 6650 | const unresolved_struct_type = try sema.resolveType(block, src, extra.container_type); | 6716 | const unresolved_ty = try sema.resolveType(block, src, extra.container_type); |
| 6651 | if (unresolved_struct_type.zigTypeTag() != .Struct) { | 6717 | const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_ty); |
| 6652 | return sema.mod.fail(&block.base, src, "expected struct; found '{}'", .{ | 6718 | switch (resolved_ty.zigTypeTag()) { |
| 6653 | unresolved_struct_type, | 6719 | .Struct => { |
| 6654 | }); | 6720 | const struct_obj = resolved_ty.castTag(.@"struct").?.data; |
| 6721 | const field = struct_obj.fields.get(field_name) orelse | ||
| 6722 | return sema.failWithBadFieldAccess(block, struct_obj, src, field_name); | ||
| 6723 | return sema.addType(field.ty); | ||
| 6724 | }, | ||
| 6725 | .Union => { | ||
| 6726 | const union_obj = resolved_ty.cast(Type.Payload.Union).?.data; | ||
| 6727 | const field = union_obj.fields.get(field_name) orelse | ||
| 6728 | return sema.failWithBadUnionFieldAccess(block, union_obj, src, field_name); | ||
| 6729 | return sema.addType(field.ty); | ||
| 6730 | }, | ||
| 6731 | else => return sema.mod.fail(&block.base, src, "expected struct or union; found '{}'", .{ | ||
| 6732 | resolved_ty, | ||
| 6733 | }), | ||
| 6655 | } | 6734 | } |
| 6656 | const struct_ty = try sema.resolveTypeFields(block, src, unresolved_struct_type); | ||
| 6657 | const struct_obj = struct_ty.castTag(.@"struct").?.data; | ||
| 6658 | const field = struct_obj.fields.get(field_name) orelse | ||
| 6659 | return sema.failWithBadFieldAccess(block, struct_obj, src, field_name); | ||
| 6660 | return sema.addType(field.ty); | ||
| 6661 | } | 6735 | } |
| 6662 | 6736 | ||
| 6663 | fn zirErrorReturnTrace( | 6737 | fn zirErrorReturnTrace( |
| ... | @@ -6732,7 +6806,54 @@ fn zirTagName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr | ... | @@ -6732,7 +6806,54 @@ fn zirTagName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr |
| 6732 | fn zirReify(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 6806 | fn zirReify(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 6733 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 6807 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 6734 | const src = inst_data.src(); | 6808 | const src = inst_data.src(); |
| 6735 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify", .{}); | 6809 | const type_info_ty = try sema.getBuiltinType(block, src, "TypeInfo"); |
| 6810 | const uncasted_operand = sema.resolveInst(inst_data.operand); | ||
| 6811 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 6812 | const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); | ||
| 6813 | const val = try sema.resolveConstValue(block, operand_src, type_info); | ||
| 6814 | const union_val = val.cast(Value.Payload.Union).?.data; | ||
| 6815 | const TypeInfoTag = std.meta.Tag(std.builtin.TypeInfo); | ||
| 6816 | const tag_index = @intCast(std.meta.Tag(TypeInfoTag), union_val.tag.toUnsignedInt()); | ||
| 6817 | switch (@intToEnum(std.builtin.TypeId, tag_index)) { | ||
| 6818 | .Type => return Air.Inst.Ref.type_type, | ||
| 6819 | .Void => return Air.Inst.Ref.void_type, | ||
| 6820 | .Bool => return Air.Inst.Ref.bool_type, | ||
| 6821 | .NoReturn => return Air.Inst.Ref.noreturn_type, | ||
| 6822 | .Int => { | ||
| 6823 | const struct_val = union_val.val.castTag(.@"struct").?.data; | ||
| 6824 | // TODO use reflection instead of magic numbers here | ||
| 6825 | const signedness_val = struct_val[0]; | ||
| 6826 | const bits_val = struct_val[1]; | ||
| 6827 | |||
| 6828 | const signedness = signedness_val.toEnum(std.builtin.Signedness); | ||
| 6829 | const bits = @intCast(u16, bits_val.toUnsignedInt()); | ||
| 6830 | const ty = switch (signedness) { | ||
| 6831 | .signed => try Type.Tag.int_signed.create(sema.arena, bits), | ||
| 6832 | .unsigned => try Type.Tag.int_unsigned.create(sema.arena, bits), | ||
| 6833 | }; | ||
| 6834 | return sema.addType(ty); | ||
| 6835 | }, | ||
| 6836 | .Float => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Float", .{}), | ||
| 6837 | .Pointer => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Pointer", .{}), | ||
| 6838 | .Array => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Array", .{}), | ||
| 6839 | .Struct => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Struct", .{}), | ||
| 6840 | .ComptimeFloat => return Air.Inst.Ref.comptime_float_type, | ||
| 6841 | .ComptimeInt => return Air.Inst.Ref.comptime_int_type, | ||
| 6842 | .Undefined => return Air.Inst.Ref.undefined_type, | ||
| 6843 | .Null => return Air.Inst.Ref.null_type, | ||
| 6844 | .Optional => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Optional", .{}), | ||
| 6845 | .ErrorUnion => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for ErrorUnion", .{}), | ||
| 6846 | .ErrorSet => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for ErrorSet", .{}), | ||
| 6847 | .Enum => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Enum", .{}), | ||
| 6848 | .Union => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Union", .{}), | ||
| 6849 | .Fn => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Fn", .{}), | ||
| 6850 | .BoundFn => @panic("TODO delete BoundFn from the language"), | ||
| 6851 | .Opaque => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Opaque", .{}), | ||
| 6852 | .Frame => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Frame", .{}), | ||
| 6853 | .AnyFrame => return Air.Inst.Ref.anyframe_type, | ||
| 6854 | .Vector => return sema.mod.fail(&block.base, src, "TODO: Sema.zirReify for Vector", .{}), | ||
| 6855 | .EnumLiteral => return Air.Inst.Ref.enum_literal_type, | ||
| 6856 | } | ||
| 6736 | } | 6857 | } |
| 6737 | 6858 | ||
| 6738 | fn zirTypeName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 6859 | fn zirTypeName(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -8152,24 +8273,35 @@ fn elemPtrArray( | ... | @@ -8152,24 +8273,35 @@ fn elemPtrArray( |
| 8152 | elem_index: Air.Inst.Ref, | 8273 | elem_index: Air.Inst.Ref, |
| 8153 | elem_index_src: LazySrcLoc, | 8274 | elem_index_src: LazySrcLoc, |
| 8154 | ) CompileError!Air.Inst.Ref { | 8275 | ) CompileError!Air.Inst.Ref { |
| 8276 | const array_ptr_ty = sema.typeOf(array_ptr); | ||
| 8277 | const pointee_type = array_ptr_ty.elemType().elemType(); | ||
| 8278 | const result_ty = if (array_ptr_ty.ptrIsMutable()) | ||
| 8279 | try Type.Tag.single_mut_pointer.create(sema.arena, pointee_type) | ||
| 8280 | else | ||
| 8281 | try Type.Tag.single_const_pointer.create(sema.arena, pointee_type); | ||
| 8282 | |||
| 8155 | if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| { | 8283 | if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| { |
| 8156 | if (try sema.resolveDefinedValue(block, src, elem_index)) |index_val| { | 8284 | if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| { |
| 8157 | // Both array pointer and index are compile-time known. | 8285 | // Both array pointer and index are compile-time known. |
| 8158 | const index_u64 = index_val.toUnsignedInt(); | 8286 | const index_u64 = index_val.toUnsignedInt(); |
| 8159 | // @intCast here because it would have been impossible to construct a value that | 8287 | // @intCast here because it would have been impossible to construct a value that |
| 8160 | // required a larger index. | 8288 | // required a larger index. |
| 8161 | const elem_ptr = try array_ptr_val.elemPtr(sema.arena, @intCast(usize, index_u64)); | 8289 | const elem_ptr = try array_ptr_val.elemPtr(sema.arena, @intCast(usize, index_u64)); |
| 8162 | const pointee_type = sema.typeOf(array_ptr).elemType().elemType(); | 8290 | return sema.addConstant(result_ty, elem_ptr); |
| 8163 | |||
| 8164 | return sema.addConstant( | ||
| 8165 | try Type.Tag.single_const_pointer.create(sema.arena, pointee_type), | ||
| 8166 | elem_ptr, | ||
| 8167 | ); | ||
| 8168 | } | 8291 | } |
| 8169 | } | 8292 | } |
| 8170 | _ = elem_index; | 8293 | // TODO safety check for array bounds |
| 8171 | _ = elem_index_src; | 8294 | try sema.requireRuntimeBlock(block, src); |
| 8172 | return sema.mod.fail(&block.base, src, "TODO implement more analyze elemptr for arrays", .{}); | 8295 | return block.addInst(.{ |
| 8296 | .tag = .ptr_elem_ptr, | ||
| 8297 | .data = .{ .ty_pl = .{ | ||
| 8298 | .ty = try sema.addType(result_ty), | ||
| 8299 | .payload = try sema.addExtra(Air.Bin{ | ||
| 8300 | .lhs = array_ptr, | ||
| 8301 | .rhs = elem_index, | ||
| 8302 | }), | ||
| 8303 | } }, | ||
| 8304 | }); | ||
| 8173 | } | 8305 | } |
| 8174 | 8306 | ||
| 8175 | fn coerce( | 8307 | fn coerce( |
| ... | @@ -9177,22 +9309,62 @@ pub fn resolveTypeLayout( | ... | @@ -9177,22 +9309,62 @@ pub fn resolveTypeLayout( |
| 9177 | } | 9309 | } |
| 9178 | } | 9310 | } |
| 9179 | 9311 | ||
| 9180 | fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!Type { | 9312 | /// `sema` and `block` are expected to be the same ones used for the `Decl`. |
| 9313 | pub fn resolveDeclFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void { | ||
| 9181 | switch (ty.tag()) { | 9314 | switch (ty.tag()) { |
| 9182 | .@"struct" => { | 9315 | .@"struct" => { |
| 9183 | const struct_obj = ty.castTag(.@"struct").?.data; | 9316 | const struct_obj = ty.castTag(.@"struct").?.data; |
| 9317 | if (struct_obj.owner_decl.namespace != sema.owner_decl.namespace) return; | ||
| 9184 | switch (struct_obj.status) { | 9318 | switch (struct_obj.status) { |
| 9185 | .none => {}, | 9319 | .none => {}, |
| 9186 | .field_types_wip => { | 9320 | .field_types_wip => { |
| 9187 | return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{ty}); | 9321 | return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{ty}); |
| 9188 | }, | 9322 | }, |
| 9189 | .have_field_types, .have_layout, .layout_wip => return ty, | 9323 | .have_field_types, .have_layout, .layout_wip => return, |
| 9190 | } | 9324 | } |
| 9325 | const prev_namespace = sema.namespace; | ||
| 9326 | sema.namespace = &struct_obj.namespace; | ||
| 9327 | defer sema.namespace = prev_namespace; | ||
| 9328 | |||
| 9191 | struct_obj.status = .field_types_wip; | 9329 | struct_obj.status = .field_types_wip; |
| 9192 | try sema.mod.analyzeStructFields(struct_obj); | 9330 | try sema.analyzeStructFields(block, struct_obj); |
| 9193 | struct_obj.status = .have_field_types; | 9331 | struct_obj.status = .have_field_types; |
| 9194 | return ty; | ||
| 9195 | }, | 9332 | }, |
| 9333 | .@"union", .union_tagged => { | ||
| 9334 | const union_obj = ty.cast(Type.Payload.Union).?.data; | ||
| 9335 | if (union_obj.owner_decl.namespace != sema.owner_decl.namespace) return; | ||
| 9336 | switch (union_obj.status) { | ||
| 9337 | .none => {}, | ||
| 9338 | .field_types_wip => { | ||
| 9339 | return sema.mod.fail(&block.base, src, "union {} depends on itself", .{ty}); | ||
| 9340 | }, | ||
| 9341 | .have_field_types, .have_layout, .layout_wip => return, | ||
| 9342 | } | ||
| 9343 | const prev_namespace = sema.namespace; | ||
| 9344 | sema.namespace = &union_obj.namespace; | ||
| 9345 | defer sema.namespace = prev_namespace; | ||
| 9346 | |||
| 9347 | union_obj.status = .field_types_wip; | ||
| 9348 | try sema.analyzeUnionFields(block, union_obj); | ||
| 9349 | union_obj.status = .have_field_types; | ||
| 9350 | }, | ||
| 9351 | else => return, | ||
| 9352 | } | ||
| 9353 | } | ||
| 9354 | |||
| 9355 | fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type) CompileError!Type { | ||
| 9356 | switch (ty.tag()) { | ||
| 9357 | .@"struct" => { | ||
| 9358 | const struct_obj = ty.castTag(.@"struct").?.data; | ||
| 9359 | switch (struct_obj.status) { | ||
| 9360 | .none => unreachable, | ||
| 9361 | .field_types_wip => { | ||
| 9362 | return sema.mod.fail(&block.base, src, "struct {} depends on itself", .{ty}); | ||
| 9363 | }, | ||
| 9364 | .have_field_types, .have_layout, .layout_wip => return ty, | ||
| 9365 | } | ||
| 9366 | }, | ||
| 9367 | .type_info => return sema.resolveBuiltinTypeFields(block, src, "TypeInfo"), | ||
| 9196 | .extern_options => return sema.resolveBuiltinTypeFields(block, src, "ExternOptions"), | 9368 | .extern_options => return sema.resolveBuiltinTypeFields(block, src, "ExternOptions"), |
| 9197 | .export_options => return sema.resolveBuiltinTypeFields(block, src, "ExportOptions"), | 9369 | .export_options => return sema.resolveBuiltinTypeFields(block, src, "ExportOptions"), |
| 9198 | .atomic_ordering => return sema.resolveBuiltinTypeFields(block, src, "AtomicOrdering"), | 9370 | .atomic_ordering => return sema.resolveBuiltinTypeFields(block, src, "AtomicOrdering"), |
| ... | @@ -9205,18 +9377,12 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type | ... | @@ -9205,18 +9377,12 @@ fn resolveTypeFields(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ty: Type |
| 9205 | .@"union", .union_tagged => { | 9377 | .@"union", .union_tagged => { |
| 9206 | const union_obj = ty.cast(Type.Payload.Union).?.data; | 9378 | const union_obj = ty.cast(Type.Payload.Union).?.data; |
| 9207 | switch (union_obj.status) { | 9379 | switch (union_obj.status) { |
| 9208 | .none => {}, | 9380 | .none => unreachable, |
| 9209 | .field_types_wip => { | 9381 | .field_types_wip => { |
| 9210 | return sema.mod.fail(&block.base, src, "union {} depends on itself", .{ | 9382 | return sema.mod.fail(&block.base, src, "union {} depends on itself", .{ty}); |
| 9211 | ty, | ||
| 9212 | }); | ||
| 9213 | }, | 9383 | }, |
| 9214 | .have_field_types, .have_layout, .layout_wip => return ty, | 9384 | .have_field_types, .have_layout, .layout_wip => return ty, |
| 9215 | } | 9385 | } |
| 9216 | union_obj.status = .field_types_wip; | ||
| 9217 | try sema.mod.analyzeUnionFields(union_obj); | ||
| 9218 | union_obj.status = .have_field_types; | ||
| 9219 | return ty; | ||
| 9220 | }, | 9386 | }, |
| 9221 | else => return ty, | 9387 | else => return ty, |
| 9222 | } | 9388 | } |
| ... | @@ -9232,6 +9398,265 @@ fn resolveBuiltinTypeFields( | ... | @@ -9232,6 +9398,265 @@ fn resolveBuiltinTypeFields( |
| 9232 | return sema.resolveTypeFields(block, src, resolved_ty); | 9398 | return sema.resolveTypeFields(block, src, resolved_ty); |
| 9233 | } | 9399 | } |
| 9234 | 9400 | ||
| 9401 | fn analyzeStructFields( | ||
| 9402 | sema: *Sema, | ||
| 9403 | block: *Scope.Block, | ||
| 9404 | struct_obj: *Module.Struct, | ||
| 9405 | ) CompileError!void { | ||
| 9406 | const tracy = trace(@src()); | ||
| 9407 | defer tracy.end(); | ||
| 9408 | |||
| 9409 | const gpa = sema.gpa; | ||
| 9410 | const zir = sema.code; | ||
| 9411 | const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended; | ||
| 9412 | assert(extended.opcode == .struct_decl); | ||
| 9413 | const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small); | ||
| 9414 | var extra_index: usize = extended.operand; | ||
| 9415 | |||
| 9416 | const src: LazySrcLoc = .{ .node_offset = struct_obj.node_offset }; | ||
| 9417 | extra_index += @boolToInt(small.has_src_node); | ||
| 9418 | |||
| 9419 | const body_len = if (small.has_body_len) blk: { | ||
| 9420 | const body_len = zir.extra[extra_index]; | ||
| 9421 | extra_index += 1; | ||
| 9422 | break :blk body_len; | ||
| 9423 | } else 0; | ||
| 9424 | |||
| 9425 | const fields_len = if (small.has_fields_len) blk: { | ||
| 9426 | const fields_len = zir.extra[extra_index]; | ||
| 9427 | extra_index += 1; | ||
| 9428 | break :blk fields_len; | ||
| 9429 | } else 0; | ||
| 9430 | |||
| 9431 | const decls_len = if (small.has_decls_len) decls_len: { | ||
| 9432 | const decls_len = zir.extra[extra_index]; | ||
| 9433 | extra_index += 1; | ||
| 9434 | break :decls_len decls_len; | ||
| 9435 | } else 0; | ||
| 9436 | |||
| 9437 | // Skip over decls. | ||
| 9438 | var decls_it = zir.declIteratorInner(extra_index, decls_len); | ||
| 9439 | while (decls_it.next()) |_| {} | ||
| 9440 | extra_index = decls_it.extra_index; | ||
| 9441 | |||
| 9442 | const body = zir.extra[extra_index..][0..body_len]; | ||
| 9443 | if (fields_len == 0) { | ||
| 9444 | assert(body.len == 0); | ||
| 9445 | return; | ||
| 9446 | } | ||
| 9447 | extra_index += body.len; | ||
| 9448 | |||
| 9449 | var decl_arena = struct_obj.owner_decl.value_arena.?.promote(gpa); | ||
| 9450 | defer struct_obj.owner_decl.value_arena.?.* = decl_arena.state; | ||
| 9451 | |||
| 9452 | try struct_obj.fields.ensureTotalCapacity(&decl_arena.allocator, fields_len); | ||
| 9453 | |||
| 9454 | if (body.len != 0) { | ||
| 9455 | _ = try sema.analyzeBody(block, body); | ||
| 9456 | } | ||
| 9457 | |||
| 9458 | const bits_per_field = 4; | ||
| 9459 | const fields_per_u32 = 32 / bits_per_field; | ||
| 9460 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; | ||
| 9461 | var bit_bag_index: usize = extra_index; | ||
| 9462 | extra_index += bit_bags_count; | ||
| 9463 | var cur_bit_bag: u32 = undefined; | ||
| 9464 | var field_i: u32 = 0; | ||
| 9465 | while (field_i < fields_len) : (field_i += 1) { | ||
| 9466 | if (field_i % fields_per_u32 == 0) { | ||
| 9467 | cur_bit_bag = zir.extra[bit_bag_index]; | ||
| 9468 | bit_bag_index += 1; | ||
| 9469 | } | ||
| 9470 | const has_align = @truncate(u1, cur_bit_bag) != 0; | ||
| 9471 | cur_bit_bag >>= 1; | ||
| 9472 | const has_default = @truncate(u1, cur_bit_bag) != 0; | ||
| 9473 | cur_bit_bag >>= 1; | ||
| 9474 | const is_comptime = @truncate(u1, cur_bit_bag) != 0; | ||
| 9475 | cur_bit_bag >>= 1; | ||
| 9476 | const unused = @truncate(u1, cur_bit_bag) != 0; | ||
| 9477 | cur_bit_bag >>= 1; | ||
| 9478 | |||
| 9479 | _ = unused; | ||
| 9480 | |||
| 9481 | const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); | ||
| 9482 | extra_index += 1; | ||
| 9483 | const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | ||
| 9484 | extra_index += 1; | ||
| 9485 | |||
| 9486 | // This string needs to outlive the ZIR code. | ||
| 9487 | const field_name = try decl_arena.allocator.dupe(u8, field_name_zir); | ||
| 9488 | const field_ty: Type = if (field_type_ref == .none) | ||
| 9489 | Type.initTag(.noreturn) | ||
| 9490 | else | ||
| 9491 | // TODO: if we need to report an error here, use a source location | ||
| 9492 | // that points to this type expression rather than the struct. | ||
| 9493 | // But only resolve the source location if we need to emit a compile error. | ||
| 9494 | try sema.resolveType(block, src, field_type_ref); | ||
| 9495 | |||
| 9496 | const gop = struct_obj.fields.getOrPutAssumeCapacity(field_name); | ||
| 9497 | assert(!gop.found_existing); | ||
| 9498 | gop.value_ptr.* = .{ | ||
| 9499 | .ty = try field_ty.copy(&decl_arena.allocator), | ||
| 9500 | .abi_align = Value.initTag(.abi_align_default), | ||
| 9501 | .default_val = Value.initTag(.unreachable_value), | ||
| 9502 | .is_comptime = is_comptime, | ||
| 9503 | .offset = undefined, | ||
| 9504 | }; | ||
| 9505 | |||
| 9506 | if (has_align) { | ||
| 9507 | const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | ||
| 9508 | extra_index += 1; | ||
| 9509 | // TODO: if we need to report an error here, use a source location | ||
| 9510 | // that points to this alignment expression rather than the struct. | ||
| 9511 | // But only resolve the source location if we need to emit a compile error. | ||
| 9512 | const abi_align_val = (try sema.resolveInstConst(block, src, align_ref)).val; | ||
| 9513 | gop.value_ptr.abi_align = try abi_align_val.copy(&decl_arena.allocator); | ||
| 9514 | } | ||
| 9515 | if (has_default) { | ||
| 9516 | const default_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | ||
| 9517 | extra_index += 1; | ||
| 9518 | const default_inst = sema.resolveInst(default_ref); | ||
| 9519 | // TODO: if we need to report an error here, use a source location | ||
| 9520 | // that points to this default value expression rather than the struct. | ||
| 9521 | // But only resolve the source location if we need to emit a compile error. | ||
| 9522 | const default_val = (try sema.resolveMaybeUndefVal(block, src, default_inst)) orelse | ||
| 9523 | return sema.failWithNeededComptime(block, src); | ||
| 9524 | gop.value_ptr.default_val = try default_val.copy(&decl_arena.allocator); | ||
| 9525 | } | ||
| 9526 | } | ||
| 9527 | } | ||
| 9528 | |||
| 9529 | fn analyzeUnionFields( | ||
| 9530 | sema: *Sema, | ||
| 9531 | block: *Scope.Block, | ||
| 9532 | union_obj: *Module.Union, | ||
| 9533 | ) CompileError!void { | ||
| 9534 | const tracy = trace(@src()); | ||
| 9535 | defer tracy.end(); | ||
| 9536 | |||
| 9537 | const gpa = sema.gpa; | ||
| 9538 | const zir = sema.code; | ||
| 9539 | const extended = zir.instructions.items(.data)[union_obj.zir_index].extended; | ||
| 9540 | assert(extended.opcode == .union_decl); | ||
| 9541 | const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small); | ||
| 9542 | var extra_index: usize = extended.operand; | ||
| 9543 | |||
| 9544 | const src: LazySrcLoc = .{ .node_offset = union_obj.node_offset }; | ||
| 9545 | extra_index += @boolToInt(small.has_src_node); | ||
| 9546 | |||
| 9547 | if (small.has_tag_type) { | ||
| 9548 | extra_index += 1; | ||
| 9549 | } | ||
| 9550 | |||
| 9551 | const body_len = if (small.has_body_len) blk: { | ||
| 9552 | const body_len = zir.extra[extra_index]; | ||
| 9553 | extra_index += 1; | ||
| 9554 | break :blk body_len; | ||
| 9555 | } else 0; | ||
| 9556 | |||
| 9557 | const fields_len = if (small.has_fields_len) blk: { | ||
| 9558 | const fields_len = zir.extra[extra_index]; | ||
| 9559 | extra_index += 1; | ||
| 9560 | break :blk fields_len; | ||
| 9561 | } else 0; | ||
| 9562 | |||
| 9563 | const decls_len = if (small.has_decls_len) decls_len: { | ||
| 9564 | const decls_len = zir.extra[extra_index]; | ||
| 9565 | extra_index += 1; | ||
| 9566 | break :decls_len decls_len; | ||
| 9567 | } else 0; | ||
| 9568 | |||
| 9569 | // Skip over decls. | ||
| 9570 | var decls_it = zir.declIteratorInner(extra_index, decls_len); | ||
| 9571 | while (decls_it.next()) |_| {} | ||
| 9572 | extra_index = decls_it.extra_index; | ||
| 9573 | |||
| 9574 | const body = zir.extra[extra_index..][0..body_len]; | ||
| 9575 | if (fields_len == 0) { | ||
| 9576 | assert(body.len == 0); | ||
| 9577 | return; | ||
| 9578 | } | ||
| 9579 | extra_index += body.len; | ||
| 9580 | |||
| 9581 | var decl_arena = union_obj.owner_decl.value_arena.?.promote(gpa); | ||
| 9582 | defer union_obj.owner_decl.value_arena.?.* = decl_arena.state; | ||
| 9583 | |||
| 9584 | try union_obj.fields.ensureCapacity(&decl_arena.allocator, fields_len); | ||
| 9585 | |||
| 9586 | if (body.len != 0) { | ||
| 9587 | _ = try sema.analyzeBody(block, body); | ||
| 9588 | } | ||
| 9589 | |||
| 9590 | const bits_per_field = 4; | ||
| 9591 | const fields_per_u32 = 32 / bits_per_field; | ||
| 9592 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; | ||
| 9593 | var bit_bag_index: usize = extra_index; | ||
| 9594 | extra_index += bit_bags_count; | ||
| 9595 | var cur_bit_bag: u32 = undefined; | ||
| 9596 | var field_i: u32 = 0; | ||
| 9597 | while (field_i < fields_len) : (field_i += 1) { | ||
| 9598 | if (field_i % fields_per_u32 == 0) { | ||
| 9599 | cur_bit_bag = zir.extra[bit_bag_index]; | ||
| 9600 | bit_bag_index += 1; | ||
| 9601 | } | ||
| 9602 | const has_type = @truncate(u1, cur_bit_bag) != 0; | ||
| 9603 | cur_bit_bag >>= 1; | ||
| 9604 | const has_align = @truncate(u1, cur_bit_bag) != 0; | ||
| 9605 | cur_bit_bag >>= 1; | ||
| 9606 | const has_tag = @truncate(u1, cur_bit_bag) != 0; | ||
| 9607 | cur_bit_bag >>= 1; | ||
| 9608 | const unused = @truncate(u1, cur_bit_bag) != 0; | ||
| 9609 | cur_bit_bag >>= 1; | ||
| 9610 | _ = unused; | ||
| 9611 | |||
| 9612 | const field_name_zir = zir.nullTerminatedString(zir.extra[extra_index]); | ||
| 9613 | extra_index += 1; | ||
| 9614 | |||
| 9615 | const field_type_ref: Zir.Inst.Ref = if (has_type) blk: { | ||
| 9616 | const field_type_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | ||
| 9617 | extra_index += 1; | ||
| 9618 | break :blk field_type_ref; | ||
| 9619 | } else .none; | ||
| 9620 | |||
| 9621 | const align_ref: Zir.Inst.Ref = if (has_align) blk: { | ||
| 9622 | const align_ref = @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]); | ||
| 9623 | extra_index += 1; | ||
| 9624 | break :blk align_ref; | ||
| 9625 | } else .none; | ||
| 9626 | |||
| 9627 | if (has_tag) { | ||
| 9628 | extra_index += 1; | ||
| 9629 | } | ||
| 9630 | |||
| 9631 | // This string needs to outlive the ZIR code. | ||
| 9632 | const field_name = try decl_arena.allocator.dupe(u8, field_name_zir); | ||
| 9633 | const field_ty: Type = if (field_type_ref == .none) | ||
| 9634 | Type.initTag(.void) | ||
| 9635 | else | ||
| 9636 | // TODO: if we need to report an error here, use a source location | ||
| 9637 | // that points to this type expression rather than the union. | ||
| 9638 | // But only resolve the source location if we need to emit a compile error. | ||
| 9639 | try sema.resolveType(block, src, field_type_ref); | ||
| 9640 | |||
| 9641 | const gop = union_obj.fields.getOrPutAssumeCapacity(field_name); | ||
| 9642 | assert(!gop.found_existing); | ||
| 9643 | gop.value_ptr.* = .{ | ||
| 9644 | .ty = try field_ty.copy(&decl_arena.allocator), | ||
| 9645 | .abi_align = Value.initTag(.abi_align_default), | ||
| 9646 | }; | ||
| 9647 | |||
| 9648 | if (align_ref != .none) { | ||
| 9649 | // TODO: if we need to report an error here, use a source location | ||
| 9650 | // that points to this alignment expression rather than the struct. | ||
| 9651 | // But only resolve the source location if we need to emit a compile error. | ||
| 9652 | const abi_align_val = (try sema.resolveInstConst(block, src, align_ref)).val; | ||
| 9653 | gop.value_ptr.abi_align = try abi_align_val.copy(&decl_arena.allocator); | ||
| 9654 | } | ||
| 9655 | } | ||
| 9656 | |||
| 9657 | // TODO resolve the union tag_type_ref | ||
| 9658 | } | ||
| 9659 | |||
| 9235 | fn getBuiltin( | 9660 | fn getBuiltin( |
| 9236 | sema: *Sema, | 9661 | sema: *Sema, |
| 9237 | block: *Scope.Block, | 9662 | block: *Scope.Block, |
| ... | @@ -9344,6 +9769,7 @@ fn typeHasOnePossibleValue( | ... | @@ -9344,6 +9769,7 @@ fn typeHasOnePossibleValue( |
| 9344 | .call_options, | 9769 | .call_options, |
| 9345 | .export_options, | 9770 | .export_options, |
| 9346 | .extern_options, | 9771 | .extern_options, |
| 9772 | .type_info, | ||
| 9347 | .@"anyframe", | 9773 | .@"anyframe", |
| 9348 | .anyframe_T, | 9774 | .anyframe_T, |
| 9349 | .many_const_pointer, | 9775 | .many_const_pointer, |
| ... | @@ -9528,6 +9954,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref { | ... | @@ -9528,6 +9954,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref { |
| 9528 | .call_options => return .call_options_type, | 9954 | .call_options => return .call_options_type, |
| 9529 | .export_options => return .export_options_type, | 9955 | .export_options => return .export_options_type, |
| 9530 | .extern_options => return .extern_options_type, | 9956 | .extern_options => return .extern_options_type, |
| 9957 | .type_info => return .type_info_type, | ||
| 9531 | .manyptr_u8 => return .manyptr_u8_type, | 9958 | .manyptr_u8 => return .manyptr_u8_type, |
| 9532 | .manyptr_const_u8 => return .manyptr_const_u8_type, | 9959 | .manyptr_const_u8 => return .manyptr_const_u8_type, |
| 9533 | .fn_noreturn_no_args => return .fn_noreturn_no_args_type, | 9960 | .fn_noreturn_no_args => return .fn_noreturn_no_args_type, |
src/Zir.zig+9-4| ... | @@ -687,14 +687,14 @@ pub const Inst = struct { | ... | @@ -687,14 +687,14 @@ pub const Inst = struct { |
| 687 | /// A struct literal with a specified type, with no fields. | 687 | /// A struct literal with a specified type, with no fields. |
| 688 | /// Uses the `un_node` field. | 688 | /// Uses the `un_node` field. |
| 689 | struct_init_empty, | 689 | struct_init_empty, |
| 690 | /// Given a struct, union, or enum, and a field name as a string index, | 690 | /// Given a struct or union, and a field name as a string index, |
| 691 | /// returns the field type. Uses the `pl_node` field. Payload is `FieldType`. | 691 | /// returns the field type. Uses the `pl_node` field. Payload is `FieldType`. |
| 692 | field_type, | 692 | field_type, |
| 693 | /// Given a struct, union, or enum, and a field name as a Ref, | 693 | /// Given a struct or union, and a field name as a Ref, |
| 694 | /// returns the field type. Uses the `pl_node` field. Payload is `FieldTypeRef`. | 694 | /// returns the field type. Uses the `pl_node` field. Payload is `FieldTypeRef`. |
| 695 | field_type_ref, | 695 | field_type_ref, |
| 696 | /// Finalizes a typed struct initialization, performs validation, and returns the | 696 | /// Finalizes a typed struct or union initialization, performs validation, and returns the |
| 697 | /// struct value. | 697 | /// struct or union value. |
| 698 | /// Uses the `pl_node` field. Payload is `StructInit`. | 698 | /// Uses the `pl_node` field. Payload is `StructInit`. |
| 699 | struct_init, | 699 | struct_init, |
| 700 | /// Struct initialization syntax, make the result a pointer. | 700 | /// Struct initialization syntax, make the result a pointer. |
| ... | @@ -1703,6 +1703,7 @@ pub const Inst = struct { | ... | @@ -1703,6 +1703,7 @@ pub const Inst = struct { |
| 1703 | call_options_type, | 1703 | call_options_type, |
| 1704 | export_options_type, | 1704 | export_options_type, |
| 1705 | extern_options_type, | 1705 | extern_options_type, |
| 1706 | type_info_type, | ||
| 1706 | manyptr_u8_type, | 1707 | manyptr_u8_type, |
| 1707 | manyptr_const_u8_type, | 1708 | manyptr_const_u8_type, |
| 1708 | fn_noreturn_no_args_type, | 1709 | fn_noreturn_no_args_type, |
| ... | @@ -1973,6 +1974,10 @@ pub const Inst = struct { | ... | @@ -1973,6 +1974,10 @@ pub const Inst = struct { |
| 1973 | .ty = Type.initTag(.type), | 1974 | .ty = Type.initTag(.type), |
| 1974 | .val = Value.initTag(.extern_options_type), | 1975 | .val = Value.initTag(.extern_options_type), |
| 1975 | }, | 1976 | }, |
| 1977 | .type_info_type = .{ | ||
| 1978 | .ty = Type.initTag(.type), | ||
| 1979 | .val = Value.initTag(.type_info_type), | ||
| 1980 | }, | ||
| 1976 | 1981 | ||
| 1977 | .undef = .{ | 1982 | .undef = .{ |
| 1978 | .ty = Type.initTag(.@"undefined"), | 1983 | .ty = Type.initTag(.@"undefined"), |
src/codegen.zig+10| ... | @@ -862,6 +862,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -862,6 +862,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 862 | .slice_elem_val => try self.airSliceElemVal(inst), | 862 | .slice_elem_val => try self.airSliceElemVal(inst), |
| 863 | .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst), | 863 | .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst), |
| 864 | .ptr_elem_val => try self.airPtrElemVal(inst), | 864 | .ptr_elem_val => try self.airPtrElemVal(inst), |
| 865 | .ptr_elem_ptr => try self.airPtrElemPtr(inst), | ||
| 865 | .ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst), | 866 | .ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst), |
| 866 | 867 | ||
| 867 | .constant => unreachable, // excluded from function bodies | 868 | .constant => unreachable, // excluded from function bodies |
| ... | @@ -1419,6 +1420,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1419,6 +1420,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1419 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1420 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1420 | } | 1421 | } |
| 1421 | 1422 | ||
| 1423 | fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1424 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | ||
| 1425 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | ||
| 1426 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else switch (arch) { | ||
| 1427 | else => return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch}), | ||
| 1428 | }; | ||
| 1429 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); | ||
| 1430 | } | ||
| 1431 | |||
| 1422 | fn airPtrPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { | 1432 | fn airPtrPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1423 | const is_volatile = false; // TODO | 1433 | const is_volatile = false; // TODO |
| 1424 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1434 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
src/codegen/c.zig+8| ... | @@ -913,6 +913,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM | ... | @@ -913,6 +913,7 @@ fn genBody(o: *Object, body: []const Air.Inst.Index) error{ AnalysisFail, OutOfM |
| 913 | 913 | ||
| 914 | .ptr_elem_val => try airPtrElemVal(o, inst, "["), | 914 | .ptr_elem_val => try airPtrElemVal(o, inst, "["), |
| 915 | .ptr_ptr_elem_val => try airPtrElemVal(o, inst, "[0]["), | 915 | .ptr_ptr_elem_val => try airPtrElemVal(o, inst, "[0]["), |
| 916 | .ptr_elem_ptr => try airPtrElemPtr(o, inst), | ||
| 916 | .slice_elem_val => try airSliceElemVal(o, inst, "["), | 917 | .slice_elem_val => try airSliceElemVal(o, inst, "["), |
| 917 | .ptr_slice_elem_val => try airSliceElemVal(o, inst, "[0]["), | 918 | .ptr_slice_elem_val => try airSliceElemVal(o, inst, "[0]["), |
| 918 | 919 | ||
| ... | @@ -960,6 +961,13 @@ fn airPtrElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue { | ... | @@ -960,6 +961,13 @@ fn airPtrElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue { |
| 960 | return o.dg.fail("TODO: C backend: airPtrElemVal", .{}); | 961 | return o.dg.fail("TODO: C backend: airPtrElemVal", .{}); |
| 961 | } | 962 | } |
| 962 | 963 | ||
| 964 | fn airPtrElemPtr(o: *Object, inst: Air.Inst.Index) !CValue { | ||
| 965 | if (o.liveness.isUnused(inst)) | ||
| 966 | return CValue.none; | ||
| 967 | |||
| 968 | return o.dg.fail("TODO: C backend: airPtrElemPtr", .{}); | ||
| 969 | } | ||
| 970 | |||
| 963 | fn airSliceElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue { | 971 | fn airSliceElemVal(o: *Object, inst: Air.Inst.Index, prefix: []const u8) !CValue { |
| 964 | const is_volatile = false; // TODO | 972 | const is_volatile = false; // TODO |
| 965 | if (!is_volatile and o.liveness.isUnused(inst)) | 973 | if (!is_volatile and o.liveness.isUnused(inst)) |
src/codegen/llvm.zig+32-3| ... | @@ -432,6 +432,8 @@ pub const Object = struct { | ... | @@ -432,6 +432,8 @@ pub const Object = struct { |
| 432 | }, | 432 | }, |
| 433 | else => |e| return e, | 433 | else => |e| return e, |
| 434 | }; | 434 | }; |
| 435 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | ||
| 436 | try self.updateDeclExports(module, decl, decl_exports); | ||
| 435 | } | 437 | } |
| 436 | 438 | ||
| 437 | pub fn updateDeclExports( | 439 | pub fn updateDeclExports( |
| ... | @@ -440,7 +442,9 @@ pub const Object = struct { | ... | @@ -440,7 +442,9 @@ pub const Object = struct { |
| 440 | decl: *const Module.Decl, | 442 | decl: *const Module.Decl, |
| 441 | exports: []const *Module.Export, | 443 | exports: []const *Module.Export, |
| 442 | ) !void { | 444 | ) !void { |
| 443 | const llvm_fn = self.llvm_module.getNamedFunction(decl.name).?; | 445 | // If the module does not already have the function, we ignore this function call |
| 446 | // because we call `updateDeclExports` at the end of `updateFunc` and `updateDecl`. | ||
| 447 | const llvm_fn = self.llvm_module.getNamedFunction(decl.name) orelse return; | ||
| 444 | const is_extern = decl.val.tag() == .extern_fn; | 448 | const is_extern = decl.val.tag() == .extern_fn; |
| 445 | if (is_extern or exports.len != 0) { | 449 | if (is_extern or exports.len != 0) { |
| 446 | llvm_fn.setLinkage(.External); | 450 | llvm_fn.setLinkage(.External); |
| ... | @@ -1041,6 +1045,7 @@ pub const FuncGen = struct { | ... | @@ -1041,6 +1045,7 @@ pub const FuncGen = struct { |
| 1041 | .slice_elem_val => try self.airSliceElemVal(inst), | 1045 | .slice_elem_val => try self.airSliceElemVal(inst), |
| 1042 | .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst), | 1046 | .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst), |
| 1043 | .ptr_elem_val => try self.airPtrElemVal(inst), | 1047 | .ptr_elem_val => try self.airPtrElemVal(inst), |
| 1048 | .ptr_elem_ptr => try self.airPtrElemPtr(inst), | ||
| 1044 | .ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst), | 1049 | .ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst), |
| 1045 | 1050 | ||
| 1046 | .optional_payload => try self.airOptionalPayload(inst, false), | 1051 | .optional_payload => try self.airOptionalPayload(inst, false), |
| ... | @@ -1296,11 +1301,35 @@ pub const FuncGen = struct { | ... | @@ -1296,11 +1301,35 @@ pub const FuncGen = struct { |
| 1296 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1301 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1297 | const base_ptr = try self.resolveInst(bin_op.lhs); | 1302 | const base_ptr = try self.resolveInst(bin_op.lhs); |
| 1298 | const rhs = try self.resolveInst(bin_op.rhs); | 1303 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1299 | const indices: [1]*const llvm.Value = .{rhs}; | 1304 | const ptr = if (self.air.typeOf(bin_op.lhs).isSinglePointer()) ptr: { |
| 1300 | const ptr = self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); | 1305 | // If this is a single-item pointer to an array, we need another index in the GEP. |
| 1306 | const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs }; | ||
| 1307 | break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); | ||
| 1308 | } else ptr: { | ||
| 1309 | const indices: [1]*const llvm.Value = .{rhs}; | ||
| 1310 | break :ptr self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); | ||
| 1311 | }; | ||
| 1301 | return self.builder.buildLoad(ptr, ""); | 1312 | return self.builder.buildLoad(ptr, ""); |
| 1302 | } | 1313 | } |
| 1303 | 1314 | ||
| 1315 | fn airPtrElemPtr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | ||
| 1316 | if (self.liveness.isUnused(inst)) | ||
| 1317 | return null; | ||
| 1318 | |||
| 1319 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | ||
| 1320 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; | ||
| 1321 | const base_ptr = try self.resolveInst(bin_op.lhs); | ||
| 1322 | const rhs = try self.resolveInst(bin_op.rhs); | ||
| 1323 | if (self.air.typeOf(bin_op.lhs).isSinglePointer()) { | ||
| 1324 | // If this is a single-item pointer to an array, we need another index in the GEP. | ||
| 1325 | const indices: [2]*const llvm.Value = .{ self.context.intType(32).constNull(), rhs }; | ||
| 1326 | return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); | ||
| 1327 | } else { | ||
| 1328 | const indices: [1]*const llvm.Value = .{rhs}; | ||
| 1329 | return self.builder.buildInBoundsGEP(base_ptr, &indices, indices.len, ""); | ||
| 1330 | } | ||
| 1331 | } | ||
| 1332 | |||
| 1304 | fn airPtrPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 1333 | fn airPtrPtrElemVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 1305 | const is_volatile = false; // TODO | 1334 | const is_volatile = false; // TODO |
| 1306 | if (!is_volatile and self.liveness.isUnused(inst)) | 1335 | if (!is_volatile and self.liveness.isUnused(inst)) |
src/print_air.zig+13-3| ... | @@ -175,6 +175,7 @@ const Writer = struct { | ... | @@ -175,6 +175,7 @@ const Writer = struct { |
| 175 | .loop, | 175 | .loop, |
| 176 | => try w.writeBlock(s, inst), | 176 | => try w.writeBlock(s, inst), |
| 177 | 177 | ||
| 178 | .ptr_elem_ptr => try w.writePtrElemPtr(s, inst), | ||
| 178 | .struct_field_ptr => try w.writeStructField(s, inst), | 179 | .struct_field_ptr => try w.writeStructField(s, inst), |
| 179 | .struct_field_val => try w.writeStructField(s, inst), | 180 | .struct_field_val => try w.writeStructField(s, inst), |
| 180 | .constant => try w.writeConstant(s, inst), | 181 | .constant => try w.writeConstant(s, inst), |
| ... | @@ -239,10 +240,19 @@ const Writer = struct { | ... | @@ -239,10 +240,19 @@ const Writer = struct { |
| 239 | 240 | ||
| 240 | fn writeStructField(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | 241 | fn writeStructField(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
| 241 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; | 242 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; |
| 242 | const extra = w.air.extraData(Air.StructField, ty_pl.payload); | 243 | const extra = w.air.extraData(Air.StructField, ty_pl.payload).data; |
| 243 | 244 | ||
| 244 | try w.writeOperand(s, inst, 0, extra.data.struct_operand); | 245 | try w.writeOperand(s, inst, 0, extra.struct_operand); |
| 245 | try s.print(", {d}", .{extra.data.field_index}); | 246 | try s.print(", {d}", .{extra.field_index}); |
| 247 | } | ||
| 248 | |||
| 249 | fn writePtrElemPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | ||
| 250 | const ty_pl = w.air.instructions.items(.data)[inst].ty_pl; | ||
| 251 | const extra = w.air.extraData(Air.Bin, ty_pl.payload).data; | ||
| 252 | |||
| 253 | try w.writeOperand(s, inst, 0, extra.lhs); | ||
| 254 | try s.writeAll(", "); | ||
| 255 | try w.writeOperand(s, inst, 0, extra.rhs); | ||
| 246 | } | 256 | } |
| 247 | 257 | ||
| 248 | fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { | 258 | fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { |
src/type.zig+39| ... | @@ -133,6 +133,7 @@ pub const Type = extern union { | ... | @@ -133,6 +133,7 @@ pub const Type = extern union { |
| 133 | 133 | ||
| 134 | .@"union", | 134 | .@"union", |
| 135 | .union_tagged, | 135 | .union_tagged, |
| 136 | .type_info, | ||
| 136 | => return .Union, | 137 | => return .Union, |
| 137 | 138 | ||
| 138 | .var_args_param => unreachable, // can be any type | 139 | .var_args_param => unreachable, // can be any type |
| ... | @@ -248,6 +249,30 @@ pub const Type = extern union { | ... | @@ -248,6 +249,30 @@ pub const Type = extern union { |
| 248 | }; | 249 | }; |
| 249 | } | 250 | } |
| 250 | 251 | ||
| 252 | pub fn ptrIsMutable(ty: Type) bool { | ||
| 253 | return switch (ty.tag()) { | ||
| 254 | .single_const_pointer_to_comptime_int, | ||
| 255 | .const_slice_u8, | ||
| 256 | .single_const_pointer, | ||
| 257 | .many_const_pointer, | ||
| 258 | .manyptr_const_u8, | ||
| 259 | .c_const_pointer, | ||
| 260 | .const_slice, | ||
| 261 | => false, | ||
| 262 | |||
| 263 | .single_mut_pointer, | ||
| 264 | .many_mut_pointer, | ||
| 265 | .manyptr_u8, | ||
| 266 | .c_mut_pointer, | ||
| 267 | .mut_slice, | ||
| 268 | => true, | ||
| 269 | |||
| 270 | .pointer => ty.castTag(.pointer).?.data.mutable, | ||
| 271 | |||
| 272 | else => unreachable, | ||
| 273 | }; | ||
| 274 | } | ||
| 275 | |||
| 251 | pub fn ptrInfo(self: Type) Payload.Pointer { | 276 | pub fn ptrInfo(self: Type) Payload.Pointer { |
| 252 | switch (self.tag()) { | 277 | switch (self.tag()) { |
| 253 | .single_const_pointer_to_comptime_int => return .{ .data = .{ | 278 | .single_const_pointer_to_comptime_int => return .{ .data = .{ |
| ... | @@ -717,6 +742,7 @@ pub const Type = extern union { | ... | @@ -717,6 +742,7 @@ pub const Type = extern union { |
| 717 | .call_options, | 742 | .call_options, |
| 718 | .export_options, | 743 | .export_options, |
| 719 | .extern_options, | 744 | .extern_options, |
| 745 | .type_info, | ||
| 720 | .@"anyframe", | 746 | .@"anyframe", |
| 721 | .generic_poison, | 747 | .generic_poison, |
| 722 | => unreachable, | 748 | => unreachable, |
| ... | @@ -928,6 +954,7 @@ pub const Type = extern union { | ... | @@ -928,6 +954,7 @@ pub const Type = extern union { |
| 928 | .call_options => return writer.writeAll("std.builtin.CallOptions"), | 954 | .call_options => return writer.writeAll("std.builtin.CallOptions"), |
| 929 | .export_options => return writer.writeAll("std.builtin.ExportOptions"), | 955 | .export_options => return writer.writeAll("std.builtin.ExportOptions"), |
| 930 | .extern_options => return writer.writeAll("std.builtin.ExternOptions"), | 956 | .extern_options => return writer.writeAll("std.builtin.ExternOptions"), |
| 957 | .type_info => return writer.writeAll("std.builtin.TypeInfo"), | ||
| 931 | .function => { | 958 | .function => { |
| 932 | const payload = ty.castTag(.function).?.data; | 959 | const payload = ty.castTag(.function).?.data; |
| 933 | try writer.writeAll("fn("); | 960 | try writer.writeAll("fn("); |
| ... | @@ -1178,6 +1205,7 @@ pub const Type = extern union { | ... | @@ -1178,6 +1205,7 @@ pub const Type = extern union { |
| 1178 | .comptime_int, | 1205 | .comptime_int, |
| 1179 | .comptime_float, | 1206 | .comptime_float, |
| 1180 | .enum_literal, | 1207 | .enum_literal, |
| 1208 | .type_info, | ||
| 1181 | => true, | 1209 | => true, |
| 1182 | 1210 | ||
| 1183 | .var_args_param => unreachable, | 1211 | .var_args_param => unreachable, |
| ... | @@ -1269,6 +1297,7 @@ pub const Type = extern union { | ... | @@ -1269,6 +1297,7 @@ pub const Type = extern union { |
| 1269 | .call_options => return Value.initTag(.call_options_type), | 1297 | .call_options => return Value.initTag(.call_options_type), |
| 1270 | .export_options => return Value.initTag(.export_options_type), | 1298 | .export_options => return Value.initTag(.export_options_type), |
| 1271 | .extern_options => return Value.initTag(.extern_options_type), | 1299 | .extern_options => return Value.initTag(.extern_options_type), |
| 1300 | .type_info => return Value.initTag(.type_info_type), | ||
| 1272 | .inferred_alloc_const => unreachable, | 1301 | .inferred_alloc_const => unreachable, |
| 1273 | .inferred_alloc_mut => unreachable, | 1302 | .inferred_alloc_mut => unreachable, |
| 1274 | else => return Value.Tag.ty.create(allocator, self), | 1303 | else => return Value.Tag.ty.create(allocator, self), |
| ... | @@ -1409,6 +1438,7 @@ pub const Type = extern union { | ... | @@ -1409,6 +1438,7 @@ pub const Type = extern union { |
| 1409 | .empty_struct, | 1438 | .empty_struct, |
| 1410 | .empty_struct_literal, | 1439 | .empty_struct_literal, |
| 1411 | .@"opaque", | 1440 | .@"opaque", |
| 1441 | .type_info, | ||
| 1412 | => false, | 1442 | => false, |
| 1413 | 1443 | ||
| 1414 | .inferred_alloc_const => unreachable, | 1444 | .inferred_alloc_const => unreachable, |
| ... | @@ -1636,6 +1666,7 @@ pub const Type = extern union { | ... | @@ -1636,6 +1666,7 @@ pub const Type = extern union { |
| 1636 | .inferred_alloc_mut, | 1666 | .inferred_alloc_mut, |
| 1637 | .@"opaque", | 1667 | .@"opaque", |
| 1638 | .var_args_param, | 1668 | .var_args_param, |
| 1669 | .type_info, | ||
| 1639 | => unreachable, | 1670 | => unreachable, |
| 1640 | 1671 | ||
| 1641 | .generic_poison => unreachable, | 1672 | .generic_poison => unreachable, |
| ... | @@ -1667,6 +1698,7 @@ pub const Type = extern union { | ... | @@ -1667,6 +1698,7 @@ pub const Type = extern union { |
| 1667 | .@"opaque" => unreachable, | 1698 | .@"opaque" => unreachable, |
| 1668 | .var_args_param => unreachable, | 1699 | .var_args_param => unreachable, |
| 1669 | .generic_poison => unreachable, | 1700 | .generic_poison => unreachable, |
| 1701 | .type_info => unreachable, | ||
| 1670 | 1702 | ||
| 1671 | .@"struct" => { | 1703 | .@"struct" => { |
| 1672 | const s = self.castTag(.@"struct").?.data; | 1704 | const s = self.castTag(.@"struct").?.data; |
| ... | @@ -1978,6 +2010,7 @@ pub const Type = extern union { | ... | @@ -1978,6 +2010,7 @@ pub const Type = extern union { |
| 1978 | .call_options, | 2010 | .call_options, |
| 1979 | .export_options, | 2011 | .export_options, |
| 1980 | .extern_options, | 2012 | .extern_options, |
| 2013 | .type_info, | ||
| 1981 | => @panic("TODO at some point we gotta resolve builtin types"), | 2014 | => @panic("TODO at some point we gotta resolve builtin types"), |
| 1982 | }; | 2015 | }; |
| 1983 | } | 2016 | } |
| ... | @@ -2691,6 +2724,7 @@ pub const Type = extern union { | ... | @@ -2691,6 +2724,7 @@ pub const Type = extern union { |
| 2691 | .call_options, | 2724 | .call_options, |
| 2692 | .export_options, | 2725 | .export_options, |
| 2693 | .extern_options, | 2726 | .extern_options, |
| 2727 | .type_info, | ||
| 2694 | .@"anyframe", | 2728 | .@"anyframe", |
| 2695 | .anyframe_T, | 2729 | .anyframe_T, |
| 2696 | .many_const_pointer, | 2730 | .many_const_pointer, |
| ... | @@ -2778,6 +2812,7 @@ pub const Type = extern union { | ... | @@ -2778,6 +2812,7 @@ pub const Type = extern union { |
| 2778 | return switch (self.tag()) { | 2812 | return switch (self.tag()) { |
| 2779 | .@"struct" => &self.castTag(.@"struct").?.data.namespace, | 2813 | .@"struct" => &self.castTag(.@"struct").?.data.namespace, |
| 2780 | .enum_full => &self.castTag(.enum_full).?.data.namespace, | 2814 | .enum_full => &self.castTag(.enum_full).?.data.namespace, |
| 2815 | .enum_nonexhaustive => &self.castTag(.enum_nonexhaustive).?.data.namespace, | ||
| 2781 | .empty_struct => self.castTag(.empty_struct).?.data, | 2816 | .empty_struct => self.castTag(.empty_struct).?.data, |
| 2782 | .@"opaque" => &self.castTag(.@"opaque").?.data, | 2817 | .@"opaque" => &self.castTag(.@"opaque").?.data, |
| 2783 | .@"union" => &self.castTag(.@"union").?.data.namespace, | 2818 | .@"union" => &self.castTag(.@"union").?.data.namespace, |
| ... | @@ -3022,6 +3057,7 @@ pub const Type = extern union { | ... | @@ -3022,6 +3057,7 @@ pub const Type = extern union { |
| 3022 | .call_options, | 3057 | .call_options, |
| 3023 | .export_options, | 3058 | .export_options, |
| 3024 | .extern_options, | 3059 | .extern_options, |
| 3060 | .type_info, | ||
| 3025 | => @panic("TODO resolve std.builtin types"), | 3061 | => @panic("TODO resolve std.builtin types"), |
| 3026 | else => unreachable, | 3062 | else => unreachable, |
| 3027 | } | 3063 | } |
| ... | @@ -3058,6 +3094,7 @@ pub const Type = extern union { | ... | @@ -3058,6 +3094,7 @@ pub const Type = extern union { |
| 3058 | .call_options, | 3094 | .call_options, |
| 3059 | .export_options, | 3095 | .export_options, |
| 3060 | .extern_options, | 3096 | .extern_options, |
| 3097 | .type_info, | ||
| 3061 | => @panic("TODO resolve std.builtin types"), | 3098 | => @panic("TODO resolve std.builtin types"), |
| 3062 | else => unreachable, | 3099 | else => unreachable, |
| 3063 | } | 3100 | } |
| ... | @@ -3167,6 +3204,7 @@ pub const Type = extern union { | ... | @@ -3167,6 +3204,7 @@ pub const Type = extern union { |
| 3167 | call_options, | 3204 | call_options, |
| 3168 | export_options, | 3205 | export_options, |
| 3169 | extern_options, | 3206 | extern_options, |
| 3207 | type_info, | ||
| 3170 | manyptr_u8, | 3208 | manyptr_u8, |
| 3171 | manyptr_const_u8, | 3209 | manyptr_const_u8, |
| 3172 | fn_noreturn_no_args, | 3210 | fn_noreturn_no_args, |
| ... | @@ -3289,6 +3327,7 @@ pub const Type = extern union { | ... | @@ -3289,6 +3327,7 @@ pub const Type = extern union { |
| 3289 | .call_options, | 3327 | .call_options, |
| 3290 | .export_options, | 3328 | .export_options, |
| 3291 | .extern_options, | 3329 | .extern_options, |
| 3330 | .type_info, | ||
| 3292 | .@"anyframe", | 3331 | .@"anyframe", |
| 3293 | => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), | 3332 | => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"), |
| 3294 | 3333 |
src/value.zig+30| ... | @@ -68,6 +68,7 @@ pub const Value = extern union { | ... | @@ -68,6 +68,7 @@ pub const Value = extern union { |
| 68 | call_options_type, | 68 | call_options_type, |
| 69 | export_options_type, | 69 | export_options_type, |
| 70 | extern_options_type, | 70 | extern_options_type, |
| 71 | type_info_type, | ||
| 71 | manyptr_u8_type, | 72 | manyptr_u8_type, |
| 72 | manyptr_const_u8_type, | 73 | manyptr_const_u8_type, |
| 73 | fn_noreturn_no_args_type, | 74 | fn_noreturn_no_args_type, |
| ... | @@ -221,6 +222,7 @@ pub const Value = extern union { | ... | @@ -221,6 +222,7 @@ pub const Value = extern union { |
| 221 | .call_options_type, | 222 | .call_options_type, |
| 222 | .export_options_type, | 223 | .export_options_type, |
| 223 | .extern_options_type, | 224 | .extern_options_type, |
| 225 | .type_info_type, | ||
| 224 | .generic_poison, | 226 | .generic_poison, |
| 225 | => @compileError("Value Tag " ++ @tagName(t) ++ " has no payload"), | 227 | => @compileError("Value Tag " ++ @tagName(t) ++ " has no payload"), |
| 226 | 228 | ||
| ... | @@ -402,6 +404,7 @@ pub const Value = extern union { | ... | @@ -402,6 +404,7 @@ pub const Value = extern union { |
| 402 | .call_options_type, | 404 | .call_options_type, |
| 403 | .export_options_type, | 405 | .export_options_type, |
| 404 | .extern_options_type, | 406 | .extern_options_type, |
| 407 | .type_info_type, | ||
| 405 | .generic_poison, | 408 | .generic_poison, |
| 406 | => unreachable, | 409 | => unreachable, |
| 407 | 410 | ||
| ... | @@ -585,6 +588,7 @@ pub const Value = extern union { | ... | @@ -585,6 +588,7 @@ pub const Value = extern union { |
| 585 | .call_options_type => return out_stream.writeAll("std.builtin.CallOptions"), | 588 | .call_options_type => return out_stream.writeAll("std.builtin.CallOptions"), |
| 586 | .export_options_type => return out_stream.writeAll("std.builtin.ExportOptions"), | 589 | .export_options_type => return out_stream.writeAll("std.builtin.ExportOptions"), |
| 587 | .extern_options_type => return out_stream.writeAll("std.builtin.ExternOptions"), | 590 | .extern_options_type => return out_stream.writeAll("std.builtin.ExternOptions"), |
| 591 | .type_info_type => return out_stream.writeAll("std.builtin.TypeInfo"), | ||
| 588 | .abi_align_default => return out_stream.writeAll("(default ABI alignment)"), | 592 | .abi_align_default => return out_stream.writeAll("(default ABI alignment)"), |
| 589 | 593 | ||
| 590 | .empty_struct_value => return out_stream.writeAll("struct {}{}"), | 594 | .empty_struct_value => return out_stream.writeAll("struct {}{}"), |
| ... | @@ -743,6 +747,7 @@ pub const Value = extern union { | ... | @@ -743,6 +747,7 @@ pub const Value = extern union { |
| 743 | .call_options_type => Type.initTag(.call_options), | 747 | .call_options_type => Type.initTag(.call_options), |
| 744 | .export_options_type => Type.initTag(.export_options), | 748 | .export_options_type => Type.initTag(.export_options), |
| 745 | .extern_options_type => Type.initTag(.extern_options), | 749 | .extern_options_type => Type.initTag(.extern_options), |
| 750 | .type_info_type => Type.initTag(.type_info), | ||
| 746 | 751 | ||
| 747 | .int_type => { | 752 | .int_type => { |
| 748 | const payload = self.castTag(.int_type).?.data; | 753 | const payload = self.castTag(.int_type).?.data; |
| ... | @@ -1514,6 +1519,31 @@ pub const Value = extern union { | ... | @@ -1514,6 +1519,31 @@ pub const Value = extern union { |
| 1514 | return Tag.int_u64.create(arena, truncated); | 1519 | return Tag.int_u64.create(arena, truncated); |
| 1515 | } | 1520 | } |
| 1516 | 1521 | ||
| 1522 | pub fn shr(lhs: Value, rhs: Value, allocator: *Allocator) !Value { | ||
| 1523 | // TODO is this a performance issue? maybe we should try the operation without | ||
| 1524 | // resorting to BigInt first. | ||
| 1525 | var lhs_space: Value.BigIntSpace = undefined; | ||
| 1526 | const lhs_bigint = lhs.toBigInt(&lhs_space); | ||
| 1527 | const shift = rhs.toUnsignedInt(); | ||
| 1528 | const limbs = try allocator.alloc( | ||
| 1529 | std.math.big.Limb, | ||
| 1530 | lhs_bigint.limbs.len - (shift / (@sizeOf(std.math.big.Limb) * 8)), | ||
| 1531 | ); | ||
| 1532 | var result_bigint = BigIntMutable{ | ||
| 1533 | .limbs = limbs, | ||
| 1534 | .positive = undefined, | ||
| 1535 | .len = undefined, | ||
| 1536 | }; | ||
| 1537 | result_bigint.shiftRight(lhs_bigint, shift); | ||
| 1538 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | ||
| 1539 | |||
| 1540 | if (result_bigint.positive) { | ||
| 1541 | return Value.Tag.int_big_positive.create(allocator, result_limbs); | ||
| 1542 | } else { | ||
| 1543 | return Value.Tag.int_big_negative.create(allocator, result_limbs); | ||
| 1544 | } | ||
| 1545 | } | ||
| 1546 | |||
| 1517 | pub fn floatAdd( | 1547 | pub fn floatAdd( |
| 1518 | lhs: Value, | 1548 | lhs: Value, |
| 1519 | rhs: Value, | 1549 | rhs: Value, |
test/behavior.zig+2-1| ... | @@ -9,12 +9,13 @@ test { | ... | @@ -9,12 +9,13 @@ test { |
| 9 | _ = @import("behavior/pointers.zig"); | 9 | _ = @import("behavior/pointers.zig"); |
| 10 | _ = @import("behavior/if.zig"); | 10 | _ = @import("behavior/if.zig"); |
| 11 | _ = @import("behavior/cast.zig"); | 11 | _ = @import("behavior/cast.zig"); |
| 12 | _ = @import("behavior/array.zig"); | ||
| 12 | 13 | ||
| 13 | if (!builtin.zig_is_stage2) { | 14 | if (!builtin.zig_is_stage2) { |
| 14 | // Tests that only pass for stage1. | 15 | // Tests that only pass for stage1. |
| 15 | _ = @import("behavior/align.zig"); | 16 | _ = @import("behavior/align.zig"); |
| 16 | _ = @import("behavior/alignof.zig"); | 17 | _ = @import("behavior/alignof.zig"); |
| 17 | _ = @import("behavior/array.zig"); | 18 | _ = @import("behavior/array_stage1.zig"); |
| 18 | if (builtin.os.tag != .wasi) { | 19 | if (builtin.os.tag != .wasi) { |
| 19 | _ = @import("behavior/asm.zig"); | 20 | _ = @import("behavior/asm.zig"); |
| 20 | _ = @import("behavior/async_fn.zig"); | 21 | _ = @import("behavior/async_fn.zig"); |
test/behavior/array.zig-484| ... | @@ -3,487 +3,3 @@ const testing = std.testing; | ... | @@ -3,487 +3,3 @@ const testing = std.testing; |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const expect = testing.expect; | 4 | const expect = testing.expect; |
| 5 | const expectEqual = testing.expectEqual; | 5 | const expectEqual = testing.expectEqual; |
| 6 | |||
| 7 | test "arrays" { | ||
| 8 | var array: [5]u32 = undefined; | ||
| 9 | |||
| 10 | var i: u32 = 0; | ||
| 11 | while (i < 5) { | ||
| 12 | array[i] = i + 1; | ||
| 13 | i = array[i]; | ||
| 14 | } | ||
| 15 | |||
| 16 | i = 0; | ||
| 17 | var accumulator = @as(u32, 0); | ||
| 18 | while (i < 5) { | ||
| 19 | accumulator += array[i]; | ||
| 20 | |||
| 21 | i += 1; | ||
| 22 | } | ||
| 23 | |||
| 24 | try expect(accumulator == 15); | ||
| 25 | try expect(getArrayLen(&array) == 5); | ||
| 26 | } | ||
| 27 | fn getArrayLen(a: []const u32) usize { | ||
| 28 | return a.len; | ||
| 29 | } | ||
| 30 | |||
| 31 | test "array with sentinels" { | ||
| 32 | const S = struct { | ||
| 33 | fn doTheTest(is_ct: bool) !void { | ||
| 34 | if (is_ct) { | ||
| 35 | var zero_sized: [0:0xde]u8 = [_:0xde]u8{}; | ||
| 36 | // Disabled at runtime because of | ||
| 37 | // https://github.com/ziglang/zig/issues/4372 | ||
| 38 | try expectEqual(@as(u8, 0xde), zero_sized[0]); | ||
| 39 | var reinterpreted = @ptrCast(*[1]u8, &zero_sized); | ||
| 40 | try expectEqual(@as(u8, 0xde), reinterpreted[0]); | ||
| 41 | } | ||
| 42 | var arr: [3:0x55]u8 = undefined; | ||
| 43 | // Make sure the sentinel pointer is pointing after the last element | ||
| 44 | if (!is_ct) { | ||
| 45 | const sentinel_ptr = @ptrToInt(&arr[3]); | ||
| 46 | const last_elem_ptr = @ptrToInt(&arr[2]); | ||
| 47 | try expectEqual(@as(usize, 1), sentinel_ptr - last_elem_ptr); | ||
| 48 | } | ||
| 49 | // Make sure the sentinel is writeable | ||
| 50 | arr[3] = 0x55; | ||
| 51 | } | ||
| 52 | }; | ||
| 53 | |||
| 54 | try S.doTheTest(false); | ||
| 55 | comptime try S.doTheTest(true); | ||
| 56 | } | ||
| 57 | |||
| 58 | test "void arrays" { | ||
| 59 | var array: [4]void = undefined; | ||
| 60 | array[0] = void{}; | ||
| 61 | array[1] = array[2]; | ||
| 62 | try expect(@sizeOf(@TypeOf(array)) == 0); | ||
| 63 | try expect(array.len == 4); | ||
| 64 | } | ||
| 65 | |||
| 66 | test "array literal" { | ||
| 67 | const hex_mult = [_]u16{ | ||
| 68 | 4096, | ||
| 69 | 256, | ||
| 70 | 16, | ||
| 71 | 1, | ||
| 72 | }; | ||
| 73 | |||
| 74 | try expect(hex_mult.len == 4); | ||
| 75 | try expect(hex_mult[1] == 256); | ||
| 76 | } | ||
| 77 | |||
| 78 | test "array dot len const expr" { | ||
| 79 | try expect(comptime x: { | ||
| 80 | break :x some_array.len == 4; | ||
| 81 | }); | ||
| 82 | } | ||
| 83 | |||
| 84 | const ArrayDotLenConstExpr = struct { | ||
| 85 | y: [some_array.len]u8, | ||
| 86 | }; | ||
| 87 | const some_array = [_]u8{ | ||
| 88 | 0, | ||
| 89 | 1, | ||
| 90 | 2, | ||
| 91 | 3, | ||
| 92 | }; | ||
| 93 | |||
| 94 | test "nested arrays" { | ||
| 95 | const array_of_strings = [_][]const u8{ | ||
| 96 | "hello", | ||
| 97 | "this", | ||
| 98 | "is", | ||
| 99 | "my", | ||
| 100 | "thing", | ||
| 101 | }; | ||
| 102 | for (array_of_strings) |s, i| { | ||
| 103 | if (i == 0) try expect(mem.eql(u8, s, "hello")); | ||
| 104 | if (i == 1) try expect(mem.eql(u8, s, "this")); | ||
| 105 | if (i == 2) try expect(mem.eql(u8, s, "is")); | ||
| 106 | if (i == 3) try expect(mem.eql(u8, s, "my")); | ||
| 107 | if (i == 4) try expect(mem.eql(u8, s, "thing")); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | var s_array: [8]Sub = undefined; | ||
| 112 | const Sub = struct { | ||
| 113 | b: u8, | ||
| 114 | }; | ||
| 115 | const Str = struct { | ||
| 116 | a: []Sub, | ||
| 117 | }; | ||
| 118 | test "set global var array via slice embedded in struct" { | ||
| 119 | var s = Str{ .a = s_array[0..] }; | ||
| 120 | |||
| 121 | s.a[0].b = 1; | ||
| 122 | s.a[1].b = 2; | ||
| 123 | s.a[2].b = 3; | ||
| 124 | |||
| 125 | try expect(s_array[0].b == 1); | ||
| 126 | try expect(s_array[1].b == 2); | ||
| 127 | try expect(s_array[2].b == 3); | ||
| 128 | } | ||
| 129 | |||
| 130 | test "array literal with specified size" { | ||
| 131 | var array = [2]u8{ | ||
| 132 | 1, | ||
| 133 | 2, | ||
| 134 | }; | ||
| 135 | try expect(array[0] == 1); | ||
| 136 | try expect(array[1] == 2); | ||
| 137 | } | ||
| 138 | |||
| 139 | test "array len field" { | ||
| 140 | var arr = [4]u8{ 0, 0, 0, 0 }; | ||
| 141 | var ptr = &arr; | ||
| 142 | try expect(arr.len == 4); | ||
| 143 | comptime try expect(arr.len == 4); | ||
| 144 | try expect(ptr.len == 4); | ||
| 145 | comptime try expect(ptr.len == 4); | ||
| 146 | } | ||
| 147 | |||
| 148 | test "single-item pointer to array indexing and slicing" { | ||
| 149 | try testSingleItemPtrArrayIndexSlice(); | ||
| 150 | comptime try testSingleItemPtrArrayIndexSlice(); | ||
| 151 | } | ||
| 152 | |||
| 153 | fn testSingleItemPtrArrayIndexSlice() !void { | ||
| 154 | { | ||
| 155 | var array: [4]u8 = "aaaa".*; | ||
| 156 | doSomeMangling(&array); | ||
| 157 | try expect(mem.eql(u8, "azya", &array)); | ||
| 158 | } | ||
| 159 | { | ||
| 160 | var array = "aaaa".*; | ||
| 161 | doSomeMangling(&array); | ||
| 162 | try expect(mem.eql(u8, "azya", &array)); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | fn doSomeMangling(array: *[4]u8) void { | ||
| 167 | array[1] = 'z'; | ||
| 168 | array[2..3][0] = 'y'; | ||
| 169 | } | ||
| 170 | |||
| 171 | test "implicit cast single-item pointer" { | ||
| 172 | try testImplicitCastSingleItemPtr(); | ||
| 173 | comptime try testImplicitCastSingleItemPtr(); | ||
| 174 | } | ||
| 175 | |||
| 176 | fn testImplicitCastSingleItemPtr() !void { | ||
| 177 | var byte: u8 = 100; | ||
| 178 | const slice = @as(*[1]u8, &byte)[0..]; | ||
| 179 | slice[0] += 1; | ||
| 180 | try expect(byte == 101); | ||
| 181 | } | ||
| 182 | |||
| 183 | fn testArrayByValAtComptime(b: [2]u8) u8 { | ||
| 184 | return b[0]; | ||
| 185 | } | ||
| 186 | |||
| 187 | test "comptime evalutating function that takes array by value" { | ||
| 188 | const arr = [_]u8{ 0, 1 }; | ||
| 189 | _ = comptime testArrayByValAtComptime(arr); | ||
| 190 | _ = comptime testArrayByValAtComptime(arr); | ||
| 191 | } | ||
| 192 | |||
| 193 | test "implicit comptime in array type size" { | ||
| 194 | var arr: [plusOne(10)]bool = undefined; | ||
| 195 | try expect(arr.len == 11); | ||
| 196 | } | ||
| 197 | |||
| 198 | fn plusOne(x: u32) u32 { | ||
| 199 | return x + 1; | ||
| 200 | } | ||
| 201 | |||
| 202 | test "runtime initialize array elem and then implicit cast to slice" { | ||
| 203 | var two: i32 = 2; | ||
| 204 | const x: []const i32 = &[_]i32{two}; | ||
| 205 | try expect(x[0] == 2); | ||
| 206 | } | ||
| 207 | |||
| 208 | test "array literal as argument to function" { | ||
| 209 | const S = struct { | ||
| 210 | fn entry(two: i32) !void { | ||
| 211 | try foo(&[_]i32{ | ||
| 212 | 1, | ||
| 213 | 2, | ||
| 214 | 3, | ||
| 215 | }); | ||
| 216 | try foo(&[_]i32{ | ||
| 217 | 1, | ||
| 218 | two, | ||
| 219 | 3, | ||
| 220 | }); | ||
| 221 | try foo2(true, &[_]i32{ | ||
| 222 | 1, | ||
| 223 | 2, | ||
| 224 | 3, | ||
| 225 | }); | ||
| 226 | try foo2(true, &[_]i32{ | ||
| 227 | 1, | ||
| 228 | two, | ||
| 229 | 3, | ||
| 230 | }); | ||
| 231 | } | ||
| 232 | fn foo(x: []const i32) !void { | ||
| 233 | try expect(x[0] == 1); | ||
| 234 | try expect(x[1] == 2); | ||
| 235 | try expect(x[2] == 3); | ||
| 236 | } | ||
| 237 | fn foo2(trash: bool, x: []const i32) !void { | ||
| 238 | try expect(trash); | ||
| 239 | try expect(x[0] == 1); | ||
| 240 | try expect(x[1] == 2); | ||
| 241 | try expect(x[2] == 3); | ||
| 242 | } | ||
| 243 | }; | ||
| 244 | try S.entry(2); | ||
| 245 | comptime try S.entry(2); | ||
| 246 | } | ||
| 247 | |||
| 248 | test "double nested array to const slice cast in array literal" { | ||
| 249 | const S = struct { | ||
| 250 | fn entry(two: i32) !void { | ||
| 251 | const cases = [_][]const []const i32{ | ||
| 252 | &[_][]const i32{&[_]i32{1}}, | ||
| 253 | &[_][]const i32{&[_]i32{ 2, 3 }}, | ||
| 254 | &[_][]const i32{ | ||
| 255 | &[_]i32{4}, | ||
| 256 | &[_]i32{ 5, 6, 7 }, | ||
| 257 | }, | ||
| 258 | }; | ||
| 259 | try check(&cases); | ||
| 260 | |||
| 261 | const cases2 = [_][]const i32{ | ||
| 262 | &[_]i32{1}, | ||
| 263 | &[_]i32{ two, 3 }, | ||
| 264 | }; | ||
| 265 | try expect(cases2.len == 2); | ||
| 266 | try expect(cases2[0].len == 1); | ||
| 267 | try expect(cases2[0][0] == 1); | ||
| 268 | try expect(cases2[1].len == 2); | ||
| 269 | try expect(cases2[1][0] == 2); | ||
| 270 | try expect(cases2[1][1] == 3); | ||
| 271 | |||
| 272 | const cases3 = [_][]const []const i32{ | ||
| 273 | &[_][]const i32{&[_]i32{1}}, | ||
| 274 | &[_][]const i32{&[_]i32{ two, 3 }}, | ||
| 275 | &[_][]const i32{ | ||
| 276 | &[_]i32{4}, | ||
| 277 | &[_]i32{ 5, 6, 7 }, | ||
| 278 | }, | ||
| 279 | }; | ||
| 280 | try check(&cases3); | ||
| 281 | } | ||
| 282 | |||
| 283 | fn check(cases: []const []const []const i32) !void { | ||
| 284 | try expect(cases.len == 3); | ||
| 285 | try expect(cases[0].len == 1); | ||
| 286 | try expect(cases[0][0].len == 1); | ||
| 287 | try expect(cases[0][0][0] == 1); | ||
| 288 | try expect(cases[1].len == 1); | ||
| 289 | try expect(cases[1][0].len == 2); | ||
| 290 | try expect(cases[1][0][0] == 2); | ||
| 291 | try expect(cases[1][0][1] == 3); | ||
| 292 | try expect(cases[2].len == 2); | ||
| 293 | try expect(cases[2][0].len == 1); | ||
| 294 | try expect(cases[2][0][0] == 4); | ||
| 295 | try expect(cases[2][1].len == 3); | ||
| 296 | try expect(cases[2][1][0] == 5); | ||
| 297 | try expect(cases[2][1][1] == 6); | ||
| 298 | try expect(cases[2][1][2] == 7); | ||
| 299 | } | ||
| 300 | }; | ||
| 301 | try S.entry(2); | ||
| 302 | comptime try S.entry(2); | ||
| 303 | } | ||
| 304 | |||
| 305 | test "read/write through global variable array of struct fields initialized via array mult" { | ||
| 306 | const S = struct { | ||
| 307 | fn doTheTest() !void { | ||
| 308 | try expect(storage[0].term == 1); | ||
| 309 | storage[0] = MyStruct{ .term = 123 }; | ||
| 310 | try expect(storage[0].term == 123); | ||
| 311 | } | ||
| 312 | |||
| 313 | pub const MyStruct = struct { | ||
| 314 | term: usize, | ||
| 315 | }; | ||
| 316 | |||
| 317 | var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1; | ||
| 318 | }; | ||
| 319 | try S.doTheTest(); | ||
| 320 | } | ||
| 321 | |||
| 322 | test "implicit cast zero sized array ptr to slice" { | ||
| 323 | { | ||
| 324 | var b = "".*; | ||
| 325 | const c: []const u8 = &b; | ||
| 326 | try expect(c.len == 0); | ||
| 327 | } | ||
| 328 | { | ||
| 329 | var b: [0]u8 = "".*; | ||
| 330 | const c: []const u8 = &b; | ||
| 331 | try expect(c.len == 0); | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | test "anonymous list literal syntax" { | ||
| 336 | const S = struct { | ||
| 337 | fn doTheTest() !void { | ||
| 338 | var array: [4]u8 = .{ 1, 2, 3, 4 }; | ||
| 339 | try expect(array[0] == 1); | ||
| 340 | try expect(array[1] == 2); | ||
| 341 | try expect(array[2] == 3); | ||
| 342 | try expect(array[3] == 4); | ||
| 343 | } | ||
| 344 | }; | ||
| 345 | try S.doTheTest(); | ||
| 346 | comptime try S.doTheTest(); | ||
| 347 | } | ||
| 348 | |||
| 349 | test "anonymous literal in array" { | ||
| 350 | const S = struct { | ||
| 351 | const Foo = struct { | ||
| 352 | a: usize = 2, | ||
| 353 | b: usize = 4, | ||
| 354 | }; | ||
| 355 | fn doTheTest() !void { | ||
| 356 | var array: [2]Foo = .{ | ||
| 357 | .{ .a = 3 }, | ||
| 358 | .{ .b = 3 }, | ||
| 359 | }; | ||
| 360 | try expect(array[0].a == 3); | ||
| 361 | try expect(array[0].b == 4); | ||
| 362 | try expect(array[1].a == 2); | ||
| 363 | try expect(array[1].b == 3); | ||
| 364 | } | ||
| 365 | }; | ||
| 366 | try S.doTheTest(); | ||
| 367 | comptime try S.doTheTest(); | ||
| 368 | } | ||
| 369 | |||
| 370 | test "access the null element of a null terminated array" { | ||
| 371 | const S = struct { | ||
| 372 | fn doTheTest() !void { | ||
| 373 | var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' }; | ||
| 374 | try expect(array[4] == 0); | ||
| 375 | var len: usize = 4; | ||
| 376 | try expect(array[len] == 0); | ||
| 377 | } | ||
| 378 | }; | ||
| 379 | try S.doTheTest(); | ||
| 380 | comptime try S.doTheTest(); | ||
| 381 | } | ||
| 382 | |||
| 383 | test "type deduction for array subscript expression" { | ||
| 384 | const S = struct { | ||
| 385 | fn doTheTest() !void { | ||
| 386 | var array = [_]u8{ 0x55, 0xAA }; | ||
| 387 | var v0 = true; | ||
| 388 | try expectEqual(@as(u8, 0xAA), array[if (v0) 1 else 0]); | ||
| 389 | var v1 = false; | ||
| 390 | try expectEqual(@as(u8, 0x55), array[if (v1) 1 else 0]); | ||
| 391 | } | ||
| 392 | }; | ||
| 393 | try S.doTheTest(); | ||
| 394 | comptime try S.doTheTest(); | ||
| 395 | } | ||
| 396 | |||
| 397 | test "sentinel element count towards the ABI size calculation" { | ||
| 398 | const S = struct { | ||
| 399 | fn doTheTest() !void { | ||
| 400 | const T = packed struct { | ||
| 401 | fill_pre: u8 = 0x55, | ||
| 402 | data: [0:0]u8 = undefined, | ||
| 403 | fill_post: u8 = 0xAA, | ||
| 404 | }; | ||
| 405 | var x = T{}; | ||
| 406 | var as_slice = mem.asBytes(&x); | ||
| 407 | try expectEqual(@as(usize, 3), as_slice.len); | ||
| 408 | try expectEqual(@as(u8, 0x55), as_slice[0]); | ||
| 409 | try expectEqual(@as(u8, 0xAA), as_slice[2]); | ||
| 410 | } | ||
| 411 | }; | ||
| 412 | |||
| 413 | try S.doTheTest(); | ||
| 414 | comptime try S.doTheTest(); | ||
| 415 | } | ||
| 416 | |||
| 417 | test "zero-sized array with recursive type definition" { | ||
| 418 | const U = struct { | ||
| 419 | fn foo(comptime T: type, comptime n: usize) type { | ||
| 420 | return struct { | ||
| 421 | s: [n]T, | ||
| 422 | x: usize = n, | ||
| 423 | }; | ||
| 424 | } | ||
| 425 | }; | ||
| 426 | |||
| 427 | const S = struct { | ||
| 428 | list: U.foo(@This(), 0), | ||
| 429 | }; | ||
| 430 | |||
| 431 | var t: S = .{ .list = .{ .s = undefined } }; | ||
| 432 | try expectEqual(@as(usize, 0), t.list.x); | ||
| 433 | } | ||
| 434 | |||
| 435 | test "type coercion of anon struct literal to array" { | ||
| 436 | const S = struct { | ||
| 437 | const U = union { | ||
| 438 | a: u32, | ||
| 439 | b: bool, | ||
| 440 | c: []const u8, | ||
| 441 | }; | ||
| 442 | |||
| 443 | fn doTheTest() !void { | ||
| 444 | var x1: u8 = 42; | ||
| 445 | const t1 = .{ x1, 56, 54 }; | ||
| 446 | var arr1: [3]u8 = t1; | ||
| 447 | try expect(arr1[0] == 42); | ||
| 448 | try expect(arr1[1] == 56); | ||
| 449 | try expect(arr1[2] == 54); | ||
| 450 | |||
| 451 | var x2: U = .{ .a = 42 }; | ||
| 452 | const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } }; | ||
| 453 | var arr2: [3]U = t2; | ||
| 454 | try expect(arr2[0].a == 42); | ||
| 455 | try expect(arr2[1].b == true); | ||
| 456 | try expect(mem.eql(u8, arr2[2].c, "hello")); | ||
| 457 | } | ||
| 458 | }; | ||
| 459 | try S.doTheTest(); | ||
| 460 | comptime try S.doTheTest(); | ||
| 461 | } | ||
| 462 | |||
| 463 | test "type coercion of pointer to anon struct literal to pointer to array" { | ||
| 464 | const S = struct { | ||
| 465 | const U = union { | ||
| 466 | a: u32, | ||
| 467 | b: bool, | ||
| 468 | c: []const u8, | ||
| 469 | }; | ||
| 470 | |||
| 471 | fn doTheTest() !void { | ||
| 472 | var x1: u8 = 42; | ||
| 473 | const t1 = &.{ x1, 56, 54 }; | ||
| 474 | var arr1: *const [3]u8 = t1; | ||
| 475 | try expect(arr1[0] == 42); | ||
| 476 | try expect(arr1[1] == 56); | ||
| 477 | try expect(arr1[2] == 54); | ||
| 478 | |||
| 479 | var x2: U = .{ .a = 42 }; | ||
| 480 | const t2 = &.{ x2, .{ .b = true }, .{ .c = "hello" } }; | ||
| 481 | var arr2: *const [3]U = t2; | ||
| 482 | try expect(arr2[0].a == 42); | ||
| 483 | try expect(arr2[1].b == true); | ||
| 484 | try expect(mem.eql(u8, arr2[2].c, "hello")); | ||
| 485 | } | ||
| 486 | }; | ||
| 487 | try S.doTheTest(); | ||
| 488 | comptime try S.doTheTest(); | ||
| 489 | } |
test/behavior/array_stage1.zig created+489| ... | @@ -0,0 +1,489 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const testing = std.testing; | ||
| 3 | const mem = std.mem; | ||
| 4 | const expect = testing.expect; | ||
| 5 | const expectEqual = testing.expectEqual; | ||
| 6 | |||
| 7 | test "arrays" { | ||
| 8 | var array: [5]u32 = undefined; | ||
| 9 | |||
| 10 | var i: u32 = 0; | ||
| 11 | while (i < 5) { | ||
| 12 | array[i] = i + 1; | ||
| 13 | i = array[i]; | ||
| 14 | } | ||
| 15 | |||
| 16 | i = 0; | ||
| 17 | var accumulator = @as(u32, 0); | ||
| 18 | while (i < 5) { | ||
| 19 | accumulator += array[i]; | ||
| 20 | |||
| 21 | i += 1; | ||
| 22 | } | ||
| 23 | |||
| 24 | try expect(accumulator == 15); | ||
| 25 | try expect(getArrayLen(&array) == 5); | ||
| 26 | } | ||
| 27 | fn getArrayLen(a: []const u32) usize { | ||
| 28 | return a.len; | ||
| 29 | } | ||
| 30 | |||
| 31 | test "array with sentinels" { | ||
| 32 | const S = struct { | ||
| 33 | fn doTheTest(is_ct: bool) !void { | ||
| 34 | if (is_ct) { | ||
| 35 | var zero_sized: [0:0xde]u8 = [_:0xde]u8{}; | ||
| 36 | // Disabled at runtime because of | ||
| 37 | // https://github.com/ziglang/zig/issues/4372 | ||
| 38 | try expectEqual(@as(u8, 0xde), zero_sized[0]); | ||
| 39 | var reinterpreted = @ptrCast(*[1]u8, &zero_sized); | ||
| 40 | try expectEqual(@as(u8, 0xde), reinterpreted[0]); | ||
| 41 | } | ||
| 42 | var arr: [3:0x55]u8 = undefined; | ||
| 43 | // Make sure the sentinel pointer is pointing after the last element | ||
| 44 | if (!is_ct) { | ||
| 45 | const sentinel_ptr = @ptrToInt(&arr[3]); | ||
| 46 | const last_elem_ptr = @ptrToInt(&arr[2]); | ||
| 47 | try expectEqual(@as(usize, 1), sentinel_ptr - last_elem_ptr); | ||
| 48 | } | ||
| 49 | // Make sure the sentinel is writeable | ||
| 50 | arr[3] = 0x55; | ||
| 51 | } | ||
| 52 | }; | ||
| 53 | |||
| 54 | try S.doTheTest(false); | ||
| 55 | comptime try S.doTheTest(true); | ||
| 56 | } | ||
| 57 | |||
| 58 | test "void arrays" { | ||
| 59 | var array: [4]void = undefined; | ||
| 60 | array[0] = void{}; | ||
| 61 | array[1] = array[2]; | ||
| 62 | try expect(@sizeOf(@TypeOf(array)) == 0); | ||
| 63 | try expect(array.len == 4); | ||
| 64 | } | ||
| 65 | |||
| 66 | test "array literal" { | ||
| 67 | const hex_mult = [_]u16{ | ||
| 68 | 4096, | ||
| 69 | 256, | ||
| 70 | 16, | ||
| 71 | 1, | ||
| 72 | }; | ||
| 73 | |||
| 74 | try expect(hex_mult.len == 4); | ||
| 75 | try expect(hex_mult[1] == 256); | ||
| 76 | } | ||
| 77 | |||
| 78 | test "array dot len const expr" { | ||
| 79 | try expect(comptime x: { | ||
| 80 | break :x some_array.len == 4; | ||
| 81 | }); | ||
| 82 | } | ||
| 83 | |||
| 84 | const ArrayDotLenConstExpr = struct { | ||
| 85 | y: [some_array.len]u8, | ||
| 86 | }; | ||
| 87 | const some_array = [_]u8{ | ||
| 88 | 0, | ||
| 89 | 1, | ||
| 90 | 2, | ||
| 91 | 3, | ||
| 92 | }; | ||
| 93 | |||
| 94 | test "nested arrays" { | ||
| 95 | const array_of_strings = [_][]const u8{ | ||
| 96 | "hello", | ||
| 97 | "this", | ||
| 98 | "is", | ||
| 99 | "my", | ||
| 100 | "thing", | ||
| 101 | }; | ||
| 102 | for (array_of_strings) |s, i| { | ||
| 103 | if (i == 0) try expect(mem.eql(u8, s, "hello")); | ||
| 104 | if (i == 1) try expect(mem.eql(u8, s, "this")); | ||
| 105 | if (i == 2) try expect(mem.eql(u8, s, "is")); | ||
| 106 | if (i == 3) try expect(mem.eql(u8, s, "my")); | ||
| 107 | if (i == 4) try expect(mem.eql(u8, s, "thing")); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | var s_array: [8]Sub = undefined; | ||
| 112 | const Sub = struct { | ||
| 113 | b: u8, | ||
| 114 | }; | ||
| 115 | const Str = struct { | ||
| 116 | a: []Sub, | ||
| 117 | }; | ||
| 118 | test "set global var array via slice embedded in struct" { | ||
| 119 | var s = Str{ .a = s_array[0..] }; | ||
| 120 | |||
| 121 | s.a[0].b = 1; | ||
| 122 | s.a[1].b = 2; | ||
| 123 | s.a[2].b = 3; | ||
| 124 | |||
| 125 | try expect(s_array[0].b == 1); | ||
| 126 | try expect(s_array[1].b == 2); | ||
| 127 | try expect(s_array[2].b == 3); | ||
| 128 | } | ||
| 129 | |||
| 130 | test "array literal with specified size" { | ||
| 131 | var array = [2]u8{ | ||
| 132 | 1, | ||
| 133 | 2, | ||
| 134 | }; | ||
| 135 | try expect(array[0] == 1); | ||
| 136 | try expect(array[1] == 2); | ||
| 137 | } | ||
| 138 | |||
| 139 | test "array len field" { | ||
| 140 | var arr = [4]u8{ 0, 0, 0, 0 }; | ||
| 141 | var ptr = &arr; | ||
| 142 | try expect(arr.len == 4); | ||
| 143 | comptime try expect(arr.len == 4); | ||
| 144 | try expect(ptr.len == 4); | ||
| 145 | comptime try expect(ptr.len == 4); | ||
| 146 | } | ||
| 147 | |||
| 148 | test "single-item pointer to array indexing and slicing" { | ||
| 149 | try testSingleItemPtrArrayIndexSlice(); | ||
| 150 | comptime try testSingleItemPtrArrayIndexSlice(); | ||
| 151 | } | ||
| 152 | |||
| 153 | fn testSingleItemPtrArrayIndexSlice() !void { | ||
| 154 | { | ||
| 155 | var array: [4]u8 = "aaaa".*; | ||
| 156 | doSomeMangling(&array); | ||
| 157 | try expect(mem.eql(u8, "azya", &array)); | ||
| 158 | } | ||
| 159 | { | ||
| 160 | var array = "aaaa".*; | ||
| 161 | doSomeMangling(&array); | ||
| 162 | try expect(mem.eql(u8, "azya", &array)); | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | fn doSomeMangling(array: *[4]u8) void { | ||
| 167 | array[1] = 'z'; | ||
| 168 | array[2..3][0] = 'y'; | ||
| 169 | } | ||
| 170 | |||
| 171 | test "implicit cast single-item pointer" { | ||
| 172 | try testImplicitCastSingleItemPtr(); | ||
| 173 | comptime try testImplicitCastSingleItemPtr(); | ||
| 174 | } | ||
| 175 | |||
| 176 | fn testImplicitCastSingleItemPtr() !void { | ||
| 177 | var byte: u8 = 100; | ||
| 178 | const slice = @as(*[1]u8, &byte)[0..]; | ||
| 179 | slice[0] += 1; | ||
| 180 | try expect(byte == 101); | ||
| 181 | } | ||
| 182 | |||
| 183 | fn testArrayByValAtComptime(b: [2]u8) u8 { | ||
| 184 | return b[0]; | ||
| 185 | } | ||
| 186 | |||
| 187 | test "comptime evalutating function that takes array by value" { | ||
| 188 | const arr = [_]u8{ 0, 1 }; | ||
| 189 | _ = comptime testArrayByValAtComptime(arr); | ||
| 190 | _ = comptime testArrayByValAtComptime(arr); | ||
| 191 | } | ||
| 192 | |||
| 193 | test "implicit comptime in array type size" { | ||
| 194 | var arr: [plusOne(10)]bool = undefined; | ||
| 195 | try expect(arr.len == 11); | ||
| 196 | } | ||
| 197 | |||
| 198 | fn plusOne(x: u32) u32 { | ||
| 199 | return x + 1; | ||
| 200 | } | ||
| 201 | |||
| 202 | test "runtime initialize array elem and then implicit cast to slice" { | ||
| 203 | var two: i32 = 2; | ||
| 204 | const x: []const i32 = &[_]i32{two}; | ||
| 205 | try expect(x[0] == 2); | ||
| 206 | } | ||
| 207 | |||
| 208 | test "array literal as argument to function" { | ||
| 209 | const S = struct { | ||
| 210 | fn entry(two: i32) !void { | ||
| 211 | try foo(&[_]i32{ | ||
| 212 | 1, | ||
| 213 | 2, | ||
| 214 | 3, | ||
| 215 | }); | ||
| 216 | try foo(&[_]i32{ | ||
| 217 | 1, | ||
| 218 | two, | ||
| 219 | 3, | ||
| 220 | }); | ||
| 221 | try foo2(true, &[_]i32{ | ||
| 222 | 1, | ||
| 223 | 2, | ||
| 224 | 3, | ||
| 225 | }); | ||
| 226 | try foo2(true, &[_]i32{ | ||
| 227 | 1, | ||
| 228 | two, | ||
| 229 | 3, | ||
| 230 | }); | ||
| 231 | } | ||
| 232 | fn foo(x: []const i32) !void { | ||
| 233 | try expect(x[0] == 1); | ||
| 234 | try expect(x[1] == 2); | ||
| 235 | try expect(x[2] == 3); | ||
| 236 | } | ||
| 237 | fn foo2(trash: bool, x: []const i32) !void { | ||
| 238 | try expect(trash); | ||
| 239 | try expect(x[0] == 1); | ||
| 240 | try expect(x[1] == 2); | ||
| 241 | try expect(x[2] == 3); | ||
| 242 | } | ||
| 243 | }; | ||
| 244 | try S.entry(2); | ||
| 245 | comptime try S.entry(2); | ||
| 246 | } | ||
| 247 | |||
| 248 | test "double nested array to const slice cast in array literal" { | ||
| 249 | const S = struct { | ||
| 250 | fn entry(two: i32) !void { | ||
| 251 | const cases = [_][]const []const i32{ | ||
| 252 | &[_][]const i32{&[_]i32{1}}, | ||
| 253 | &[_][]const i32{&[_]i32{ 2, 3 }}, | ||
| 254 | &[_][]const i32{ | ||
| 255 | &[_]i32{4}, | ||
| 256 | &[_]i32{ 5, 6, 7 }, | ||
| 257 | }, | ||
| 258 | }; | ||
| 259 | try check(&cases); | ||
| 260 | |||
| 261 | const cases2 = [_][]const i32{ | ||
| 262 | &[_]i32{1}, | ||
| 263 | &[_]i32{ two, 3 }, | ||
| 264 | }; | ||
| 265 | try expect(cases2.len == 2); | ||
| 266 | try expect(cases2[0].len == 1); | ||
| 267 | try expect(cases2[0][0] == 1); | ||
| 268 | try expect(cases2[1].len == 2); | ||
| 269 | try expect(cases2[1][0] == 2); | ||
| 270 | try expect(cases2[1][1] == 3); | ||
| 271 | |||
| 272 | const cases3 = [_][]const []const i32{ | ||
| 273 | &[_][]const i32{&[_]i32{1}}, | ||
| 274 | &[_][]const i32{&[_]i32{ two, 3 }}, | ||
| 275 | &[_][]const i32{ | ||
| 276 | &[_]i32{4}, | ||
| 277 | &[_]i32{ 5, 6, 7 }, | ||
| 278 | }, | ||
| 279 | }; | ||
| 280 | try check(&cases3); | ||
| 281 | } | ||
| 282 | |||
| 283 | fn check(cases: []const []const []const i32) !void { | ||
| 284 | try expect(cases.len == 3); | ||
| 285 | try expect(cases[0].len == 1); | ||
| 286 | try expect(cases[0][0].len == 1); | ||
| 287 | try expect(cases[0][0][0] == 1); | ||
| 288 | try expect(cases[1].len == 1); | ||
| 289 | try expect(cases[1][0].len == 2); | ||
| 290 | try expect(cases[1][0][0] == 2); | ||
| 291 | try expect(cases[1][0][1] == 3); | ||
| 292 | try expect(cases[2].len == 2); | ||
| 293 | try expect(cases[2][0].len == 1); | ||
| 294 | try expect(cases[2][0][0] == 4); | ||
| 295 | try expect(cases[2][1].len == 3); | ||
| 296 | try expect(cases[2][1][0] == 5); | ||
| 297 | try expect(cases[2][1][1] == 6); | ||
| 298 | try expect(cases[2][1][2] == 7); | ||
| 299 | } | ||
| 300 | }; | ||
| 301 | try S.entry(2); | ||
| 302 | comptime try S.entry(2); | ||
| 303 | } | ||
| 304 | |||
| 305 | test "read/write through global variable array of struct fields initialized via array mult" { | ||
| 306 | const S = struct { | ||
| 307 | fn doTheTest() !void { | ||
| 308 | try expect(storage[0].term == 1); | ||
| 309 | storage[0] = MyStruct{ .term = 123 }; | ||
| 310 | try expect(storage[0].term == 123); | ||
| 311 | } | ||
| 312 | |||
| 313 | pub const MyStruct = struct { | ||
| 314 | term: usize, | ||
| 315 | }; | ||
| 316 | |||
| 317 | var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1; | ||
| 318 | }; | ||
| 319 | try S.doTheTest(); | ||
| 320 | } | ||
| 321 | |||
| 322 | test "implicit cast zero sized array ptr to slice" { | ||
| 323 | { | ||
| 324 | var b = "".*; | ||
| 325 | const c: []const u8 = &b; | ||
| 326 | try expect(c.len == 0); | ||
| 327 | } | ||
| 328 | { | ||
| 329 | var b: [0]u8 = "".*; | ||
| 330 | const c: []const u8 = &b; | ||
| 331 | try expect(c.len == 0); | ||
| 332 | } | ||
| 333 | } | ||
| 334 | |||
| 335 | test "anonymous list literal syntax" { | ||
| 336 | const S = struct { | ||
| 337 | fn doTheTest() !void { | ||
| 338 | var array: [4]u8 = .{ 1, 2, 3, 4 }; | ||
| 339 | try expect(array[0] == 1); | ||
| 340 | try expect(array[1] == 2); | ||
| 341 | try expect(array[2] == 3); | ||
| 342 | try expect(array[3] == 4); | ||
| 343 | } | ||
| 344 | }; | ||
| 345 | try S.doTheTest(); | ||
| 346 | comptime try S.doTheTest(); | ||
| 347 | } | ||
| 348 | |||
| 349 | test "anonymous literal in array" { | ||
| 350 | const S = struct { | ||
| 351 | const Foo = struct { | ||
| 352 | a: usize = 2, | ||
| 353 | b: usize = 4, | ||
| 354 | }; | ||
| 355 | fn doTheTest() !void { | ||
| 356 | var array: [2]Foo = .{ | ||
| 357 | .{ .a = 3 }, | ||
| 358 | .{ .b = 3 }, | ||
| 359 | }; | ||
| 360 | try expect(array[0].a == 3); | ||
| 361 | try expect(array[0].b == 4); | ||
| 362 | try expect(array[1].a == 2); | ||
| 363 | try expect(array[1].b == 3); | ||
| 364 | } | ||
| 365 | }; | ||
| 366 | try S.doTheTest(); | ||
| 367 | comptime try S.doTheTest(); | ||
| 368 | } | ||
| 369 | |||
| 370 | test "access the null element of a null terminated array" { | ||
| 371 | const S = struct { | ||
| 372 | fn doTheTest() !void { | ||
| 373 | var array: [4:0]u8 = .{ 'a', 'o', 'e', 'u' }; | ||
| 374 | try expect(array[4] == 0); | ||
| 375 | var len: usize = 4; | ||
| 376 | try expect(array[len] == 0); | ||
| 377 | } | ||
| 378 | }; | ||
| 379 | try S.doTheTest(); | ||
| 380 | comptime try S.doTheTest(); | ||
| 381 | } | ||
| 382 | |||
| 383 | test "type deduction for array subscript expression" { | ||
| 384 | const S = struct { | ||
| 385 | fn doTheTest() !void { | ||
| 386 | var array = [_]u8{ 0x55, 0xAA }; | ||
| 387 | var v0 = true; | ||
| 388 | try expectEqual(@as(u8, 0xAA), array[if (v0) 1 else 0]); | ||
| 389 | var v1 = false; | ||
| 390 | try expectEqual(@as(u8, 0x55), array[if (v1) 1 else 0]); | ||
| 391 | } | ||
| 392 | }; | ||
| 393 | try S.doTheTest(); | ||
| 394 | comptime try S.doTheTest(); | ||
| 395 | } | ||
| 396 | |||
| 397 | test "sentinel element count towards the ABI size calculation" { | ||
| 398 | const S = struct { | ||
| 399 | fn doTheTest() !void { | ||
| 400 | const T = packed struct { | ||
| 401 | fill_pre: u8 = 0x55, | ||
| 402 | data: [0:0]u8 = undefined, | ||
| 403 | fill_post: u8 = 0xAA, | ||
| 404 | }; | ||
| 405 | var x = T{}; | ||
| 406 | var as_slice = mem.asBytes(&x); | ||
| 407 | try expectEqual(@as(usize, 3), as_slice.len); | ||
| 408 | try expectEqual(@as(u8, 0x55), as_slice[0]); | ||
| 409 | try expectEqual(@as(u8, 0xAA), as_slice[2]); | ||
| 410 | } | ||
| 411 | }; | ||
| 412 | |||
| 413 | try S.doTheTest(); | ||
| 414 | comptime try S.doTheTest(); | ||
| 415 | } | ||
| 416 | |||
| 417 | test "zero-sized array with recursive type definition" { | ||
| 418 | const U = struct { | ||
| 419 | fn foo(comptime T: type, comptime n: usize) type { | ||
| 420 | return struct { | ||
| 421 | s: [n]T, | ||
| 422 | x: usize = n, | ||
| 423 | }; | ||
| 424 | } | ||
| 425 | }; | ||
| 426 | |||
| 427 | const S = struct { | ||
| 428 | list: U.foo(@This(), 0), | ||
| 429 | }; | ||
| 430 | |||
| 431 | var t: S = .{ .list = .{ .s = undefined } }; | ||
| 432 | try expectEqual(@as(usize, 0), t.list.x); | ||
| 433 | } | ||
| 434 | |||
| 435 | test "type coercion of anon struct literal to array" { | ||
| 436 | const S = struct { | ||
| 437 | const U = union { | ||
| 438 | a: u32, | ||
| 439 | b: bool, | ||
| 440 | c: []const u8, | ||
| 441 | }; | ||
| 442 | |||
| 443 | fn doTheTest() !void { | ||
| 444 | var x1: u8 = 42; | ||
| 445 | const t1 = .{ x1, 56, 54 }; | ||
| 446 | var arr1: [3]u8 = t1; | ||
| 447 | try expect(arr1[0] == 42); | ||
| 448 | try expect(arr1[1] == 56); | ||
| 449 | try expect(arr1[2] == 54); | ||
| 450 | |||
| 451 | var x2: U = .{ .a = 42 }; | ||
| 452 | const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } }; | ||
| 453 | var arr2: [3]U = t2; | ||
| 454 | try expect(arr2[0].a == 42); | ||
| 455 | try expect(arr2[1].b == true); | ||
| 456 | try expect(mem.eql(u8, arr2[2].c, "hello")); | ||
| 457 | } | ||
| 458 | }; | ||
| 459 | try S.doTheTest(); | ||
| 460 | comptime try S.doTheTest(); | ||
| 461 | } | ||
| 462 | |||
| 463 | test "type coercion of pointer to anon struct literal to pointer to array" { | ||
| 464 | const S = struct { | ||
| 465 | const U = union { | ||
| 466 | a: u32, | ||
| 467 | b: bool, | ||
| 468 | c: []const u8, | ||
| 469 | }; | ||
| 470 | |||
| 471 | fn doTheTest() !void { | ||
| 472 | var x1: u8 = 42; | ||
| 473 | const t1 = &.{ x1, 56, 54 }; | ||
| 474 | var arr1: *const [3]u8 = t1; | ||
| 475 | try expect(arr1[0] == 42); | ||
| 476 | try expect(arr1[1] == 56); | ||
| 477 | try expect(arr1[2] == 54); | ||
| 478 | |||
| 479 | var x2: U = .{ .a = 42 }; | ||
| 480 | const t2 = &.{ x2, .{ .b = true }, .{ .c = "hello" } }; | ||
| 481 | var arr2: *const [3]U = t2; | ||
| 482 | try expect(arr2[0].a == 42); | ||
| 483 | try expect(arr2[1].b == true); | ||
| 484 | try expect(mem.eql(u8, arr2[2].c, "hello")); | ||
| 485 | } | ||
| 486 | }; | ||
| 487 | try S.doTheTest(); | ||
| 488 | comptime try S.doTheTest(); | ||
| 489 | } | ||
test/behavior/eval.zig+18| ... | @@ -130,3 +130,21 @@ test "no undeclared identifier error in unanalyzed branches" { | ... | @@ -130,3 +130,21 @@ test "no undeclared identifier error in unanalyzed branches" { |
| 130 | lol_this_doesnt_exist = nonsense; | 130 | lol_this_doesnt_exist = nonsense; |
| 131 | } | 131 | } |
| 132 | } | 132 | } |
| 133 | |||
| 134 | test "a type constructed in a global expression" { | ||
| 135 | var l: List = undefined; | ||
| 136 | l.array[0] = 10; | ||
| 137 | l.array[1] = 11; | ||
| 138 | l.array[2] = 12; | ||
| 139 | const ptr = @ptrCast([*]u8, &l.array); | ||
| 140 | try expect(ptr[0] == 10); | ||
| 141 | try expect(ptr[1] == 11); | ||
| 142 | try expect(ptr[2] == 12); | ||
| 143 | } | ||
| 144 | |||
| 145 | const List = blk: { | ||
| 146 | const T = [10]u8; | ||
| 147 | break :blk struct { | ||
| 148 | array: T, | ||
| 149 | }; | ||
| 150 | }; |
test/stage2/llvm.zig+4-2| ... | @@ -32,18 +32,20 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -32,18 +32,20 @@ pub fn addCases(ctx: *TestContext) !void { |
| 32 | var case = ctx.exeUsingLlvmBackend("shift right + left", linux_x64); | 32 | var case = ctx.exeUsingLlvmBackend("shift right + left", linux_x64); |
| 33 | 33 | ||
| 34 | case.addCompareOutput( | 34 | case.addCompareOutput( |
| 35 | \\pub export fn main() void { | 35 | \\pub export fn main() c_int { |
| 36 | \\ var i: u32 = 16; | 36 | \\ var i: u32 = 16; |
| 37 | \\ assert(i >> 1, 8); | 37 | \\ assert(i >> 1, 8); |
| 38 | \\ return 0; | ||
| 38 | \\} | 39 | \\} |
| 39 | \\fn assert(a: u32, b: u32) void { | 40 | \\fn assert(a: u32, b: u32) void { |
| 40 | \\ if (a != b) unreachable; | 41 | \\ if (a != b) unreachable; |
| 41 | \\} | 42 | \\} |
| 42 | , ""); | 43 | , ""); |
| 43 | case.addCompareOutput( | 44 | case.addCompareOutput( |
| 44 | \\pub export fn main() void { | 45 | \\pub export fn main() c_int { |
| 45 | \\ var i: u32 = 16; | 46 | \\ var i: u32 = 16; |
| 46 | \\ assert(i << 1, 32); | 47 | \\ assert(i << 1, 32); |
| 48 | \\ return 0; | ||
| 47 | \\} | 49 | \\} |
| 48 | \\fn assert(a: u32, b: u32) void { | 50 | \\fn assert(a: u32, b: u32) void { |
| 49 | \\ if (a != b) unreachable; | 51 | \\ if (a != b) unreachable; |