stage2: add helpful error message for invalid for operands

This commit is contained in:
Vexu 2020-08-25 19:31:39 +03:00 committed by Andrew Kelley
parent b1aa2857ff
commit bcd04089eb
4 changed files with 26 additions and 0 deletions

View File

@ -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,

View File

@ -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

View File

@ -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,

View File

@ -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