| author | |
| committer | |
| log | 9ff926bb1e47ebad0f13835355841ef4654fbe60 |
| tree | 976c2e9cdac3f0944a68ac6343f144700f1e299a |
| parent | f3607c75cc072df8ad52d3ae8ff6bcfb2dfeeb8f |
AstGen doesn't emit a `ref` ZIR inst if it encounters a dereference `.*`
with a reference result location (`ref`/`ref_coerced_ty`/`ref_const`),
since the operand of the dereference expression already is a reference.
This caused some problems as Zig allows `.*` on both single-item *and* C
pointers, but most logic in Sema assumes that everything with a reference
rl is always a single-item pointer. This largely didn't cause any issues
since C pointers can do everything single-item pointers can, but it caused
Sema to mistake `&p.*[0]` with `@TypeOf(p) == [*c][n]T` for an index into
a many-item pointer `[*][n]T` instead of a single-item pointer `*[n]T`.
To avoid such problems in the future, a pointer obtained from `&p.*` is
now always turned into a proper single-item pointer. This is achieved by
introducing two new ZIR instructions, `deref` and `ref_deref`:
For `ptr.*`:
```
%1 = validate_deref(%ptr)
%2 = load(%ptr)
```
vvv
```
%1 = deref(%ptr)
```
For `&ptr.*`:
```
%1 = validate_deref(%ptr)
// use %ptr directly
```
vvv
```
%1 = ref_deref(%ptr)
// use %1
```
This makes `validate_deref` superfluous, so it's been removed.
Also fixes `node_offset_deref_ptr` source location to actually point to
the pointer being dereferenced and uses it to provide better source locs
for `deref` and `ref_deref`.9 files changed, 136 insertions(+), 47 deletions(-)
lib/std/zig/AstGen.zig+4-4| ... | @@ -927,15 +927,14 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE | ... | @@ -927,15 +927,14 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE |
| 927 | 927 | ||
| 928 | .deref => { | 928 | .deref => { |
| 929 | const lhs = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(node).node); | 929 | const lhs = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(node).node); |
| 930 | _ = try gz.addUnNode(.validate_deref, lhs, node); | ||
| 931 | switch (ri.rl) { | 930 | switch (ri.rl) { |
| 932 | .ref, | 931 | .ref, |
| 933 | .ref_coerced_ty, | 932 | .ref_coerced_ty, |
| 934 | .ref_const, | 933 | .ref_const, |
| 935 | => return lhs, | 934 | => return gz.addUnNode(.ref_deref, lhs, node), |
| 936 | 935 | ||
| 937 | else => { | 936 | else => { |
| 938 | const result = try gz.addUnNode(.load, lhs, node); | 937 | const result = try gz.addUnNode(.deref, lhs, node); |
| 939 | return rvalue(gz, ri, result, node); | 938 | return rvalue(gz, ri, result, node); |
| 940 | }, | 939 | }, |
| 941 | } | 940 | } |
| ... | @@ -2759,6 +2758,8 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2759,6 +2758,8 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2759 | .mulwrap, | 2758 | .mulwrap, |
| 2760 | .mul_sat, | 2759 | .mul_sat, |
| 2761 | .ref, | 2760 | .ref, |
| 2761 | .deref, | ||
| 2762 | .ref_deref, | ||
| 2762 | .shl, | 2763 | .shl, |
| 2763 | .shl_sat, | 2764 | .shl_sat, |
| 2764 | .shr, | 2765 | .shr, |
| ... | @@ -2932,7 +2933,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2932,7 +2933,6 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2932 | .memcpy, | 2933 | .memcpy, |
| 2933 | .memset, | 2934 | .memset, |
| 2934 | .memmove, | 2935 | .memmove, |
| 2935 | .validate_deref, | ||
| 2936 | .validate_destructure, | 2936 | .validate_destructure, |
| 2937 | .save_err_ret_index, | 2937 | .save_err_ret_index, |
| 2938 | .restore_err_ret_index_unconditional, | 2938 | .restore_err_ret_index_unconditional, |
lib/std/zig/Zir.zig+21-11| ... | @@ -567,12 +567,21 @@ pub const Inst = struct { | ... | @@ -567,12 +567,21 @@ pub const Inst = struct { |
| 567 | /// Uses the `pl_node` field with payload `Bin`. | 567 | /// Uses the `pl_node` field with payload `Bin`. |
| 568 | merge_error_sets, | 568 | merge_error_sets, |
| 569 | /// Turns an R-Value into a const L-Value. In other words, it takes a value, | 569 | /// Turns an R-Value into a const L-Value. In other words, it takes a value, |
| 570 | /// stores it in a memory location, and returns a const pointer to it. If the value | 570 | /// stores it in a memory location, and returns a const single-item pointer to it. |
| 571 | /// is `comptime`, the memory location is global static constant data. Otherwise, | 571 | /// If the value is `comptime`, the memory location is global static constant data. |
| 572 | /// the memory location is in the stack frame, local to the scope containing the | 572 | /// Otherwise, the memory location is in the stack frame, local to the scope |
| 573 | /// instruction. | 573 | /// containing the instruction. |
| 574 | /// Uses the `un_tok` union field. | 574 | /// Uses the `un_tok` union field. |
| 575 | ref, | 575 | ref, |
| 576 | /// Implements the dereference operand (`.*`). Checks that operand is a pointer | ||
| 577 | /// that supports being directly dereferenced. | ||
| 578 | /// Uses the `un_node` union field. | ||
| 579 | deref, | ||
| 580 | /// Emitted when a dereference (`.*`) with a reference result location occurs. | ||
| 581 | /// Checks that operand is a pointer that supports being directly dereferenced | ||
| 582 | /// and returns a single-item pointer to the dereferenced memory location. | ||
| 583 | /// Uses the `un_node` union field. | ||
| 584 | ref_deref, | ||
| 576 | /// Sends control flow back to the function's callee. | 585 | /// Sends control flow back to the function's callee. |
| 577 | /// Includes an operand as the return value. | 586 | /// Includes an operand as the return value. |
| 578 | /// Includes an AST node source location. | 587 | /// Includes an AST node source location. |
| ... | @@ -717,9 +726,6 @@ pub const Inst = struct { | ... | @@ -717,9 +726,6 @@ pub const Inst = struct { |
| 717 | /// - `if (eu) |payload| {...} else |err| {...}`, AST node is the `if`. | 726 | /// - `if (eu) |payload| {...} else |err| {...}`, AST node is the `if`. |
| 718 | /// Uses the `pl_node` union field. Payload is `SwitchBlock`. | 727 | /// Uses the `pl_node` union field. Payload is `SwitchBlock`. |
| 719 | switch_block_err_union, | 728 | switch_block_err_union, |
| 720 | /// Check that operand type supports the dereference operand (.*). | ||
| 721 | /// Uses the `un_node` field. | ||
| 722 | validate_deref, | ||
| 723 | /// Check that the operand's type is an array or tuple with the given number of elements. | 729 | /// Check that the operand's type is an array or tuple with the given number of elements. |
| 724 | /// Uses the `pl_node` field. Payload is `ValidateDestructure`. | 730 | /// Uses the `pl_node` field. Payload is `ValidateDestructure`. |
| 725 | validate_destructure, | 731 | validate_destructure, |
| ... | @@ -1170,6 +1176,8 @@ pub const Inst = struct { | ... | @@ -1170,6 +1176,8 @@ pub const Inst = struct { |
| 1170 | .mulwrap, | 1176 | .mulwrap, |
| 1171 | .mul_sat, | 1177 | .mul_sat, |
| 1172 | .ref, | 1178 | .ref, |
| 1179 | .deref, | ||
| 1180 | .ref_deref, | ||
| 1173 | .shl, | 1181 | .shl, |
| 1174 | .shl_sat, | 1182 | .shl_sat, |
| 1175 | .shr, | 1183 | .shr, |
| ... | @@ -1213,7 +1221,6 @@ pub const Inst = struct { | ... | @@ -1213,7 +1221,6 @@ pub const Inst = struct { |
| 1213 | .switch_block, | 1221 | .switch_block, |
| 1214 | .switch_block_ref, | 1222 | .switch_block_ref, |
| 1215 | .switch_block_err_union, | 1223 | .switch_block_err_union, |
| 1216 | .validate_deref, | ||
| 1217 | .validate_destructure, | 1224 | .validate_destructure, |
| 1218 | .union_init, | 1225 | .union_init, |
| 1219 | .field_type_ref, | 1226 | .field_type_ref, |
| ... | @@ -1352,7 +1359,6 @@ pub const Inst = struct { | ... | @@ -1352,7 +1359,6 @@ pub const Inst = struct { |
| 1352 | .atomic_store, | 1359 | .atomic_store, |
| 1353 | .store_node, | 1360 | .store_node, |
| 1354 | .store_to_inferred_ptr, | 1361 | .store_to_inferred_ptr, |
| 1355 | .validate_deref, | ||
| 1356 | .validate_destructure, | 1362 | .validate_destructure, |
| 1357 | .@"export", | 1363 | .@"export", |
| 1358 | .set_runtime_safety, | 1364 | .set_runtime_safety, |
| ... | @@ -1456,6 +1462,8 @@ pub const Inst = struct { | ... | @@ -1456,6 +1462,8 @@ pub const Inst = struct { |
| 1456 | .mulwrap, | 1462 | .mulwrap, |
| 1457 | .mul_sat, | 1463 | .mul_sat, |
| 1458 | .ref, | 1464 | .ref, |
| 1465 | .deref, | ||
| 1466 | .ref_deref, | ||
| 1459 | .shl, | 1467 | .shl, |
| 1460 | .shl_sat, | 1468 | .shl_sat, |
| 1461 | .shr, | 1469 | .shr, |
| ... | @@ -1708,6 +1716,8 @@ pub const Inst = struct { | ... | @@ -1708,6 +1716,8 @@ pub const Inst = struct { |
| 1708 | .merge_error_sets = .pl_node, | 1716 | .merge_error_sets = .pl_node, |
| 1709 | .mod_rem = .pl_node, | 1717 | .mod_rem = .pl_node, |
| 1710 | .ref = .un_tok, | 1718 | .ref = .un_tok, |
| 1719 | .deref = .un_node, | ||
| 1720 | .ref_deref = .un_node, | ||
| 1711 | .ret_node = .un_node, | 1721 | .ret_node = .un_node, |
| 1712 | .ret_load = .un_node, | 1722 | .ret_load = .un_node, |
| 1713 | .ret_implicit = .un_tok, | 1723 | .ret_implicit = .un_tok, |
| ... | @@ -1744,7 +1754,6 @@ pub const Inst = struct { | ... | @@ -1744,7 +1754,6 @@ pub const Inst = struct { |
| 1744 | .switch_block = .pl_node, | 1754 | .switch_block = .pl_node, |
| 1745 | .switch_block_ref = .pl_node, | 1755 | .switch_block_ref = .pl_node, |
| 1746 | .switch_block_err_union = .pl_node, | 1756 | .switch_block_err_union = .pl_node, |
| 1747 | .validate_deref = .un_node, | ||
| 1748 | .validate_destructure = .pl_node, | 1757 | .validate_destructure = .pl_node, |
| 1749 | .field_type_ref = .pl_node, | 1758 | .field_type_ref = .pl_node, |
| 1750 | .union_init = .pl_node, | 1759 | .union_init = .pl_node, |
| ... | @@ -4186,6 +4195,8 @@ fn findTrackableInner( | ... | @@ -4186,6 +4195,8 @@ fn findTrackableInner( |
| 4186 | .for_len, | 4195 | .for_len, |
| 4187 | .merge_error_sets, | 4196 | .merge_error_sets, |
| 4188 | .ref, | 4197 | .ref, |
| 4198 | .deref, | ||
| 4199 | .ref_deref, | ||
| 4189 | .ret_node, | 4200 | .ret_node, |
| 4190 | .ret_load, | 4201 | .ret_load, |
| 4191 | .ret_implicit, | 4202 | .ret_implicit, |
| ... | @@ -4219,7 +4230,6 @@ fn findTrackableInner( | ... | @@ -4219,7 +4230,6 @@ fn findTrackableInner( |
| 4219 | .enum_literal, | 4230 | .enum_literal, |
| 4220 | .decl_literal, | 4231 | .decl_literal, |
| 4221 | .decl_literal_no_coerce, | 4232 | .decl_literal_no_coerce, |
| 4222 | .validate_deref, | ||
| 4223 | .validate_destructure, | 4233 | .validate_destructure, |
| 4224 | .field_type_ref, | 4234 | .field_type_ref, |
| 4225 | .opt_eu_base_ptr_init, | 4235 | .opt_eu_base_ptr_init, |
src/Sema.zig+65-29| ... | @@ -1241,6 +1241,8 @@ fn analyzeBodyInner( | ... | @@ -1241,6 +1241,8 @@ fn analyzeBodyInner( |
| 1241 | .optional_type => try sema.zirOptionalType(block, inst), | 1241 | .optional_type => try sema.zirOptionalType(block, inst), |
| 1242 | .ptr_type => try sema.zirPtrType(block, inst), | 1242 | .ptr_type => try sema.zirPtrType(block, inst), |
| 1243 | .ref => try sema.zirRef(block, inst), | 1243 | .ref => try sema.zirRef(block, inst), |
| 1244 | .deref => try sema.zirDeref(block, inst), | ||
| 1245 | .ref_deref => try sema.zirRefDeref(block, inst), | ||
| 1244 | .shr => try sema.zirShr(block, inst, .shr), | 1246 | .shr => try sema.zirShr(block, inst, .shr), |
| 1245 | .shr_exact => try sema.zirShr(block, inst, .shr_exact), | 1247 | .shr_exact => try sema.zirShr(block, inst, .shr_exact), |
| 1246 | .slice_end => try sema.zirSliceEnd(block, inst), | 1248 | .slice_end => try sema.zirSliceEnd(block, inst), |
| ... | @@ -1566,11 +1568,6 @@ fn analyzeBodyInner( | ... | @@ -1566,11 +1568,6 @@ fn analyzeBodyInner( |
| 1566 | i += 1; | 1568 | i += 1; |
| 1567 | continue; | 1569 | continue; |
| 1568 | }, | 1570 | }, |
| 1569 | .validate_deref => { | ||
| 1570 | try sema.zirValidateDeref(block, inst); | ||
| 1571 | i += 1; | ||
| 1572 | continue; | ||
| 1573 | }, | ||
| 1574 | .validate_destructure => { | 1571 | .validate_destructure => { |
| 1575 | try sema.zirValidateDestructure(block, inst); | 1572 | try sema.zirValidateDestructure(block, inst); |
| 1576 | i += 1; | 1573 | i += 1; |
| ... | @@ -3082,6 +3079,69 @@ fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins | ... | @@ -3082,6 +3079,69 @@ fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins |
| 3082 | return sema.analyzeRef(block, block.tokenOffset(inst_data.src_tok), operand, .none); | 3079 | return sema.analyzeRef(block, block.tokenOffset(inst_data.src_tok), operand, .none); |
| 3083 | } | 3080 | } |
| 3084 | 3081 | ||
| 3082 | fn zirDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | ||
| 3083 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; | ||
| 3084 | const src = block.nodeOffset(inst_data.src_node); | ||
| 3085 | const ptr_src = block.src(.{ .node_offset_deref_ptr = inst_data.src_node }); | ||
| 3086 | const operand = sema.resolveInst(inst_data.operand); | ||
| 3087 | |||
| 3088 | try sema.validateDeref(block, src, operand, sema.typeOf(operand)); | ||
| 3089 | |||
| 3090 | return sema.analyzeLoad(block, src, operand, ptr_src); | ||
| 3091 | } | ||
| 3092 | |||
| 3093 | fn zirRefDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | ||
| 3094 | const pt = sema.pt; | ||
| 3095 | const zcu = pt.zcu; | ||
| 3096 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; | ||
| 3097 | const src = block.nodeOffset(inst_data.src_node); | ||
| 3098 | const ptr_src = block.src(.{ .node_offset_deref_ptr = inst_data.src_node }); | ||
| 3099 | const operand = sema.resolveInst(inst_data.operand); | ||
| 3100 | const operand_ty = sema.typeOf(operand); | ||
| 3101 | |||
| 3102 | try sema.validateDeref(block, src, operand, operand_ty); | ||
| 3103 | |||
| 3104 | const ptr_info = operand_ty.ptrInfo(zcu); | ||
| 3105 | return switch (ptr_info.flags.size) { | ||
| 3106 | .many, .slice => unreachable, // cannot be dereferenced | ||
| 3107 | .c => ptr: { | ||
| 3108 | var single_ptr_flags = ptr_info.flags; | ||
| 3109 | single_ptr_flags.size = .one; | ||
| 3110 | single_ptr_flags.is_allowzero = false; | ||
| 3111 | const single_ptr_ty = try pt.ptrType(.{ | ||
| 3112 | .child = ptr_info.child, | ||
| 3113 | .flags = single_ptr_flags, | ||
| 3114 | }); | ||
| 3115 | break :ptr try sema.coerceCompatiblePtrs(block, single_ptr_ty, operand, ptr_src); | ||
| 3116 | }, | ||
| 3117 | .one => operand, | ||
| 3118 | }; | ||
| 3119 | } | ||
| 3120 | |||
| 3121 | fn validateDeref( | ||
| 3122 | sema: *Sema, | ||
| 3123 | block: *Block, | ||
| 3124 | src: LazySrcLoc, | ||
| 3125 | ref: Air.Inst.Ref, | ||
| 3126 | ty: Type, | ||
| 3127 | ) CompileError!void { | ||
| 3128 | const pt = sema.pt; | ||
| 3129 | const zcu = pt.zcu; | ||
| 3130 | if (ty.zigTypeTag(zcu) != .pointer) { | ||
| 3131 | return sema.fail(block, src, "cannot dereference non-pointer type '{f}'", .{ty.fmt(pt)}); | ||
| 3132 | } else switch (ty.ptrSize(zcu)) { | ||
| 3133 | .one, .c => {}, | ||
| 3134 | .many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{f}'", .{ty.fmt(pt)}), | ||
| 3135 | .slice => return sema.fail(block, src, "index syntax required for slice type '{f}'", .{ty.fmt(pt)}), | ||
| 3136 | } | ||
| 3137 | if (sema.resolveValue(ref)) |val| { | ||
| 3138 | // Error for deref of undef pointer, unless the pointee is OPV in which case it's legal. | ||
| 3139 | if (val.isUndef(zcu) and ty.childType(zcu).classify(zcu) != .one_possible_value) { | ||
| 3140 | return sema.fail(block, src, "cannot dereference undefined value", .{}); | ||
| 3141 | } | ||
| 3142 | } | ||
| 3143 | } | ||
| 3144 | |||
| 3085 | fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { | 3145 | fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 3086 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; | 3146 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; |
| 3087 | const operand = sema.resolveInst(inst_data.operand); | 3147 | const operand = sema.resolveInst(inst_data.operand); |
| ... | @@ -4606,30 +4666,6 @@ fn zirValidatePtrArrayInit( | ... | @@ -4606,30 +4666,6 @@ fn zirValidatePtrArrayInit( |
| 4606 | } | 4666 | } |
| 4607 | } | 4667 | } |
| 4608 | 4668 | ||
| 4609 | fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { | ||
| 4610 | const pt = sema.pt; | ||
| 4611 | const zcu = pt.zcu; | ||
| 4612 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; | ||
| 4613 | const src = block.nodeOffset(inst_data.src_node); | ||
| 4614 | const operand = sema.resolveInst(inst_data.operand); | ||
| 4615 | const operand_ty = sema.typeOf(operand); | ||
| 4616 | |||
| 4617 | if (operand_ty.zigTypeTag(zcu) != .pointer) { | ||
| 4618 | return sema.fail(block, src, "cannot dereference non-pointer type '{f}'", .{operand_ty.fmt(pt)}); | ||
| 4619 | } else switch (operand_ty.ptrSize(zcu)) { | ||
| 4620 | .one, .c => {}, | ||
| 4621 | .many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{f}'", .{operand_ty.fmt(pt)}), | ||
| 4622 | .slice => return sema.fail(block, src, "index syntax required for slice type '{f}'", .{operand_ty.fmt(pt)}), | ||
| 4623 | } | ||
| 4624 | |||
| 4625 | if (sema.resolveValue(operand)) |val| { | ||
| 4626 | // Error for deref of undef pointer, unless the pointee is OPV in which case it's legal. | ||
| 4627 | if (val.isUndef(zcu) and operand_ty.childType(zcu).classify(zcu) != .one_possible_value) { | ||
| 4628 | return sema.fail(block, src, "cannot dereference undefined value", .{}); | ||
| 4629 | } | ||
| 4630 | } | ||
| 4631 | } | ||
| 4632 | |||
| 4633 | fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { | 4669 | fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 4634 | const pt = sema.pt; | 4670 | const pt = sema.pt; |
| 4635 | const zcu = pt.zcu; | 4671 | const zcu = pt.zcu; |
src/Zcu.zig+1-1| ... | @@ -1531,7 +1531,7 @@ pub const SrcLoc = struct { | ... | @@ -1531,7 +1531,7 @@ pub const SrcLoc = struct { |
| 1531 | .node_offset_deref_ptr => |node_off| { | 1531 | .node_offset_deref_ptr => |node_off| { |
| 1532 | const tree = try src_loc.file_scope.getTree(zcu); | 1532 | const tree = try src_loc.file_scope.getTree(zcu); |
| 1533 | const node = node_off.toAbsolute(src_loc.base_node); | 1533 | const node = node_off.toAbsolute(src_loc.base_node); |
| 1534 | return tree.nodeToSpan(node); | 1534 | return tree.nodeToSpan(tree.nodeData(node).node); |
| 1535 | }, | 1535 | }, |
| 1536 | .node_offset_asm_source => |node_off| { | 1536 | .node_offset_asm_source => |node_off| { |
| 1537 | const tree = try src_loc.file_scope.getTree(zcu); | 1537 | const tree = try src_loc.file_scope.getTree(zcu); |
src/print_zir.zig+2-1| ... | @@ -204,6 +204,8 @@ const Writer = struct { | ... | @@ -204,6 +204,8 @@ const Writer = struct { |
| 204 | .ensure_result_used, | 204 | .ensure_result_used, |
| 205 | .ensure_result_non_error, | 205 | .ensure_result_non_error, |
| 206 | .ensure_err_union_payload_void, | 206 | .ensure_err_union_payload_void, |
| 207 | .deref, | ||
| 208 | .ref_deref, | ||
| 207 | .ret_node, | 209 | .ret_node, |
| 208 | .ret_load, | 210 | .ret_load, |
| 209 | .resolve_inferred_alloc, | 211 | .resolve_inferred_alloc, |
| ... | @@ -260,7 +262,6 @@ const Writer = struct { | ... | @@ -260,7 +262,6 @@ const Writer = struct { |
| 260 | .bit_reverse, | 262 | .bit_reverse, |
| 261 | .@"resume", | 263 | .@"resume", |
| 262 | .make_ptr_const, | 264 | .make_ptr_const, |
| 263 | .validate_deref, | ||
| 264 | .validate_const, | 265 | .validate_const, |
| 265 | .check_comptime_control_flow, | 266 | .check_comptime_control_flow, |
| 266 | .opt_eu_base_ptr_init, | 267 | .opt_eu_base_ptr_init, |
test/behavior/array.zig+25| ... | @@ -1136,3 +1136,28 @@ test "resist alias of explicit copy of array passed as arg" { | ... | @@ -1136,3 +1136,28 @@ test "resist alias of explicit copy of array passed as arg" { |
| 1136 | 1136 | ||
| 1137 | try expect(buf_b[0] == 1234); | 1137 | try expect(buf_b[0] == 1234); |
| 1138 | } | 1138 | } |
| 1139 | |||
| 1140 | test "access element through reference" { | ||
| 1141 | const S = struct { | ||
| 1142 | fn doTheTest(x: u8) !void { | ||
| 1143 | { | ||
| 1144 | var val: [1]u8 = .{x}; | ||
| 1145 | const single_ptr: *[1]u8 = &val; | ||
| 1146 | try expect(single_ptr.*[0] == x); | ||
| 1147 | const elem_ptr = &single_ptr.*[0]; | ||
| 1148 | comptime assert(@TypeOf(elem_ptr) == *u8); | ||
| 1149 | try expect(elem_ptr.* == x); | ||
| 1150 | } | ||
| 1151 | { | ||
| 1152 | var val: [1]u8 = .{x}; | ||
| 1153 | const c_ptr: [*c][1]u8 = &val; | ||
| 1154 | try expect(c_ptr.*[0] == x); | ||
| 1155 | const elem_ptr = &c_ptr.*[0]; | ||
| 1156 | comptime assert(@TypeOf(elem_ptr) == *u8); | ||
| 1157 | try expect(elem_ptr.* == x); | ||
| 1158 | } | ||
| 1159 | } | ||
| 1160 | }; | ||
| 1161 | try comptime S.doTheTest(123); | ||
| 1162 | try S.doTheTest(123); | ||
| 1163 | } |
test/behavior/pointers.zig+1-1| ... | @@ -793,6 +793,6 @@ test "comptime C pointer to optional pointer" { | ... | @@ -793,6 +793,6 @@ test "comptime C pointer to optional pointer" { |
| 793 | const opt: ?*u8 = @ptrFromInt(0x1000); | 793 | const opt: ?*u8 = @ptrFromInt(0x1000); |
| 794 | const outer_ptr: [*c]const ?*u8 = &opt; | 794 | const outer_ptr: [*c]const ?*u8 = &opt; |
| 795 | const inner_ptr = &outer_ptr.*.?; | 795 | const inner_ptr = &outer_ptr.*.?; |
| 796 | comptime assert(@TypeOf(inner_ptr) == [*c]const *u8); | 796 | comptime assert(@TypeOf(inner_ptr) == *const *u8); |
| 797 | comptime assert(@intFromPtr(inner_ptr.*) == 0x1000); | 797 | comptime assert(@intFromPtr(inner_ptr.*) == 0x1000); |
| 798 | } | 798 | } |
test/cases/compile_errors/deref_ptr_to_comptime_only_type.zig created+9| ... | @@ -0,0 +1,9 @@ | ||
| 1 | export fn entry(ptr: *anyopaque) void { | ||
| 2 | const ct_only: *type = @ptrCast(ptr); | ||
| 3 | _ = ct_only.*; | ||
| 4 | } | ||
| 5 | |||
| 6 | // error | ||
| 7 | // | ||
| 8 | // :3:16: error: cannot load comptime-only type 'type' | ||
| 9 | // :3:9: note: pointer of type '*type' is runtime-known | ||
test/cases/compile_errors/ref_deref_of_null_c_ptr.zig created+8| ... | @@ -0,0 +1,8 @@ | ||
| 1 | export fn entry() void { | ||
| 2 | const ptr: [*c]u8 = null; | ||
| 3 | _ = &ptr.*; | ||
| 4 | } | ||
| 5 | |||
| 6 | // error | ||
| 7 | // | ||
| 8 | // :3:10: error: null pointer casted to type '*u8' | ||