astgen: eliminate rlWrapPtr and all its callsites

The following AST avoids unnecessary derefs now:
 * error set decl
 * field access
 * array access
 * for loops: replace ensure_indexable and deref on the len_ptr with a
   special purpose ZIR instruction called indexable_ptr_len.

Added an error note when for loop operand is the wrong type.

I also accidentally implemented `@field`.
This commit is contained in:
Andrew Kelley 2021-01-19 00:38:53 -07:00
parent 7e56028bc7
commit 30a824cb9e
8 changed files with 394 additions and 222 deletions

View File

@ -2357,6 +2357,11 @@ pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*De
return self.decl_table.get(name_hash);
}
pub fn analyzeDeclVal(mod: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
const decl_ref = try mod.analyzeDeclRef(scope, src, decl);
return mod.analyzeDeref(scope, src, decl_ref, src);
}
pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
const scope_decl = scope.ownerDecl().?;
try self.declareDeclDependency(scope_decl, decl);
@ -2408,6 +2413,20 @@ fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) Inner
return &inst.base;
}
pub fn analyzeRef(mod: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst {
const ptr_type = try mod.simplePtrType(scope, src, operand.ty, false, .One);
if (operand.value()) |val| {
return mod.constInst(scope, src, .{
.ty = ptr_type,
.val = try Value.Tag.ref_val.create(scope.arena(), val),
});
}
const b = try mod.requireRuntimeBlock(scope, src);
return mod.addUnOp(b, src, ptr_type, .ref, operand);
}
pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_src: usize) InnerError!*Inst {
const elem_ty = switch (ptr.ty.zigTypeTag()) {
.Pointer => ptr.ty.elemType(),
@ -3543,3 +3562,147 @@ pub fn emitBackwardBranch(mod: *Module, block: *Scope.Block, src: usize) !void {
});
}
}
pub fn namedFieldPtr(
mod: *Module,
scope: *Scope,
src: usize,
object_ptr: *Inst,
field_name: []const u8,
field_name_src: usize,
) InnerError!*Inst {
const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
.Pointer => object_ptr.ty.elemType(),
else => return mod.fail(scope, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
};
switch (elem_ty.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) {
return mod.constInst(scope, src, .{
.ty = Type.initTag(.single_const_pointer_to_comptime_int),
.val = try Value.Tag.ref_val.create(
scope.arena(),
try Value.Tag.int_u64.create(scope.arena(), elem_ty.arrayLen()),
),
});
} else {
return mod.fail(
scope,
field_name_src,
"no member named '{s}' in '{}'",
.{ field_name, elem_ty },
);
}
},
.Pointer => {
const ptr_child = elem_ty.elemType();
switch (ptr_child.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) {
return mod.constInst(scope, src, .{
.ty = Type.initTag(.single_const_pointer_to_comptime_int),
.val = try Value.Tag.ref_val.create(
scope.arena(),
try Value.Tag.int_u64.create(scope.arena(), ptr_child.arrayLen()),
),
});
} else {
return mod.fail(
scope,
field_name_src,
"no member named '{s}' in '{}'",
.{ field_name, elem_ty },
);
}
},
else => {},
}
},
.Type => {
_ = try mod.resolveConstValue(scope, object_ptr);
const result = try mod.analyzeDeref(scope, src, object_ptr, object_ptr.src);
const val = result.value().?;
const child_type = try val.toType(scope.arena());
switch (child_type.zigTypeTag()) {
.ErrorSet => {
// TODO resolve inferred error sets
const entry = if (val.castTag(.error_set)) |payload|
(payload.data.fields.getEntry(field_name) orelse
return mod.fail(scope, src, "no error named '{s}' in '{}'", .{ field_name, child_type })).*
else
try mod.getErrorValue(field_name);
const result_type = if (child_type.tag() == .anyerror)
try Type.Tag.error_set_single.create(scope.arena(), entry.key)
else
child_type;
return mod.constInst(scope, src, .{
.ty = try mod.simplePtrType(scope, src, result_type, false, .One),
.val = try Value.Tag.ref_val.create(
scope.arena(),
try Value.Tag.@"error".create(scope.arena(), .{
.name = entry.key,
.value = entry.value,
}),
),
});
},
.Struct => {
const container_scope = child_type.getContainerScope();
if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
// TODO if !decl.is_pub and inDifferentFiles() "{} is private"
return mod.analyzeDeclRef(scope, src, decl);
}
if (container_scope.file_scope == mod.root_scope) {
return mod.fail(scope, src, "root source file has no member called '{s}'", .{field_name});
} else {
return mod.fail(scope, src, "container '{}' has no member called '{s}'", .{ child_type, field_name });
}
},
else => return mod.fail(scope, src, "type '{}' does not support field access", .{child_type}),
}
},
else => {},
}
return mod.fail(scope, src, "type '{}' does not support field access", .{elem_ty});
}
pub fn elemPtr(
mod: *Module,
scope: *Scope,
src: usize,
array_ptr: *Inst,
elem_index: *Inst,
) InnerError!*Inst {
const elem_ty = switch (array_ptr.ty.zigTypeTag()) {
.Pointer => array_ptr.ty.elemType(),
else => return mod.fail(scope, array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}),
};
if (!elem_ty.isIndexable()) {
return mod.fail(scope, src, "array access of non-array type '{}'", .{elem_ty});
}
if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) {
// we have to deref the ptr operand to get the actual array pointer
const array_ptr_deref = try mod.analyzeDeref(scope, src, array_ptr, array_ptr.src);
if (array_ptr_deref.value()) |array_ptr_val| {
if (elem_index.value()) |index_val| {
// Both array pointer and index are compile-time known.
const index_u64 = index_val.toUnsignedInt();
// @intCast here because it would have been impossible to construct a value that
// required a larger index.
const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
const pointee_type = elem_ty.elemType().elemType();
return mod.constInst(scope, src, .{
.ty = try Type.Tag.single_const_pointer.create(scope.arena(), pointee_type),
.val = elem_ptr,
});
}
}
}
return mod.fail(scope, src, "TODO implement more analyze elemptr", .{});
}

