authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-17 19:54:26-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-18 19:17:21-07:00
log321ccbdc525ab0f5862e42378b962c10ec54e4a1
treec3f7ddbd4e78b3aea1905f32c6c314f1933722c2
parent5029e5364caccac07f2296c1f30f2f238f13864d

Sema: implement for_len

This also makes another breaking change to for loops: in order to capture a pointer of an element, one must take the address of array values. This simplifies a lot of things, and makes more sense than how it was before semantically. It is still legal to use a for loop on an array value if the corresponding element capture is byval instead of byref.

4 files changed, 136 insertions(+), 38 deletions(-)

lib/std/builtin.zig+1
......@@ -975,6 +975,7 @@ pub const panic_messages = struct {
975975 pub const unwrap_error = "attempt to unwrap error";
976976 pub const index_out_of_bounds = "index out of bounds";
977977 pub const start_index_greater_than_end = "start index is larger than end index";
978 pub const for_len_mismatch = "for loop over objects with non-equal lengths";
978979};
979980
980981pub noinline fn returnError(st: *StackTrace) void {
src/AstGen.zig+1-2
......@@ -6381,11 +6381,10 @@ fn forExpr(
63816381 lens[i] = range_len;
63826382 } else {
63836383 const indexable = try expr(parent_gz, scope, .{ .rl = .none }, input);
6384 const indexable_len = try parent_gz.addUnNode(.indexable_ptr_len, indexable, input);
63856384
63866385 any_len_checks = true;
63876386 indexables[i] = indexable;
6388 lens[i] = indexable_len;
6387 lens[i] = indexable;
63896388 }
63906389 }
63916390 }
src/Sema.zig+121-36
......@@ -3378,26 +3378,7 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
33783378 else
33793379 object_ty;
33803380
3381 if (!array_ty.isIndexable()) {
3382 const msg = msg: {
3383 const msg = try sema.errMsg(
3384 block,
3385 src,
3386 "type '{}' does not support indexing",
3387 .{array_ty.fmt(sema.mod)},
3388 );
3389 errdefer msg.destroy(sema.gpa);
3390 try sema.errNote(
3391 block,
3392 src,
3393 msg,
3394 "for loop operand must be an array, slice, tuple, or vector",
3395 .{},
3396 );
3397 break :msg msg;
3398 };
3399 return sema.failWithOwnedErrorMsg(msg);
3400 }
3381 try checkIndexable(sema, block, src, array_ty);
34013382
34023383 return sema.fieldVal(block, src, object, "len", src);
34033384}
......@@ -3921,13 +3902,70 @@ fn zirFieldBasePtr(
39213902}
39223903
39233904fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
3905 const gpa = sema.gpa;
39243906 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
39253907 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
39263908 const args = sema.code.refSlice(extra.end, extra.data.operands_len);
39273909 const src = inst_data.src();
39283910
3929 _ = args;
3930 return sema.fail(block, src, "TODO implement zirForCheckLens", .{});
3911 var len: Air.Inst.Ref = .none;
3912 var len_val: ?Value = null;
3913 var len_idx: usize = undefined;
3914 var any_runtime = false;
3915
3916 const runtime_arg_lens = try gpa.alloc(Air.Inst.Ref, args.len);
3917 defer gpa.free(runtime_arg_lens);
3918
3919 // First pass to look for comptime values.
3920 for (args) |zir_arg, i| {
3921 runtime_arg_lens[i] = .none;
3922 if (zir_arg == .none) continue;
3923 const object = try sema.resolveInst(zir_arg);
3924 const object_ty = sema.typeOf(object);
3925 // Each arg could be an indexable, or a range, in which case the length
3926 // is passed directly as an integer.
3927 const arg_len = if (object_ty.zigTypeTag() == .Int) object else l: {
3928 try checkIndexable(sema, block, src, object_ty);
3929 if (!object_ty.indexableHasLen()) continue;
3930
3931 break :l try sema.fieldVal(block, src, object, "len", src);
3932 };
3933 if (len == .none) {
3934 len = arg_len;
3935 len_idx = i;
3936 }
3937 if (try sema.resolveDefinedValue(block, src, arg_len)) |arg_val| {
3938 if (len_val) |v| {
3939 if (!(try sema.valuesEqual(arg_val, v, Type.usize))) {
3940 // TODO error notes for each arg stating the differing values
3941 return sema.fail(block, src, "non-matching for loop lengths", .{});
3942 }
3943 } else {
3944 len = arg_len;
3945 len_val = arg_val;
3946 len_idx = i;
3947 }
3948 continue;
3949 }
3950 runtime_arg_lens[i] = arg_len;
3951 any_runtime = true;
3952 }
3953
3954 if (len == .none) {
3955 return sema.fail(block, src, "non-obvious infinite loop", .{});
3956 }
3957
3958 // Now for the runtime checks.
3959 if (any_runtime and block.wantSafety()) {
3960 for (runtime_arg_lens) |arg_len, i| {
3961 if (arg_len == .none) continue;
3962 if (i == len_idx) continue;
3963 const ok = try block.addBinOp(.cmp_eq, len, arg_len);
3964 try sema.addSafetyCheck(block, ok, .for_len_mismatch);
3965 }
3966 }
3967
3968 return len;
39313969}
39323970
39333971fn validateArrayInitTy(
......@@ -9655,7 +9693,7 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
96559693 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
96569694 const array_ptr = try sema.resolveInst(extra.lhs);
96579695 const elem_index = try sema.resolveInst(extra.rhs);
9658 return sema.elemPtr(block, src, array_ptr, elem_index, src, false);
9696 return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false);
96599697}
96609698
96619699fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -22687,6 +22725,7 @@ pub const PanicId = enum {
2268722725 unwrap_error,
2268822726 index_out_of_bounds,
2268922727 start_index_greater_than_end,
22728 for_len_mismatch,
2269022729};
2269122730
2269222731fn addSafetyCheck(
......@@ -24076,21 +24115,46 @@ fn elemPtr(
2407624115 .Pointer => indexable_ptr_ty.elemType(),
2407724116 else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(sema.mod)}),
2407824117 };
24118 switch (indexable_ty.zigTypeTag()) {
24119 .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init),
24120 .Struct => {
24121 // Tuple field access.
24122 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known");
24123 const index = @intCast(u32, index_val.toUnsignedInt(target));
24124 return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init);
24125 },
24126 else => {
24127 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
24128 return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init);
24129 },
24130 }
24131}
24132
24133fn elemPtrOneLayerOnly(
24134 sema: *Sema,
24135 block: *Block,
24136 src: LazySrcLoc,
24137 indexable: Air.Inst.Ref,
24138 elem_index: Air.Inst.Ref,
24139 elem_index_src: LazySrcLoc,
24140 init: bool,
24141) CompileError!Air.Inst.Ref {
24142 const indexable_src = src; // TODO better source location
24143 const indexable_ty = sema.typeOf(indexable);
2407924144 if (!indexable_ty.isIndexable()) {
2408024145 return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(sema.mod)});
2408124146 }
24147 const target = sema.mod.getTarget();
2408224148
2408324149 switch (indexable_ty.zigTypeTag()) {
2408424150 .Pointer => {
24085 // In all below cases, we have to deref the ptr operand to get the actual indexable pointer.
24086 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
2408724151 switch (indexable_ty.ptrSize()) {
24088 .Slice => return sema.elemPtrSlice(block, src, indexable_ptr_src, indexable, elem_index_src, elem_index),
24152 .Slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index),
2408924153 .Many, .C => {
24090 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_ptr_src, indexable);
24154 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
2409124155 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
2409224156 const runtime_src = rs: {
24093 const ptr_val = maybe_ptr_val orelse break :rs indexable_ptr_src;
24157 const ptr_val = maybe_ptr_val orelse break :rs indexable_src;
2409424158 const index_val = maybe_index_val orelse break :rs elem_index_src;
2409524159 const index = @intCast(usize, index_val.toUnsignedInt(target));
2409624160 const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);
......@@ -24104,18 +24168,16 @@ fn elemPtr(
2410424168 },
2410524169 .One => {
2410624170 assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
24107 return sema.elemPtrArray(block, src, indexable_ptr_src, indexable, elem_index_src, elem_index, init);
24171 return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init);
2410824172 },
2410924173 }
2411024174 },
24111 .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init),
24112 .Struct => {
24113 // Tuple field access.
24114 const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known");
24115 const index = @intCast(u32, index_val.toUnsignedInt(target));
24116 return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init);
24175 else => {
24176 // TODO add note pointing at corresponding for loop input and suggest using '&'
24177 return sema.fail(block, indexable_src, "pointer capture of non pointer type '{}'", .{
24178 indexable_ty.fmt(sema.mod),
24179 });
2411724180 },
24118 else => unreachable,
2411924181 }
2412024182}
2412124183
......@@ -30202,6 +30264,29 @@ fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_
3020230264 }
3020330265}
3020430266
30267fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, array_ty: Type) !void {
30268 if (!array_ty.isIndexable()) {
30269 const msg = msg: {
30270 const msg = try sema.errMsg(
30271 block,
30272 src,
30273 "type '{}' does not support indexing",
30274 .{array_ty.fmt(sema.mod)},
30275 );
30276 errdefer msg.destroy(sema.gpa);
30277 try sema.errNote(
30278 block,
30279 src,
30280 msg,
30281 "for loop operand must be an array, slice, tuple, or vector",
30282 .{},
30283 );
30284 break :msg msg;
30285 };
30286 return sema.failWithOwnedErrorMsg(msg);
30287 }
30288}
30289
3020530290fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
3020630291 const resolved_ty = try sema.resolveTypeFields(ty);
3020730292 const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;
src/type.zig+13
......@@ -5326,6 +5326,19 @@ pub const Type = extern union {
53265326 };
53275327 }
53285328
5329 pub fn indexableHasLen(ty: Type) bool {
5330 return switch (ty.zigTypeTag()) {
5331 .Array, .Vector => true,
5332 .Pointer => switch (ty.ptrSize()) {
5333 .Many, .C => false,
5334 .Slice => true,
5335 .One => ty.elemType().zigTypeTag() == .Array,
5336 },
5337 .Struct => ty.isTuple(),
5338 else => false,
5339 };
5340 }
5341
53295342 /// Returns null if the type has no namespace.
53305343 pub fn getNamespace(self: Type) ?*Module.Namespace {
53315344 return switch (self.tag()) {