aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-02-17 19:54:26 -0700
committerAndrew Kelley <andrew@ziglang.org>2023-02-18 19:17:21 -0700
commit321ccbdc525ab0f5862e42378b962c10ec54e4a1 (patch)
treec3f7ddbd4e78b3aea1905f32c6c314f1933722c2 /src/Sema.zig
parent5029e5364caccac07f2296c1f30f2f238f13864d (diff)
downloadzig-321ccbdc525ab0f5862e42378b962c10ec54e4a1.tar.gz
zig-321ccbdc525ab0f5862e42378b962c10ec54e4a1.zip
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.
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig157
1 files changed, 121 insertions, 36 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index cb40a85364..aeb4e25a24 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -3378,26 +3378,7 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
else
object_ty;
- if (!array_ty.isIndexable()) {
- const msg = msg: {
- const msg = try sema.errMsg(
- block,
- src,
- "type '{}' does not support indexing",
- .{array_ty.fmt(sema.mod)},
- );
- errdefer msg.destroy(sema.gpa);
- try sema.errNote(
- block,
- src,
- msg,
- "for loop operand must be an array, slice, tuple, or vector",
- .{},
- );
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(msg);
- }
+ try checkIndexable(sema, block, src, array_ty);
return sema.fieldVal(block, src, object, "len", src);
}
@@ -3921,13 +3902,70 @@ fn zirFieldBasePtr(
}
fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const gpa = sema.gpa;
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
const args = sema.code.refSlice(extra.end, extra.data.operands_len);
const src = inst_data.src();
- _ = args;
- return sema.fail(block, src, "TODO implement zirForCheckLens", .{});
+ var len: Air.Inst.Ref = .none;
+ var len_val: ?Value = null;
+ var len_idx: usize = undefined;
+ var any_runtime = false;
+
+ const runtime_arg_lens = try gpa.alloc(Air.Inst.Ref, args.len);
+ defer gpa.free(runtime_arg_lens);
+
+ // First pass to look for comptime values.
+ for (args) |zir_arg, i| {
+ runtime_arg_lens[i] = .none;
+ if (zir_arg == .none) continue;
+ const object = try sema.resolveInst(zir_arg);
+ const object_ty = sema.typeOf(object);
+ // Each arg could be an indexable, or a range, in which case the length
+ // is passed directly as an integer.
+ const arg_len = if (object_ty.zigTypeTag() == .Int) object else l: {
+ try checkIndexable(sema, block, src, object_ty);
+ if (!object_ty.indexableHasLen()) continue;
+
+ break :l try sema.fieldVal(block, src, object, "len", src);
+ };
+ if (len == .none) {
+ len = arg_len;
+ len_idx = i;
+ }
+ if (try sema.resolveDefinedValue(block, src, arg_len)) |arg_val| {
+ if (len_val) |v| {
+ if (!(try sema.valuesEqual(arg_val, v, Type.usize))) {
+ // TODO error notes for each arg stating the differing values
+ return sema.fail(block, src, "non-matching for loop lengths", .{});
+ }
+ } else {
+ len = arg_len;
+ len_val = arg_val;
+ len_idx = i;
+ }
+ continue;
+ }
+ runtime_arg_lens[i] = arg_len;
+ any_runtime = true;
+ }
+
+ if (len == .none) {
+ return sema.fail(block, src, "non-obvious infinite loop", .{});
+ }
+
+ // Now for the runtime checks.
+ if (any_runtime and block.wantSafety()) {
+ for (runtime_arg_lens) |arg_len, i| {
+ if (arg_len == .none) continue;
+ if (i == len_idx) continue;
+ const ok = try block.addBinOp(.cmp_eq, len, arg_len);
+ try sema.addSafetyCheck(block, ok, .for_len_mismatch);
+ }
+ }
+
+ return len;
}
fn validateArrayInitTy(
@@ -9655,7 +9693,7 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
const array_ptr = try sema.resolveInst(extra.lhs);
const elem_index = try sema.resolveInst(extra.rhs);
- return sema.elemPtr(block, src, array_ptr, elem_index, src, false);
+ return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false);
}
fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -22687,6 +22725,7 @@ pub const PanicId = enum {
unwrap_error,
index_out_of_bounds,
start_index_greater_than_end,
+ for_len_mismatch,
};
fn addSafetyCheck(
@@ -24076,21 +24115,46 @@ fn elemPtr(
.Pointer => indexable_ptr_ty.elemType(),
else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(sema.mod)}),
};
+ switch (indexable_ty.zigTypeTag()) {
+ .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init),
+ .Struct => {
+ // Tuple field access.
+ const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known");
+ const index = @intCast(u32, index_val.toUnsignedInt(target));
+ return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init);
+ },
+ else => {
+ const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
+ return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init);
+ },
+ }
+}
+
+fn elemPtrOneLayerOnly(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ indexable: Air.Inst.Ref,
+ elem_index: Air.Inst.Ref,
+ elem_index_src: LazySrcLoc,
+ init: bool,
+) CompileError!Air.Inst.Ref {
+ const indexable_src = src; // TODO better source location
+ const indexable_ty = sema.typeOf(indexable);
if (!indexable_ty.isIndexable()) {
return sema.fail(block, src, "element access of non-indexable type '{}'", .{indexable_ty.fmt(sema.mod)});
}
+ const target = sema.mod.getTarget();
switch (indexable_ty.zigTypeTag()) {
.Pointer => {
- // In all below cases, we have to deref the ptr operand to get the actual indexable pointer.
- const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
switch (indexable_ty.ptrSize()) {
- .Slice => return sema.elemPtrSlice(block, src, indexable_ptr_src, indexable, elem_index_src, elem_index),
+ .Slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index),
.Many, .C => {
- const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_ptr_src, indexable);
+ const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
const runtime_src = rs: {
- const ptr_val = maybe_ptr_val orelse break :rs indexable_ptr_src;
+ const ptr_val = maybe_ptr_val orelse break :rs indexable_src;
const index_val = maybe_index_val orelse break :rs elem_index_src;
const index = @intCast(usize, index_val.toUnsignedInt(target));
const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);
@@ -24104,18 +24168,16 @@ fn elemPtr(
},
.One => {
assert(indexable_ty.childType().zigTypeTag() == .Array); // Guaranteed by isIndexable
- return sema.elemPtrArray(block, src, indexable_ptr_src, indexable, elem_index_src, elem_index, init);
+ return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init);
},
}
},
- .Array, .Vector => return sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init),
- .Struct => {
- // Tuple field access.
- const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known");
- const index = @intCast(u32, index_val.toUnsignedInt(target));
- return sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init);
+ else => {
+ // TODO add note pointing at corresponding for loop input and suggest using '&'
+ return sema.fail(block, indexable_src, "pointer capture of non pointer type '{}'", .{
+ indexable_ty.fmt(sema.mod),
+ });
},
- else => unreachable,
}
}
@@ -30202,6 +30264,29 @@ fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_
}
}
+fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, array_ty: Type) !void {
+ if (!array_ty.isIndexable()) {
+ const msg = msg: {
+ const msg = try sema.errMsg(
+ block,
+ src,
+ "type '{}' does not support indexing",
+ .{array_ty.fmt(sema.mod)},
+ );
+ errdefer msg.destroy(sema.gpa);
+ try sema.errNote(
+ block,
+ src,
+ msg,
+ "for loop operand must be an array, slice, tuple, or vector",
+ .{},
+ );
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
+ }
+}
+
fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
const resolved_ty = try sema.resolveTypeFields(ty);
const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;