diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index cb35b0070a..8a7846453c 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -1235,6 +1235,7 @@ fn forExpr(mod: *Module, scope: *Scope, rl: ResultLoc, for_node: *ast.Node.For) break :blk index_ptr; }; const array_ptr = try expr(mod, &for_scope.base, .ref, for_node.array_expr); + _ = try addZIRUnOp(mod, &for_scope.base, for_node.array_expr.firstToken(), .ensure_indexable, array_ptr); const cond_src = tree.token_locs[for_node.array_expr.firstToken()].start; const len_ptr = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.FieldPtr, .{ .object_ptr = array_ptr, diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 82079aa9f7..13024f34de 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -2675,6 +2675,13 @@ pub const Type = extern union { }; } + pub fn isIndexable(self: Type) bool { + const zig_tag = self.zigTypeTag(); + // TODO tuples are indexable + return zig_tag == .Array or zig_tag == .Vector or self.isSlice() or + (self.isSinglePointer() and self.elemType().zigTypeTag() == .Array); + } + /// This enum does not directly correspond to `std.builtin.TypeId` because /// it has extra enum tags in it, as a way of using less memory. For example, /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index c552f28553..e06ce1fcca 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -137,6 +137,8 @@ pub const Inst = struct { ensure_result_used, /// Emits a compile error if an error is ignored. ensure_result_non_error, + /// Emits a compile error if operand cannot be indexed. + ensure_indexable, /// Create a `E!T` type. error_union_type, /// Create an error set. @@ -278,6 +280,7 @@ pub const Inst = struct { .alloc, .ensure_result_used, .ensure_result_non_error, + .ensure_indexable, .bitcast_result_ptr, .ref, .bitcast_ref, @@ -409,6 +412,7 @@ pub const Inst = struct { .elemptr, .ensure_result_used, .ensure_result_non_error, + .ensure_indexable, .@"export", .floatcast, .fieldptr, diff --git a/src-self-hosted/zir_sema.zig b/src-self-hosted/zir_sema.zig index 7106bda090..d98e190431 100644 --- a/src-self-hosted/zir_sema.zig +++ b/src-self-hosted/zir_sema.zig @@ -48,6 +48,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .declval_in_module => return analyzeInstDeclValInModule(mod, scope, old_inst.castTag(.declval_in_module).?), .ensure_result_used => return analyzeInstEnsureResultUsed(mod, scope, old_inst.castTag(.ensure_result_used).?), .ensure_result_non_error => return analyzeInstEnsureResultNonError(mod, scope, old_inst.castTag(.ensure_result_non_error).?), + .ensure_indexable => return analyzeInstEnsureIndexable(mod, scope, old_inst.castTag(.ensure_indexable).?), .ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?), .ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?), .ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?), @@ -382,6 +383,19 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst. } } +fn analyzeInstEnsureIndexable(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + const operand = try resolveInst(mod, scope, inst.positionals.operand); + const elem_ty = operand.ty.elemType(); + if (elem_ty.isIndexable()) { + return mod.constVoid(scope, operand.src); + } else { + // TODO error notes + // error: type '{}' does not support indexing + // note: for loop operand must be an array, a slice or a tuple + return mod.fail(scope, operand.src, "for loop operand must be an array, a slice or a tuple", .{}); + } +} + fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { const var_type = try resolveType(mod, scope, inst.positionals.operand); // TODO this should happen only for var allocs