View File

@ -278,7 +278,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
.ErrorUnion => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)),
.MergeErrorSets => return rlWrap(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)),
.AnyFrameType => return rlWrap(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)),
.ErrorSetDecl => return errorSetDecl(mod, scope, rl, node.castTag(.ErrorSetDecl).?),
.ErrorSetDecl => return rlWrap(mod, scope, rl, try errorSetDecl(mod, scope, node.castTag(.ErrorSetDecl).?)),
.ErrorType => return rlWrap(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),
.For => return forExpr(mod, scope, rl, node.castTag(.For).?),
.ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),
@ -1107,7 +1107,7 @@ fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Con
}
}
fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst {
fn errorSetDecl(mod: *Module, scope: *Scope, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst {
const tree = scope.tree();
const src = tree.token_locs[node.error_token].start;
const decls = node.decls();
@ -1118,9 +1118,7 @@ fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Erro
fields[i] = try mod.identifierTokenString(scope, tag.name_token);
}
// analyzing the error set results in a decl ref, so we might need to dereference it
// TODO remove all callsites to rlWrapPtr
return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}));
return addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{});
}
fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
@ -1299,35 +1297,72 @@ fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: as
return mem.eql(u8, ident_name_1, ident_name_2);
}
pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
const tree = scope.tree();
const src = tree.token_locs[node.token].start;
const ident_name = try mod.identifierTokenString(scope, node.token);
return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
}
fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
pub fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
const tree = scope.tree();
const src = tree.token_locs[node.op_token].start;
// TODO custom AST node for field access so that we don't have to go through a node cast here
const field_name = try mod.identifierTokenString(scope, node.rhs.castTag(.Identifier).?.token);
if (rl == .ref) {
return addZirInstTag(mod, scope, src, .field_ptr, .{
.object = try expr(mod, scope, .ref, node.lhs),
.field_name = field_name,
});
}
return rlWrap(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val, .{
.object = try expr(mod, scope, .none, node.lhs),
.field_name = field_name,
}));
}
const lhs = try expr(mod, scope, .ref, node.lhs);
const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
fn namedField(
mod: *Module,
scope: *Scope,
rl: ResultLoc,
call: *ast.Node.BuiltinCall,
) InnerError!*zir.Inst {
try ensureBuiltinParamCount(mod, scope, call, 2);
// TODO remove all callsites to rlWrapPtr
return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}));
const tree = scope.tree();
const src = tree.token_locs[call.builtin_token].start;
const params = call.params();
const string_type = try addZIRInstConst(mod, scope, src, .{
.ty = Type.initTag(.type),
.val = Value.initTag(.const_slice_u8_type),
});
const string_rl: ResultLoc = .{ .ty = string_type };
if (rl == .ref) {
return addZirInstTag(mod, scope, src, .field_ptr_named, .{
.object = try expr(mod, scope, .ref, params[0]),
.field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
});
}
return rlWrap(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val_named, .{
.object = try expr(mod, scope, .none, params[0]),
.field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
}));
}
fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ArrayAccess) InnerError!*zir.Inst {
const tree = scope.tree();
const src = tree.token_locs[node.rtoken].start;
const usize_type = try addZIRInstConst(mod, scope, src, .{
.ty = Type.initTag(.type),
.val = Value.initTag(.usize_type),
});
const index_rl: ResultLoc = .{ .ty = usize_type };
const array_ptr = try expr(mod, scope, .ref, node.lhs);
const index = try expr(mod, scope, .none, node.index_expr);
// TODO remove all callsites to rlWrapPtr
return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ElemPtr, .{ .array_ptr = array_ptr, .index = index }, .{}));
if (rl == .ref) {
return addZirInstTag(mod, scope, src, .elem_ptr, .{
.array = try expr(mod, scope, .ref, node.lhs),
.index = try expr(mod, scope, index_rl, node.index_expr),
});
}
return rlWrap(mod, scope, rl, try addZirInstTag(mod, scope, src, .elem_val, .{
.array = try expr(mod, scope, .none, node.lhs),
.index = try expr(mod, scope, index_rl, node.index_expr),
}));
}
fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.Slice) InnerError!*zir.Inst {
@ -1819,12 +1854,8 @@ fn forExpr(
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,
.field_name = try addZIRInst(mod, &for_scope.base, cond_src, zir.Inst.Str, .{ .bytes = "len" }, .{}),
}, .{});
const len = try addZIRUnOp(mod, &for_scope.base, cond_src, .indexable_ptr_len, array_ptr);
var loop_scope: Scope.GenZIR = .{
.parent = &for_scope.base,
@ -1845,7 +1876,6 @@ fn forExpr(
// check condition i < array_expr.len
const index = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, index_ptr);
const len = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, len_ptr);
const cond = try addZIRBinOp(mod, &cond_scope.base, cond_src, .cmp_lt, index, len);
const condbr = try addZIRInstSpecial(mod, &cond_scope.base, for_src, zir.Inst.CondBr, .{
@ -2328,8 +2358,9 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
.local_ptr => {
const local_ptr = s.cast(Scope.LocalPtr).?;
if (mem.eql(u8, local_ptr.name, ident_name)) {
// TODO remove all callsites to rlWrapPtr
return rlWrapPtr(mod, scope, rl, local_ptr.ptr);
if (rl == .ref) return local_ptr.ptr;
const loaded = try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr);
return rlWrap(mod, scope, rl, loaded);
}
s = local_ptr.parent;
},
@ -2747,6 +2778,8 @@ fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.Built
return setEvalBranchQuota(mod, scope, call);
} else if (mem.eql(u8, builtin_name, "@compileLog")) {
return compileLog(mod, scope, call);
} else if (mem.eql(u8, builtin_name, "@field")) {
return namedField(mod, scope, rl, call);
} else {
return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{s}'", .{builtin_name});
}
@ -3119,6 +3152,28 @@ fn rlWrapPtr(mod: *Module, scope: *Scope, rl: ResultLoc, ptr: *zir.Inst) InnerEr
return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, ptr.src, .deref, ptr));
}
pub fn addZirInstTag(
mod: *Module,
scope: *Scope,
src: usize,
comptime tag: zir.Inst.Tag,
positionals: std.meta.fieldInfo(tag.Type(), .positionals).field_type,
) !*zir.Inst {
const gen_zir = scope.getGenZIR();
try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
const inst = try gen_zir.arena.create(tag.Type());
inst.* = .{
.base = .{
.tag = tag,
.src = src,
},
.positionals = positionals,
.kw_args = .{},
};
gen_zir.instructions.appendAssumeCapacity(&inst.base);
return &inst.base;
}
pub fn addZIRInstSpecial(
mod: *Module,
scope: *Scope,

View File

@ -47,6 +47,10 @@ pub const Inst = struct {
array_type,
/// Create an array type with sentinel
array_type_sentinel,
/// Given a pointer to an indexable object, returns the len property. This is
/// used by for loops. This instruction also emits a for-loop specific instruction
/// if the indexable object is not indexable.
indexable_ptr_len,
/// Function parameter value. These must be first in a function's main block,
/// in respective order with the parameters.
arg,
@ -142,13 +146,13 @@ pub const Inst = struct {
div,
/// Given a pointer to an array, slice, or pointer, returns a pointer to the element at
/// the provided index.
elemptr,
elem_ptr,
/// Given an array, slice, or pointer, returns the element at the provided index.
elem_val,
/// Emits a compile error if the operand is not `void`.
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.
@ -156,8 +160,17 @@ pub const Inst = struct {
/// Export the provided Decl as the provided name in the compilation's output object file.
@"export",
/// Given a pointer to a struct or object that contains virtual fields, returns a pointer
/// to the named field.
fieldptr,
/// to the named field. The field name is a []const u8. Used by a.b syntax.
field_ptr,
/// Given a struct or object that contains virtual fields, returns the named field.
/// The field name is a []const u8. Used by a.b syntax.
field_val,
/// Given a pointer to a struct or object that contains virtual fields, returns a pointer
/// to the named field. The field name is a comptime instruction. Used by @field.
field_ptr_named,
/// Given a struct or object that contains virtual fields, returns the named field.
/// The field name is a comptime instruction. Used by @field.
field_val_named,
/// Convert a larger float type to any other float type, possibly causing a loss of precision.
floatcast,
/// Declare a function body.
@ -361,7 +374,6 @@ pub const Inst = struct {
.ptrtoint,
.ensure_result_used,
.ensure_result_non_error,
.ensure_indexable,
.bitcast_result_ptr,
.ref,
.bitcast_ref,
@ -391,6 +403,7 @@ pub const Inst = struct {
.bitnot,
.import,
.set_eval_branch_quota,
.indexable_ptr_len,
=> UnOp,
.add,
@ -452,14 +465,15 @@ pub const Inst = struct {
.str => Str,
.int => Int,
.inttype => IntType,
.fieldptr => FieldPtr,
.field_ptr, .field_val => Field,
.field_ptr_named, .field_val_named => FieldNamed,
.@"asm" => Asm,
.@"fn" => Fn,
.@"export" => Export,
.param_type => ParamType,
.primitive => Primitive,
.fntype => FnType,
.elemptr => ElemPtr,
.elem_ptr, .elem_val => Elem,
.condbr => CondBr,
.ptr_type => PtrType,
.enum_literal => EnumLiteral,
@ -490,6 +504,7 @@ pub const Inst = struct {
.array_mul,
.array_type,
.array_type_sentinel,
.indexable_ptr_len,
.arg,
.as,
.@"asm",
@ -523,13 +538,16 @@ pub const Inst = struct {
.declval,
.deref,
.div,
.elemptr,
.elem_ptr,
.elem_val,
.ensure_result_used,
.ensure_result_non_error,
.ensure_indexable,
.@"export",
.floatcast,
.fieldptr,
.field_ptr,
.field_val,
.field_ptr_named,
.field_val_named,
.@"fn",
.fntype,
.int,
@ -823,12 +841,21 @@ pub const Inst = struct {
kw_args: struct {},
};
pub const FieldPtr = struct {
pub const base_tag = Tag.fieldptr;
pub const Field = struct {
base: Inst,
positionals: struct {
object_ptr: *Inst,
object: *Inst,
field_name: []const u8,
},
kw_args: struct {},
};
pub const FieldNamed = struct {
base: Inst,
positionals: struct {
object: *Inst,
field_name: *Inst,
},
kw_args: struct {},
@ -1000,12 +1027,11 @@ pub const Inst = struct {
};
};
pub const ElemPtr = struct {
pub const base_tag = Tag.elemptr;
pub const Elem = struct {
base: Inst,
positionals: struct {
array_ptr: *Inst,
array: *Inst,
index: *Inst,
},
kw_args: struct {},

View File

@ -43,8 +43,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
.inferred_alloc_mut,
),
.arg => return analyzeInstArg(mod, scope, old_inst.castTag(.arg).?),
.bitcast_ref => return analyzeInstBitCastRef(mod, scope, old_inst.castTag(.bitcast_ref).?),
.bitcast_result_ptr => return analyzeInstBitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?),
.bitcast_ref => return bitCastRef(mod, scope, old_inst.castTag(.bitcast_ref).?),
.bitcast_result_ptr => return bitCastResultPtr(mod, scope, old_inst.castTag(.bitcast_result_ptr).?),
.block => return analyzeInstBlock(mod, scope, old_inst.castTag(.block).?, false),
.block_comptime => return analyzeInstBlock(mod, scope, old_inst.castTag(.block_comptime).?, true),
.block_flat => return analyzeInstBlockFlat(mod, scope, old_inst.castTag(.block_flat).?, false),
@ -52,7 +52,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
.@"break" => return analyzeInstBreak(mod, scope, old_inst.castTag(.@"break").?),
.breakpoint => return analyzeInstBreakpoint(mod, scope, old_inst.castTag(.breakpoint).?),
.breakvoid => return analyzeInstBreakVoid(mod, scope, old_inst.castTag(.breakvoid).?),
.call => return analyzeInstCall(mod, scope, old_inst.castTag(.call).?),
.call => return call(mod, scope, old_inst.castTag(.call).?),
.coerce_result_block_ptr => return analyzeInstCoerceResultBlockPtr(mod, scope, old_inst.castTag(.coerce_result_block_ptr).?),
.coerce_result_ptr => return analyzeInstCoerceResultPtr(mod, scope, old_inst.castTag(.coerce_result_ptr).?),
.coerce_to_ptr_elem => return analyzeInstCoerceToPtrElem(mod, scope, old_inst.castTag(.coerce_to_ptr_elem).?),
@ -60,13 +60,13 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
.compilelog => return analyzeInstCompileLog(mod, scope, old_inst.castTag(.compilelog).?),
.@"const" => return analyzeInstConst(mod, scope, old_inst.castTag(.@"const").?),
.dbg_stmt => return analyzeInstDbgStmt(mod, scope, old_inst.castTag(.dbg_stmt).?),
.declref => return analyzeInstDeclRef(mod, scope, old_inst.castTag(.declref).?),
.declref => return declRef(mod, scope, old_inst.castTag(.declref).?),
.declref_str => return analyzeInstDeclRefStr(mod, scope, old_inst.castTag(.declref_str).?),
.declval => return analyzeInstDeclVal(mod, scope, old_inst.castTag(.declval).?),
.declval => return declVal(mod, scope, old_inst.castTag(.declval).?),
.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).?),
.indexable_ptr_len => return indexablePtrLen(mod, scope, old_inst.castTag(.indexable_ptr_len).?),
.ref => return ref(mod, scope, old_inst.castTag(.ref).?),
.resolve_inferred_alloc => return analyzeInstResolveInferredAlloc(mod, scope, old_inst.castTag(.resolve_inferred_alloc).?),
.ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?),
.ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?),
@ -88,7 +88,10 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
.loop => return analyzeInstLoop(mod, scope, old_inst.castTag(.loop).?),
.param_type => return analyzeInstParamType(mod, scope, old_inst.castTag(.param_type).?),
.ptrtoint => return analyzeInstPtrToInt(mod, scope, old_inst.castTag(.ptrtoint).?),
.fieldptr => return analyzeInstFieldPtr(mod, scope, old_inst.castTag(.fieldptr).?),
.field_ptr => return fieldPtr(mod, scope, old_inst.castTag(.field_ptr).?),
.field_val => return fieldVal(mod, scope, old_inst.castTag(.field_val).?),
.field_ptr_named => return fieldPtrNamed(mod, scope, old_inst.castTag(.field_ptr_named).?),
.field_val_named => return fieldValNamed(mod, scope, old_inst.castTag(.field_val_named).?),
.deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?),
.as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?),
.@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?),
@ -103,7 +106,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
.intcast => return analyzeInstIntCast(mod, scope, old_inst.castTag(.intcast).?),
.bitcast => return analyzeInstBitCast(mod, scope, old_inst.castTag(.bitcast).?),
.floatcast => return analyzeInstFloatCast(mod, scope, old_inst.castTag(.floatcast).?),
.elemptr => return analyzeInstElemPtr(mod, scope, old_inst.castTag(.elemptr).?),
.elem_ptr => return elemPtr(mod, scope, old_inst.castTag(.elem_ptr).?),
.elem_val => return elemVal(mod, scope, old_inst.castTag(.elem_val).?),
.add => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.add).?),
.addwrap => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.addwrap).?),
.sub => return analyzeInstArithmetic(mod, scope, old_inst.castTag(.sub).?),
@ -281,16 +285,16 @@ fn analyzeInstCoerceResultBlockPtr(
return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultBlockPtr", .{});
}
fn analyzeInstBitCastRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
fn bitCastRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastRef", .{});
return mod.fail(scope, inst.base.src, "TODO implement zir_sema.bitCastRef", .{});
}
fn analyzeInstBitCastResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
fn bitCastResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
return mod.fail(scope, inst.base.src, "TODO implement analyzeInstBitCastResultPtr", .{});
return mod.fail(scope, inst.base.src, "TODO implement zir_sema.bitCastResultPtr", .{});
}
fn analyzeInstCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
@ -318,21 +322,12 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr
return mod.addNoOp(b, inst.base.src, ptr_type, .alloc);
}
fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
fn ref(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
const operand = try resolveInst(mod, scope, inst.positionals.operand);
const ptr_type = try mod.simplePtrType(scope, inst.base.src, operand.ty, false, .One);
if (operand.value()) |val| {
return mod.constInst(scope, inst.base.src, .{
.ty = ptr_type,
.val = try Value.Tag.ref_val.create(scope.arena(), val),
});
}
const b = try mod.requireRuntimeBlock(scope, inst.base.src);
return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand);
return mod.analyzeRef(scope, inst.base.src, operand);
}
fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
@ -364,19 +359,34 @@ fn analyzeInstEnsureResultNonError(mod: *Module, scope: *Scope, inst: *zir.Inst.
}
}
fn analyzeInstEnsureIndexable(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
fn indexablePtrLen(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
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", .{});
const array_ptr = try resolveInst(mod, scope, inst.positionals.operand);
const elem_ty = array_ptr.ty.elemType();
if (!elem_ty.isIndexable()) {
const msg = msg: {
const msg = try mod.errMsg(
scope,
inst.base.src,
"type '{}' does not support indexing",
.{elem_ty},
);
errdefer msg.destroy(mod.gpa);
try mod.errNote(
scope,
inst.base.src,
msg,
"for loop operand must be an array, slice, tuple, or vector",
.{},
);
break :msg msg;
};
return mod.failWithOwnedErrorMsg(scope, msg);
}
const result_ptr = try mod.namedFieldPtr(scope, inst.base.src, array_ptr, "len", inst.base.src);
return mod.analyzeDeref(scope, inst.base.src, result_ptr, result_ptr.src);
}
fn analyzeInstAlloc(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
@ -826,21 +836,19 @@ fn analyzeInstDeclRefStr(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRefStr
return mod.analyzeDeclRefByName(scope, inst.base.src, decl_name);
}
fn analyzeInstDeclRef(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
fn declRef(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
return mod.analyzeDeclRef(scope, inst.base.src, inst.positionals.decl);
}
fn analyzeInstDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst {
fn declVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
const decl_ref = try mod.analyzeDeclRef(scope, inst.base.src, inst.positionals.decl);
// TODO look into avoiding the call to analyzeDeref here
return mod.analyzeDeref(scope, inst.base.src, decl_ref, inst.base.src);
return mod.analyzeDeclVal(scope, inst.base.src, inst.positionals.decl);
}
fn analyzeInstCall(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
fn call(mod: *Module, scope: *Scope, inst: *zir.Inst.Call) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
@ -1093,7 +1101,7 @@ fn analyzeInstErrorSet(mod: *Module, scope: *Scope, inst: *zir.Inst.ErrorSet) In
.val = Value.initPayload(&payload.base),
});
payload.data.decl = new_decl;
return mod.analyzeDeclRef(scope, inst.base.src, new_decl);
return mod.analyzeDeclVal(scope, inst.base.src, new_decl);
}
fn analyzeInstMergeErrorSets(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
@ -1293,108 +1301,46 @@ fn analyzeInstPtrToInt(mod: *Module, scope: *Scope, ptrtoint: *zir.Inst.UnOp) In
return mod.addUnOp(b, ptrtoint.base.src, ty, .ptrtoint, ptr);
}
fn analyzeInstFieldPtr(mod: *Module, scope: *Scope, fieldptr: *zir.Inst.FieldPtr) InnerError!*Inst {
fn fieldVal(mod: *Module, scope: *Scope, inst: *zir.Inst.Field) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
const object_ptr = try resolveInst(mod, scope, fieldptr.positionals.object_ptr);
const field_name = try resolveConstString(mod, scope, fieldptr.positionals.field_name);
const elem_ty = switch (object_ptr.ty.zigTypeTag()) {
.Pointer => object_ptr.ty.elemType(),
else => return mod.fail(scope, fieldptr.positionals.object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}),
};
switch (elem_ty.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) {
return mod.constInst(scope, fieldptr.base.src, .{
.ty = Type.initTag(.single_const_pointer_to_comptime_int),
.val = try Value.Tag.ref_val.create(
scope.arena(),
try Value.Tag.int_u64.create(scope.arena(), elem_ty.arrayLen()),
),
});
} else {
return mod.fail(
scope,
fieldptr.positionals.field_name.src,
"no member named '{s}' in '{}'",
.{ field_name, elem_ty },
);
}
},
.Pointer => {
const ptr_child = elem_ty.elemType();
switch (ptr_child.zigTypeTag()) {
.Array => {
if (mem.eql(u8, field_name, "len")) {
return mod.constInst(scope, fieldptr.base.src, .{
.ty = Type.initTag(.single_const_pointer_to_comptime_int),
.val = try Value.Tag.ref_val.create(
scope.arena(),
try Value.Tag.int_u64.create(scope.arena(), ptr_child.arrayLen()),
),
});
} else {
return mod.fail(
scope,
fieldptr.positionals.field_name.src,
"no member named '{s}' in '{}'",
.{ field_name, elem_ty },
);
}
},
else => {},
}
},
.Type => {
_ = try mod.resolveConstValue(scope, object_ptr);
const result = try mod.analyzeDeref(scope, fieldptr.base.src, object_ptr, object_ptr.src);
const val = result.value().?;
const child_type = try val.toType(scope.arena());
switch (child_type.zigTypeTag()) {
.ErrorSet => {
// TODO resolve inferred error sets
const entry = if (val.castTag(.error_set)) |payload|
(payload.data.fields.getEntry(field_name) orelse
return mod.fail(scope, fieldptr.base.src, "no error named '{s}' in '{}'", .{ field_name, child_type })).*
else
try mod.getErrorValue(field_name);
const object = try resolveInst(mod, scope, inst.positionals.object);
const field_name = inst.positionals.field_name;
const object_ptr = try mod.analyzeRef(scope, inst.base.src, object);
const result_ptr = try mod.namedFieldPtr(scope, inst.base.src, object_ptr, field_name, inst.base.src);
return mod.analyzeDeref(scope, inst.base.src, result_ptr, result_ptr.src);
}
const result_type = if (child_type.tag() == .anyerror)
try Type.Tag.error_set_single.create(scope.arena(), entry.key)
else
child_type;
fn fieldPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.Field) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
return mod.constInst(scope, fieldptr.base.src, .{
.ty = try mod.simplePtrType(scope, fieldptr.base.src, result_type, false, .One),
.val = try Value.Tag.ref_val.create(
scope.arena(),
try Value.Tag.@"error".create(scope.arena(), .{
.name = entry.key,
.value = entry.value,
}),
),
});
},
.Struct => {
const container_scope = child_type.getContainerScope();
if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| {
// TODO if !decl.is_pub and inDifferentFiles() "{} is private"
return mod.analyzeDeclRef(scope, fieldptr.base.src, decl);
}
const object_ptr = try resolveInst(mod, scope, inst.positionals.object);
const field_name = inst.positionals.field_name;
return mod.namedFieldPtr(scope, inst.base.src, object_ptr, field_name, inst.base.src);
}
if (container_scope.file_scope == mod.root_scope) {
return mod.fail(scope, fieldptr.base.src, "root source file has no member called '{s}'", .{field_name});
} else {
return mod.fail(scope, fieldptr.base.src, "container '{}' has no member called '{s}'", .{ child_type, field_name });
}
},
else => return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{child_type}),
}
},
else => {},
}
return mod.fail(scope, fieldptr.base.src, "type '{}' does not support field access", .{elem_ty});
fn fieldValNamed(mod: *Module, scope: *Scope, inst: *zir.Inst.FieldNamed) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
const object = try resolveInst(mod, scope, inst.positionals.object);
const field_name = try resolveConstString(mod, scope, inst.positionals.field_name);
const fsrc = inst.positionals.field_name.src;
const object_ptr = try mod.analyzeRef(scope, inst.base.src, object);
const result_ptr = try mod.namedFieldPtr(scope, inst.base.src, object_ptr, field_name, fsrc);
return mod.analyzeDeref(scope, inst.base.src, result_ptr, result_ptr.src);
}
fn fieldPtrNamed(mod: *Module, scope: *Scope, inst: *zir.Inst.FieldNamed) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
const object_ptr = try resolveInst(mod, scope, inst.positionals.object);
const field_name = try resolveConstString(mod, scope, inst.positionals.field_name);
const fsrc = inst.positionals.field_name.src;
return mod.namedFieldPtr(scope, inst.base.src, object_ptr, field_name, fsrc);
}
fn analyzeInstIntCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
@ -1481,42 +1427,24 @@ fn analyzeInstFloatCast(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inne
return mod.fail(scope, inst.base.src, "TODO implement analyze widen or shorten float", .{});
}
fn analyzeInstElemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) InnerError!*Inst {
fn elemVal(mod: *Module, scope: *Scope, inst: *zir.Inst.Elem) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
const array_ptr = try resolveInst(mod, scope, inst.positionals.array_ptr);
const uncasted_index = try resolveInst(mod, scope, inst.positionals.index);
const elem_index = try mod.coerce(scope, Type.initTag(.usize), uncasted_index);
const elem_ty = switch (array_ptr.ty.zigTypeTag()) {
.Pointer => array_ptr.ty.elemType(),
else => return mod.fail(scope, inst.positionals.array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}),
};
if (!elem_ty.isIndexable()) {
return mod.fail(scope, inst.base.src, "array access of non-array type '{}'", .{elem_ty});
}
const array = try resolveInst(mod, scope, inst.positionals.array);
const array_ptr = try mod.analyzeRef(scope, inst.base.src, array);
const elem_index = try resolveInst(mod, scope, inst.positionals.index);
const result_ptr = try mod.elemPtr(scope, inst.base.src, array_ptr, elem_index);
return mod.analyzeDeref(scope, inst.base.src, result_ptr, result_ptr.src);
}
if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) {
// we have to deref the ptr operand to get the actual array pointer
const array_ptr_deref = try mod.analyzeDeref(scope, inst.base.src, array_ptr, inst.positionals.array_ptr.src);
if (array_ptr_deref.value()) |array_ptr_val| {
if (elem_index.value()) |index_val| {
// Both array pointer and index are compile-time known.
const index_u64 = index_val.toUnsignedInt();
// @intCast here because it would have been impossible to construct a value that
// required a larger index.
const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64));
const pointee_type = elem_ty.elemType().elemType();
fn elemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.Elem) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
return mod.constInst(scope, inst.base.src, .{
.ty = try Type.Tag.single_const_pointer.create(scope.arena(), pointee_type),
.val = elem_ptr,
});
}
}
}
return mod.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
const array_ptr = try resolveInst(mod, scope, inst.positionals.array);
const elem_index = try resolveInst(mod, scope, inst.positionals.index);
return mod.elemPtr(scope, inst.base.src, array_ptr, elem_index);
}
fn analyzeInstSlice(mod: *Module, scope: *Scope, inst: *zir.Inst.Slice) InnerError!*Inst {

View File

@ -80,7 +80,7 @@ pub fn addCases(ctx: *TestContext) !void {
}
{
var case = ctx.exe("hello world", linux_aarch64);
var case = ctx.exe("linux_aarch64 hello world", linux_aarch64);
// Regular old hello world
case.addCompareOutput(
\\export fn _start() noreturn {

View File

@ -8,7 +8,7 @@ const linux_arm = std.zig.CrossTarget{
pub fn addCases(ctx: *TestContext) !void {
{
var case = ctx.exe("hello world", linux_arm);
var case = ctx.exe("linux_arm hello world", linux_arm);
// Regular old hello world
case.addCompareOutput(
\\export fn _start() noreturn {

View File

@ -29,7 +29,7 @@ pub fn addCases(ctx: *TestContext) !void {
}
{
var case = ctx.exeUsingLlvmBackend("hello world", linux_x64);
var case = ctx.exeUsingLlvmBackend("llvm hello world", linux_x64);
case.addCompareOutput(
\\extern fn puts(s: [*:0]const u8) c_int;

View File

@ -231,7 +231,7 @@ pub fn addCases(ctx: *TestContext) !void {
}
{
var case = ctx.exe("hello world", linux_riscv64);
var case = ctx.exe("riscv64 hello world", linux_riscv64);
// Regular old hello world
case.addCompareOutput(
\\export fn _start() noreturn {