Commit bae35bdf2d
Changed files (7)
test
behavior
src/AstGen.zig
@@ -1318,13 +1318,13 @@ fn arrayInitExpr(
return arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
}
},
- .ty, .coerced_ty => |ty_inst| {
+ .ty, .coerced_ty => {
if (types.array != .none) {
const result = try arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, types.elem, types.sentinel, false);
return rvalue(gz, rl, result, node);
} else {
- const elem_type = try gz.addUnNode(.elem_type, ty_inst, node);
- return arrayInitExprRlTy(gz, scope, node, array_init.ast.elements, elem_type, types.sentinel, false);
+ const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
+ return rvalue(gz, rl, result, node);
}
},
.ptr => |ptr_inst| {
@@ -1559,7 +1559,7 @@ fn structInitExpr(
_ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init_ref);
} else {
- return structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon_ref);
+ return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon_ref);
}
},
.none => {
@@ -1568,12 +1568,13 @@ fn structInitExpr(
_ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init);
} else {
- return structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon);
+ return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
}
},
.ty, .coerced_ty => |ty_inst| {
if (struct_init.ast.type_expr == 0) {
- return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init);
+ const result = try structInitExprRlNone(gz, scope, node, struct_init, ty_inst, .struct_init_anon);
+ return rvalue(gz, rl, result, node);
}
const inner_ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
_ = try gz.addUnNode(.validate_struct_init_ty, inner_ty_inst, node);
@@ -1586,7 +1587,7 @@ fn structInitExpr(
// We treat this case differently so that we don't get a crash when
// analyzing field_base_ptr against an alloc_inferred_mut.
// See corresponding logic in arrayInitExpr.
- const result = try structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon);
+ const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
return rvalue(gz, rl, result, node);
} else {
return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst);
@@ -1596,7 +1597,7 @@ fn structInitExpr(
// This condition is here for the same reason as the above condition in `inferred_ptr`.
// See corresponding logic in arrayInitExpr.
if (struct_init.ast.type_expr == 0 and astgen.isInferred(block_gz.rl_ptr)) {
- const result = try structInitExprRlNone(gz, scope, node, struct_init, .struct_init_anon);
+ const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
return rvalue(gz, rl, result, node);
}
@@ -1610,6 +1611,7 @@ fn structInitExprRlNone(
scope: *Scope,
node: Ast.Node.Index,
struct_init: Ast.full.StructInit,
+ ty_inst: Zir.Inst.Ref,
tag: Zir.Inst.Tag,
) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
@@ -1624,9 +1626,16 @@ fn structInitExprRlNone(
for (struct_init.ast.fields) |field_init| {
const name_token = tree.firstToken(field_init) - 2;
const str_index = try astgen.identAsString(name_token);
+ const sub_rl: ResultLoc = if (ty_inst != .none)
+ ResultLoc{ .ty = try gz.addPlNode(.field_type, field_init, Zir.Inst.FieldType{
+ .container_type = ty_inst,
+ .name_start = str_index,
+ }) }
+ else
+ .none;
setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{
.field_name = str_index,
- .init = try expr(gz, scope, .none, field_init),
+ .init = try expr(gz, scope, sub_rl, field_init),
});
extra_index += field_size;
}
@@ -2350,6 +2359,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
.closure_get,
.array_base_ptr,
.field_base_ptr,
+ .param_type,
=> break :b false,
// ZIR instructions that are always `noreturn`.
@@ -7846,10 +7856,15 @@ fn callExpr(
});
var extra_index = try reserveExtra(astgen, call.ast.params.len);
- for (call.ast.params) |param_node| {
- // Parameters are always temporary values, they have no
- // meaningful result location. Sema will coerce them.
- const arg_ref = try expr(gz, scope, .none, param_node);
+ for (call.ast.params) |param_node, i| {
+ const param_type = try gz.add(.{
+ .tag = .param_type,
+ .data = .{ .param_type = .{
+ .callee = callee,
+ .param_index = @intCast(u32, i),
+ } },
+ });
+ const arg_ref = try expr(gz, scope, .{ .coerced_ty = param_type }, param_node);
astgen.extra.items[extra_index] = @enumToInt(arg_ref);
extra_index += 1;
}
src/print_zir.zig
@@ -252,6 +252,7 @@ const Writer = struct {
=> try self.writeBoolBr(stream, inst),
.array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst),
+ .param_type => try self.writeParamType(stream, inst),
.ptr_type_simple => try self.writePtrTypeSimple(stream, inst),
.ptr_type => try self.writePtrType(stream, inst),
.int => try self.writeInt(stream, inst),
@@ -558,6 +559,16 @@ const Writer = struct {
try self.writeSrc(stream, inst_data.src());
}
+ fn writeParamType(
+ self: *Writer,
+ stream: anytype,
+ inst: Zir.Inst.Index,
+ ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
+ const inst_data = self.code.instructions.items(.data)[inst].param_type;
+ try self.writeInstRef(stream, inst_data.callee);
+ try stream.print(", {d})", .{inst_data.param_index});
+ }
+
fn writePtrTypeSimple(
self: *Writer,
stream: anytype,
src/Sema.zig
@@ -738,6 +738,7 @@ fn analyzeBodyInner(
.optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false),
.optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false),
.optional_type => try sema.zirOptionalType(block, inst),
+ .param_type => try sema.zirParamType(block, inst),
.ptr_type => try sema.zirPtrType(block, inst),
.ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),
.ref => try sema.zirRef(block, inst),
@@ -3638,6 +3639,39 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
return sema.storePtr(block, src, ptr, operand);
}
+fn zirParamType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+ const callee_src = sema.src;
+
+ const inst_data = sema.code.instructions.items(.data)[inst].param_type;
+ const callee = sema.resolveInst(inst_data.callee);
+ const callee_ty = sema.typeOf(callee);
+ var param_index = inst_data.param_index;
+
+ const fn_ty = if (callee_ty.tag() == .bound_fn) fn_ty: {
+ const bound_fn_val = try sema.resolveConstValue(block, callee_src, callee);
+ const bound_fn = bound_fn_val.castTag(.bound_fn).?.data;
+ const fn_ty = sema.typeOf(bound_fn.func_inst);
+ param_index += 1;
+ break :fn_ty fn_ty;
+ } else callee_ty;
+
+ const fn_info = if (fn_ty.zigTypeTag() == .Pointer)
+ fn_ty.childType().fnInfo()
+ else
+ fn_ty.fnInfo();
+
+ if (param_index >= fn_info.param_types.len) {
+ assert(fn_info.is_var_args);
+ return sema.addType(Type.initTag(.var_args_param));
+ }
+
+ if (fn_info.param_types[param_index].tag() == .generic_poison) {
+ return sema.addType(Type.initTag(.var_args_param));
+ }
+
+ return sema.addType(fn_info.param_types[param_index]);
+}
+
fn zirStr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
@@ -6613,6 +6647,7 @@ fn analyzeAs(
) CompileError!Air.Inst.Ref {
const dest_ty = try sema.resolveType(block, src, zir_dest_type);
const operand = sema.resolveInst(zir_operand);
+ if (dest_ty.tag() == .var_args_param) return operand;
return sema.coerce(block, dest_ty, operand, src);
}
@@ -12140,7 +12175,7 @@ fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
fn unionInit(
sema: *Sema,
block: *Block,
- init: Air.Inst.Ref,
+ uncasted_init: Air.Inst.Ref,
init_src: LazySrcLoc,
union_ty: Type,
union_ty_src: LazySrcLoc,
@@ -12148,6 +12183,8 @@ fn unionInit(
field_src: LazySrcLoc,
) CompileError!Air.Inst.Ref {
const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
+ const field = union_ty.unionFields().values()[field_index];
+ const init = try sema.coerce(block, field.ty, uncasted_init, init_src);
if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| {
const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
@@ -12620,6 +12657,7 @@ fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
const ty_src = inst_data.src();
const field_src = inst_data.src();
const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
+ if (aggregate_ty.tag() == .var_args_param) return sema.addType(aggregate_ty);
const field_name = sema.code.nullTerminatedString(extra.name_start);
return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
}
@@ -18964,7 +19002,7 @@ fn beginComptimePtrLoad(
if (coerce_in_mem_ok) {
deref.pointee = TypedValue{
.ty = field_ty,
- .val = try tv.val.fieldValue(sema.arena, field_index),
+ .val = tv.val.fieldValue(tv.ty, field_index),
};
break :blk deref;
}
src/type.zig
@@ -3724,6 +3724,8 @@ pub const Type = extern union {
.single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
.pointer => ty.castTag(.pointer).?.data.pointee_type,
+ .var_args_param => ty,
+
else => unreachable,
};
}
src/value.zig
@@ -2659,8 +2659,7 @@ pub const Value = extern union {
};
}
- pub fn fieldValue(val: Value, allocator: Allocator, index: usize) error{OutOfMemory}!Value {
- _ = allocator;
+ pub fn fieldValue(val: Value, ty: Type, index: usize) Value {
switch (val.tag()) {
.aggregate => {
const field_values = val.castTag(.aggregate).?.data;
@@ -2671,8 +2670,16 @@ pub const Value = extern union {
// TODO assert the tag is correct
return payload.val;
},
- // Structs which have only one possible value need to consist of members which have only one possible value.
- .the_only_possible_value => return val,
+
+ .the_only_possible_value => return ty.onePossibleValue().?,
+
+ .empty_struct_value => {
+ if (ty.isTupleOrAnonStruct()) {
+ const tuple = ty.tupleFields();
+ return tuple.values[index];
+ }
+ unreachable;
+ },
else => unreachable,
}
src/Zir.zig
@@ -464,6 +464,14 @@ pub const Inst = struct {
/// Merge two error sets into one, `E1 || E2`.
/// Uses the `pl_node` field with payload `Bin`.
merge_error_sets,
+ /// Given a reference to a function and a parameter index, returns the
+ /// type of the parameter. The only usage of this instruction is for the
+ /// result location of parameters of function calls. In the case of a function's
+ /// parameter type being `anytype`, it is the type coercion's job to detect this
+ /// scenario and skip the coercion, so that semantic analysis of this instruction
+ /// is not in a position where it must create an invalid type.
+ /// Uses the `param_type` union field.
+ param_type,
/// Turns an R-Value into a const L-Value. In other words, it takes a value,
/// stores it in a memory location, and returns a const pointer to it. If the value
/// is `comptime`, the memory location is global static constant data. Otherwise,
@@ -1077,6 +1085,7 @@ pub const Inst = struct {
.mul,
.mulwrap,
.mul_sat,
+ .param_type,
.ref,
.shl,
.shl_sat,
@@ -1266,6 +1275,7 @@ pub const Inst = struct {
.mulwrap = .pl_node,
.mul_sat = .pl_node,
+ .param_type = .param_type,
.param = .pl_tok,
.param_comptime = .pl_tok,
.param_anytype = .str_tok,
@@ -2213,6 +2223,10 @@ pub const Inst = struct {
/// Points to a `Block`.
payload_index: u32,
},
+ param_type: struct {
+ callee: Ref,
+ param_index: u32,
+ },
@"unreachable": struct {
/// Offset from Decl AST node index.
/// `Tag` determines which kind of AST node this points to.
@@ -2288,6 +2302,7 @@ pub const Inst = struct {
ptr_type,
int_type,
bool_br,
+ param_type,
@"unreachable",
@"break",
switch_capture,
test/behavior/call.zig
@@ -98,3 +98,23 @@ test "comptime call with bound function as parameter" {
var inst: S = undefined;
try expectEqual(?i32, S.ReturnType(inst.call_me_maybe));
}
+
+test "result location of function call argument through runtime condition and struct init" {
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+ const E = enum { a, b };
+ const S = struct {
+ e: E,
+ };
+ const namespace = struct {
+ fn foo(s: S) !void {
+ try expect(s.e == .b);
+ }
+ };
+ var runtime = true;
+ try namespace.foo(.{
+ .e = if (!runtime) .a else .b,
+ });
+}