diff options
| -rw-r--r-- | src/Module.zig | 163 | ||||
| -rw-r--r-- | src/astgen.zig | 115 | ||||
| -rw-r--r-- | src/zir.zig | 60 | ||||
| -rw-r--r-- | src/zir_sema.zig | 270 | ||||
| -rw-r--r-- | test/stage2/aarch64.zig | 2 | ||||
| -rw-r--r-- | test/stage2/arm.zig | 2 | ||||
| -rw-r--r-- | test/stage2/llvm.zig | 2 | ||||
| -rw-r--r-- | test/stage2/test.zig | 2 |
8 files changed, 394 insertions, 222 deletions
diff --git a/src/Module.zig b/src/Module.zig index 747e60f970..7a6dc12967 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -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", .{}); +} diff --git a/src/astgen.zig b/src/astgen.zig index f24d078b4b..8b4f1cc93c 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -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 { +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.token].start; - - const ident_name = try mod.identifierTokenString(scope, node.token); - - return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{}); + 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, + })); } -fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { +fn namedField( + mod: *Module, + scope: *Scope, + rl: ResultLoc, + call: *ast.Node.BuiltinCall, +) InnerError!*zir.Inst { + try ensureBuiltinParamCount(mod, scope, call, 2); + const tree = scope.tree(); - const src = tree.token_locs[node.op_token].start; + const src = tree.token_locs[call.builtin_token].start; + const params = call.params(); - const lhs = try expr(mod, scope, .ref, node.lhs); - const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?); + 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 }; - // 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 }, .{})); + 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, diff --git a/src/zir.zig b/src/zir.zig index be45538288..97c8c94e78 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -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 {}, diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 82772cac16..b5b256afd3 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -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); + const operand = try resolveInst(mod, scope, inst.positionals.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 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, 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 = 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); +} - 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 fieldPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.Field) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); + + 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); +} + +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}); - } - - 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(); + 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); +} - return mod.constInst(scope, inst.base.src, .{ - .ty = try Type.Tag.single_const_pointer.create(scope.arena(), pointee_type), - .val = elem_ptr, - }); - } - } - } +fn elemPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.Elem) InnerError!*Inst { + const tracy = trace(@src()); + defer tracy.end(); - 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 { diff --git a/test/stage2/aarch64.zig b/test/stage2/aarch64.zig index 6c283f8e9f..3eaf2f51f9 100644 --- a/test/stage2/aarch64.zig +++ b/test/stage2/aarch64.zig @@ -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 { diff --git a/test/stage2/arm.zig b/test/stage2/arm.zig index dcab852784..06e359ccff 100644 --- a/test/stage2/arm.zig +++ b/test/stage2/arm.zig @@ -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 { diff --git a/test/stage2/llvm.zig b/test/stage2/llvm.zig index 69622714a7..f52ccecb68 100644 --- a/test/stage2/llvm.zig +++ b/test/stage2/llvm.zig @@ -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; diff --git a/test/stage2/test.zig b/test/stage2/test.zig index 845b9b627d..a384e4a8d4 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -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 { |
