authorgravatar for spexguy070@gmail.comMartin Wickham <spexguy070@gmail.com> 2021-09-28 12:00:35-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-09-28 12:00:35-05:00
log1cc5d4e758a95be373756e7c32f9bb46d21633c9
tree9c142d3d009e1622f27a22c9228a6ff10b878721
parent60b6e74468570a124f602a62b6bd2da95ba8c17c
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Stage 2: Support inst.func() syntax (#9827)

* Merge call zir instructions to make space for field_call * Fix bug with comptime known anytype args * Delete the param_type zir instruction * Move some passing tests to stage 2 * Implement a.b() function calls * Add field_call_bind support for call and field builtins

12 files changed, 629 insertions(+), 219 deletions(-)

src/AstGen.zig+112-49
...@@ -56,6 +56,7 @@ fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {...@@ -56,6 +56,7 @@ fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
56 u32 => @field(extra, field.name),56 u32 => @field(extra, field.name),
57 Zir.Inst.Ref => @enumToInt(@field(extra, field.name)),57 Zir.Inst.Ref => @enumToInt(@field(extra, field.name)),
58 i32 => @bitCast(u32, @field(extra, field.name)),58 i32 => @bitCast(u32, @field(extra, field.name)),
59 Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)),
59 else => @compileError("bad field type"),60 else => @compileError("bad field type"),
60 });61 });
61 }62 }
...@@ -1934,11 +1935,14 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -1934,11 +1935,14 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
1934 // in the above while loop.1935 // in the above while loop.
1935 const zir_tags = gz.astgen.instructions.items(.tag);1936 const zir_tags = gz.astgen.instructions.items(.tag);
1936 switch (zir_tags[inst]) {1937 switch (zir_tags[inst]) {
1937 // For some instructions, swap in a slightly different ZIR tag1938 // For some instructions, modify the zir data
1938 // so we can avoid a separate ensure_result_used instruction.1939 // so we can avoid a separate ensure_result_used instruction.
1939 .call_chkused => unreachable,
1940 .call => {1940 .call => {
1941 zir_tags[inst] = .call_chkused;1941 const extra_index = gz.astgen.instructions.items(.data)[inst].pl_node.payload_index;
1942 const slot = &gz.astgen.extra.items[extra_index];
1943 var flags = @bitCast(Zir.Inst.Call.Flags, slot.*);
1944 flags.ensure_result_used = true;
1945 slot.* = @bitCast(u32, flags);
1942 break :b true;1946 break :b true;
1943 },1947 },
19441948
...@@ -1976,9 +1980,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -1976,9 +1980,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
1976 .bool_br_and,1980 .bool_br_and,
1977 .bool_br_or,1981 .bool_br_or,
1978 .bool_not,1982 .bool_not,
1979 .call_compile_time,
1980 .call_nosuspend,
1981 .call_async,
1982 .cmp_lt,1983 .cmp_lt,
1983 .cmp_lte,1984 .cmp_lte,
1984 .cmp_eq,1985 .cmp_eq,
...@@ -1996,8 +1997,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -1996,8 +1997,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
1996 .elem_val_node,1997 .elem_val_node,
1997 .field_ptr,1998 .field_ptr,
1998 .field_val,1999 .field_val,
2000 .field_call_bind,
1999 .field_ptr_named,2001 .field_ptr_named,
2000 .field_val_named,2002 .field_val_named,
2003 .field_call_bind_named,
2001 .func,2004 .func,
2002 .func_inferred,2005 .func_inferred,
2003 .int,2006 .int,
...@@ -2012,7 +2015,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2012,7 +2015,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2012 .mod_rem,2015 .mod_rem,
2013 .mul,2016 .mul,
2014 .mulwrap,2017 .mulwrap,
2015 .param_type,
2016 .ref,2018 .ref,
2017 .shl,2019 .shl,
2018 .shr,2020 .shr,
...@@ -4968,6 +4970,21 @@ fn fieldAccess(...@@ -4968,6 +4970,21 @@ fn fieldAccess(
4968 scope: *Scope,4970 scope: *Scope,
4969 rl: ResultLoc,4971 rl: ResultLoc,
4970 node: Ast.Node.Index,4972 node: Ast.Node.Index,
4973) InnerError!Zir.Inst.Ref {
4974 if (rl == .ref) {
4975 return addFieldAccess(.field_ptr, gz, scope, .ref, node);
4976 } else {
4977 const access = try addFieldAccess(.field_val, gz, scope, .none_or_ref, node);
4978 return rvalue(gz, rl, access, node);
4979 }
4980}
4981
4982fn addFieldAccess(
4983 tag: Zir.Inst.Tag,
4984 gz: *GenZir,
4985 scope: *Scope,
4986 lhs_rl: ResultLoc,
4987 node: Ast.Node.Index,
4971) InnerError!Zir.Inst.Ref {4988) InnerError!Zir.Inst.Ref {
4972 const astgen = gz.astgen;4989 const astgen = gz.astgen;
4973 const tree = astgen.tree;4990 const tree = astgen.tree;
...@@ -4978,16 +4995,11 @@ fn fieldAccess(...@@ -4978,16 +4995,11 @@ fn fieldAccess(
4978 const dot_token = main_tokens[node];4995 const dot_token = main_tokens[node];
4979 const field_ident = dot_token + 1;4996 const field_ident = dot_token + 1;
4980 const str_index = try astgen.identAsString(field_ident);4997 const str_index = try astgen.identAsString(field_ident);
4981 switch (rl) {4998
4982 .ref => return gz.addPlNode(.field_ptr, node, Zir.Inst.Field{4999 return gz.addPlNode(tag, node, Zir.Inst.Field{
4983 .lhs = try expr(gz, scope, .ref, object_node),5000 .lhs = try expr(gz, scope, lhs_rl, object_node),
4984 .field_name_start = str_index,5001 .field_name_start = str_index,
4985 }),5002 });
4986 else => return rvalue(gz, rl, try gz.addPlNode(.field_val, node, Zir.Inst.Field{
4987 .lhs = try expr(gz, scope, .none_or_ref, object_node),
4988 .field_name_start = str_index,
4989 }), node),
4990 }
4991}5003}
49925004
4993fn arrayAccess(5005fn arrayAccess(
...@@ -7169,16 +7181,15 @@ fn builtinCall(...@@ -7169,16 +7181,15 @@ fn builtinCall(
7169 return rvalue(gz, rl, result, node);7181 return rvalue(gz, rl, result, node);
7170 },7182 },
7171 .field => {7183 .field => {
7172 const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
7173 if (rl == .ref) {7184 if (rl == .ref) {
7174 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{7185 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
7175 .lhs = try expr(gz, scope, .ref, params[0]),7186 .lhs = try expr(gz, scope, .ref, params[0]),
7176 .field_name = field_name,7187 .field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]),
7177 });7188 });
7178 }7189 }
7179 const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{7190 const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{
7180 .lhs = try expr(gz, scope, .none, params[0]),7191 .lhs = try expr(gz, scope, .none, params[0]),
7181 .field_name = field_name,7192 .field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]),
7182 });7193 });
7183 return rvalue(gz, rl, result, node);7194 return rvalue(gz, rl, result, node);
7184 },7195 },
...@@ -7554,7 +7565,7 @@ fn builtinCall(...@@ -7554,7 +7565,7 @@ fn builtinCall(
7554 },7565 },
7555 .call => {7566 .call => {
7556 const options = try comptimeExpr(gz, scope, .{ .ty = .call_options_type }, params[0]);7567 const options = try comptimeExpr(gz, scope, .{ .ty = .call_options_type }, params[0]);
7557 const callee = try expr(gz, scope, .none, params[1]);7568 const callee = try calleeExpr(gz, scope, params[1]);
7558 const args = try expr(gz, scope, .none, params[2]);7569 const args = try expr(gz, scope, .none, params[2]);
7559 const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{7570 const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{
7560 .options = options,7571 .options = options,
...@@ -7897,20 +7908,16 @@ fn callExpr(...@@ -7897,20 +7908,16 @@ fn callExpr(
7897 call: Ast.full.Call,7908 call: Ast.full.Call,
7898) InnerError!Zir.Inst.Ref {7909) InnerError!Zir.Inst.Ref {
7899 const astgen = gz.astgen;7910 const astgen = gz.astgen;
7900 const lhs = try expr(gz, scope, .none, call.ast.fn_expr);7911
7912 const callee = try calleeExpr(gz, scope, call.ast.fn_expr);
79017913
7902 const args = try astgen.gpa.alloc(Zir.Inst.Ref, call.ast.params.len);7914 const args = try astgen.gpa.alloc(Zir.Inst.Ref, call.ast.params.len);
7903 defer astgen.gpa.free(args);7915 defer astgen.gpa.free(args);
79047916
7905 for (call.ast.params) |param_node, i| {7917 for (call.ast.params) |param_node, i| {
7906 const param_type = try gz.add(.{7918 // Parameters are always temporary values, they have no
7907 .tag = .param_type,7919 // meaningful result location. Sema will coerce them.
7908 .data = .{ .param_type = .{7920 args[i] = try expr(gz, scope, .none, param_node);
7909 .callee = lhs,
7910 .param_index = @intCast(u32, i),
7911 } },
7912 });
7913 args[i] = try expr(gz, scope, .{ .coerced_ty = param_type }, param_node);
7914 }7921 }
79157922
7916 const modifier: std.builtin.CallOptions.Modifier = blk: {7923 const modifier: std.builtin.CallOptions.Modifier = blk: {
...@@ -7925,20 +7932,72 @@ fn callExpr(...@@ -7925,20 +7932,72 @@ fn callExpr(
7925 }7932 }
7926 break :blk .auto;7933 break :blk .auto;
7927 };7934 };
7928 const result: Zir.Inst.Ref = res: {7935 const call_inst = try gz.addCall(modifier, callee, args, node);
7929 const tag: Zir.Inst.Tag = switch (modifier) {7936 return rvalue(gz, rl, call_inst, node); // TODO function call with result location
7930 .auto => .call,7937}
7931 .async_kw => .call_async,7938
7932 .never_tail => unreachable,7939/// calleeExpr generates the function part of a call expression (f in f(x)), or the
7933 .never_inline => unreachable,7940/// callee argument to the @call() builtin. If the lhs is a field access or the
7934 .no_async => .call_nosuspend,7941/// @field() builtin, we need to generate a special field_call_bind instruction
7935 .always_tail => unreachable,7942/// instead of the normal field_val or field_ptr. If this is a inst.func() call,
7936 .always_inline => unreachable,7943/// this instruction will capture the value of the first argument before evaluating
7937 .compile_time => .call_compile_time,7944/// the other arguments. We need to use .ref here to guarantee we will be able to
7938 };7945/// promote an lvalue to an address if the first parameter requires it. This
7939 break :res try gz.addCall(tag, lhs, args, node);7946/// unfortunately also means we need to take a reference to any types on the lhs.
7940 };7947fn calleeExpr(
7941 return rvalue(gz, rl, result, node); // TODO function call with result location7948 gz: *GenZir,
7949 scope: *Scope,
7950 node: Ast.Node.Index,
7951) InnerError!Zir.Inst.Ref {
7952 const astgen = gz.astgen;
7953 const tree = astgen.tree;
7954
7955 const tag = tree.nodes.items(.tag)[node];
7956 switch (tag) {
7957 .field_access => return addFieldAccess(.field_call_bind, gz, scope, .ref, node),
7958
7959 .builtin_call_two,
7960 .builtin_call_two_comma,
7961 .builtin_call,
7962 .builtin_call_comma,
7963 => {
7964 const node_datas = tree.nodes.items(.data);
7965 const main_tokens = tree.nodes.items(.main_token);
7966 const builtin_token = main_tokens[node];
7967 const builtin_name = tree.tokenSlice(builtin_token);
7968
7969 var inline_params: [2]Ast.Node.Index = undefined;
7970 var params: []Ast.Node.Index = switch (tag) {
7971 .builtin_call,
7972 .builtin_call_comma,
7973 => tree.extra_data[node_datas[node].lhs..node_datas[node].rhs],
7974
7975 .builtin_call_two,
7976 .builtin_call_two_comma,
7977 => blk: {
7978 inline_params = .{ node_datas[node].lhs, node_datas[node].rhs };
7979 const len: usize = if (inline_params[0] == 0) @as(usize, 0) else if (inline_params[1] == 0) @as(usize, 1) else @as(usize, 2);
7980 break :blk inline_params[0..len];
7981 },
7982
7983 else => unreachable,
7984 };
7985
7986 // If anything is wrong, fall back to builtinCall.
7987 // It will emit any necessary compile errors and notes.
7988 if (std.mem.eql(u8, builtin_name, "@field") and params.len == 2) {
7989 const lhs = try expr(gz, scope, .ref, params[0]);
7990 const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
7991 return gz.addPlNode(.field_call_bind_named, node, Zir.Inst.FieldNamed{
7992 .lhs = lhs,
7993 .field_name = field_name,
7994 });
7995 }
7996
7997 return builtinCall(gz, scope, .none, node, params);
7998 },
7999 else => return expr(gz, scope, .none, node),
8000 }
7942}8001}
79438002
7944pub const simple_types = std.ComptimeStringMap(Zir.Inst.Ref, .{8003pub const simple_types = std.ComptimeStringMap(Zir.Inst.Ref, .{
...@@ -9607,7 +9666,7 @@ const GenZir = struct {...@@ -9607,7 +9666,7 @@ const GenZir = struct {
96079666
9608 fn addCall(9667 fn addCall(
9609 gz: *GenZir,9668 gz: *GenZir,
9610 tag: Zir.Inst.Tag,9669 modifier: std.builtin.CallOptions.Modifier,
9611 callee: Zir.Inst.Ref,9670 callee: Zir.Inst.Ref,
9612 args: []const Zir.Inst.Ref,9671 args: []const Zir.Inst.Ref,
9613 /// Absolute node index. This function does the conversion to offset from Decl.9672 /// Absolute node index. This function does the conversion to offset from Decl.
...@@ -9616,20 +9675,24 @@ const GenZir = struct {...@@ -9616,20 +9675,24 @@ const GenZir = struct {
9616 assert(callee != .none);9675 assert(callee != .none);
9617 assert(src_node != 0);9676 assert(src_node != 0);
9618 const gpa = gz.astgen.gpa;9677 const gpa = gz.astgen.gpa;
9678 const Call = Zir.Inst.Call;
9619 try gz.instructions.ensureUnusedCapacity(gpa, 1);9679 try gz.instructions.ensureUnusedCapacity(gpa, 1);
9620 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);9680 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
9621 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Call).Struct.fields.len +9681 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Call).Struct.fields.len +
9622 args.len);9682 args.len);
96239683
9624 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Call{9684 const payload_index = gz.astgen.addExtraAssumeCapacity(Call{
9625 .callee = callee,9685 .callee = callee,
9626 .args_len = @intCast(u32, args.len),9686 .flags = .{
9687 .packed_modifier = @intCast(Call.Flags.PackedModifier, @enumToInt(modifier)),
9688 .args_len = @intCast(Call.Flags.PackedArgsLen, args.len),
9689 },
9627 });9690 });
9628 gz.astgen.appendRefsAssumeCapacity(args);9691 gz.astgen.appendRefsAssumeCapacity(args);
96299692
9630 const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);9693 const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
9631 gz.astgen.instructions.appendAssumeCapacity(.{9694 gz.astgen.instructions.appendAssumeCapacity(.{
9632 .tag = tag,9695 .tag = .call,
9633 .data = .{ .pl_node = .{9696 .data = .{ .pl_node = .{
9634 .src_node = gz.nodeIndexToRelative(src_node),9697 .src_node = gz.nodeIndexToRelative(src_node),
9635 .payload_index = payload_index,9698 .payload_index = payload_index,
src/Sema.zig+223-71
...@@ -185,11 +185,7 @@ pub fn analyzeBody(...@@ -185,11 +185,7 @@ pub fn analyzeBody(
185 .bool_br_and => try sema.zirBoolBr(block, inst, false),185 .bool_br_and => try sema.zirBoolBr(block, inst, false),
186 .bool_br_or => try sema.zirBoolBr(block, inst, true),186 .bool_br_or => try sema.zirBoolBr(block, inst, true),
187 .c_import => try sema.zirCImport(block, inst),187 .c_import => try sema.zirCImport(block, inst),
188 .call => try sema.zirCall(block, inst, .auto, false),188 .call => try sema.zirCall(block, inst),
189 .call_chkused => try sema.zirCall(block, inst, .auto, true),
190 .call_compile_time => try sema.zirCall(block, inst, .compile_time, false),
191 .call_nosuspend => try sema.zirCall(block, inst, .no_async, false),
192 .call_async => try sema.zirCall(block, inst, .async_kw, false),
193 .closure_get => try sema.zirClosureGet(block, inst),189 .closure_get => try sema.zirClosureGet(block, inst),
194 .cmp_lt => try sema.zirCmp(block, inst, .lt),190 .cmp_lt => try sema.zirCmp(block, inst, .lt),
195 .cmp_lte => try sema.zirCmp(block, inst, .lte),191 .cmp_lte => try sema.zirCmp(block, inst, .lte),
...@@ -223,6 +219,8 @@ pub fn analyzeBody(...@@ -223,6 +219,8 @@ pub fn analyzeBody(
223 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),219 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),
224 .field_val => try sema.zirFieldVal(block, inst),220 .field_val => try sema.zirFieldVal(block, inst),
225 .field_val_named => try sema.zirFieldValNamed(block, inst),221 .field_val_named => try sema.zirFieldValNamed(block, inst),
222 .field_call_bind => try sema.zirFieldCallBind(block, inst),
223 .field_call_bind_named => try sema.zirFieldCallBindNamed(block, inst),
226 .func => try sema.zirFunc(block, inst, false),224 .func => try sema.zirFunc(block, inst, false),
227 .func_inferred => try sema.zirFunc(block, inst, true),225 .func_inferred => try sema.zirFunc(block, inst, true),
228 .import => try sema.zirImport(block, inst),226 .import => try sema.zirImport(block, inst),
...@@ -244,7 +242,6 @@ pub fn analyzeBody(...@@ -244,7 +242,6 @@ pub fn analyzeBody(
244 .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false),242 .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false),
245 .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false),243 .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false),
246 .optional_type => try sema.zirOptionalType(block, inst),244 .optional_type => try sema.zirOptionalType(block, inst),
247 .param_type => try sema.zirParamType(block, inst),
248 .ptr_type => try sema.zirPtrType(block, inst),245 .ptr_type => try sema.zirPtrType(block, inst),
249 .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),246 .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),
250 .ref => try sema.zirRef(block, inst),247 .ref => try sema.zirRef(block, inst),
...@@ -2031,45 +2028,6 @@ fn zirStoreNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE...@@ -2031,45 +2028,6 @@ fn zirStoreNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
2031 return sema.storePtr(block, src, ptr, value);2028 return sema.storePtr(block, src, ptr, value);
2032}2029}
20332030
2034fn zirParamType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
2035 const tracy = trace(@src());
2036 defer tracy.end();
2037
2038 const src = sema.src;
2039 const fn_inst_src = sema.src;
2040
2041 const inst_data = sema.code.instructions.items(.data)[inst].param_type;
2042 const fn_inst = sema.resolveInst(inst_data.callee);
2043 const fn_inst_ty = sema.typeOf(fn_inst);
2044 const param_index = inst_data.param_index;
2045
2046 const fn_ty: Type = switch (fn_inst_ty.zigTypeTag()) {
2047 .Fn => fn_inst_ty,
2048 .BoundFn => {
2049 return sema.mod.fail(&block.base, fn_inst_src, "TODO implement zirParamType for method call syntax", .{});
2050 },
2051 else => {
2052 return sema.mod.fail(&block.base, fn_inst_src, "expected function, found '{}'", .{fn_inst_ty});
2053 },
2054 };
2055
2056 const param_count = fn_ty.fnParamLen();
2057 if (param_index >= param_count) {
2058 if (fn_ty.fnIsVarArgs()) {
2059 return sema.addType(Type.initTag(.var_args_param));
2060 }
2061 return sema.mod.fail(&block.base, src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{
2062 param_index,
2063 fn_ty,
2064 param_count,
2065 });
2066 }
2067
2068 // TODO support generic functions
2069 const param_type = fn_ty.fnParamType(param_index);
2070 return sema.addType(param_type);
2071}
2072
2073fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {2031fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
2074 const tracy = trace(@src());2032 const tracy = trace(@src());
2075 defer tracy.end();2033 defer tracy.end();
...@@ -2786,8 +2744,6 @@ fn zirCall(...@@ -2786,8 +2744,6 @@ fn zirCall(
2786 sema: *Sema,2744 sema: *Sema,
2787 block: *Scope.Block,2745 block: *Scope.Block,
2788 inst: Zir.Inst.Index,2746 inst: Zir.Inst.Index,
2789 modifier: std.builtin.CallOptions.Modifier,
2790 ensure_result_used: bool,
2791) CompileError!Air.Inst.Ref {2747) CompileError!Air.Inst.Ref {
2792 const tracy = trace(@src());2748 const tracy = trace(@src());
2793 defer tracy.end();2749 defer tracy.end();
...@@ -2796,14 +2752,31 @@ fn zirCall(...@@ -2796,14 +2752,31 @@ fn zirCall(
2796 const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };2752 const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };
2797 const call_src = inst_data.src();2753 const call_src = inst_data.src();
2798 const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index);2754 const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index);
2799 const args = sema.code.refSlice(extra.end, extra.data.args_len);2755 const args = sema.code.refSlice(extra.end, extra.data.flags.args_len);
28002756
2801 const func = sema.resolveInst(extra.data.callee);2757 const modifier = @intToEnum(std.builtin.CallOptions.Modifier, extra.data.flags.packed_modifier);
2802 // TODO handle function calls of generic functions2758 const ensure_result_used = extra.data.flags.ensure_result_used;
2803 const resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len);2759
2804 for (args) |zir_arg, i| {2760 var func = sema.resolveInst(extra.data.callee);
2805 // the args are already casted to the result of a param type instruction.2761 var resolved_args: []Air.Inst.Ref = undefined;
2806 resolved_args[i] = sema.resolveInst(zir_arg);2762
2763 const func_type = sema.typeOf(func);
2764
2765 // Desugar bound functions here
2766 if (func_type.tag() == .bound_fn) {
2767 const bound_func = try sema.resolveValue(block, func_src, func);
2768 const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data;
2769 func = bound_data.func_inst;
2770 resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len + 1);
2771 resolved_args[0] = bound_data.arg0_inst;
2772 for (args) |zir_arg, i| {
2773 resolved_args[i + 1] = sema.resolveInst(zir_arg);
2774 }
2775 } else {
2776 resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len);
2777 for (args) |zir_arg, i| {
2778 resolved_args[i] = sema.resolveInst(zir_arg);
2779 }
2807 }2780 }
28082781
2809 return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args);2782 return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args);
...@@ -3334,14 +3307,16 @@ fn analyzeCall(...@@ -3334,14 +3307,16 @@ fn analyzeCall(
3334 }3307 }
3335 const arg_src = call_src; // TODO: better source location3308 const arg_src = call_src; // TODO: better source location
3336 const arg = uncasted_args[arg_i];3309 const arg = uncasted_args[arg_i];
3337 if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {3310 if (is_comptime) {
3338 const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);3311 if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {
3339 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);3312 const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
3340 } else if (is_comptime) {3313 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
3341 return sema.failWithNeededComptime(block, arg_src);3314 } else {
3315 return sema.failWithNeededComptime(block, arg_src);
3316 }
3342 } else if (is_anytype) {3317 } else if (is_anytype) {
3343 // We insert into the map an instruction which is runtime-known3318 // We insert into the map an instruction which is runtime-known
3344 // but has the type of the comptime argument.3319 // but has the type of the argument.
3345 const child_arg = try child_block.addArg(sema.typeOf(arg), 0);3320 const child_arg = try child_block.addArg(sema.typeOf(arg), 0);
3346 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);3321 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
3347 }3322 }
...@@ -4558,6 +4533,19 @@ fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr...@@ -4558,6 +4533,19 @@ fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
4558 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);4533 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
4559}4534}
45604535
4536fn zirFieldCallBind(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
4537 const tracy = trace(@src());
4538 defer tracy.end();
4539
4540 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
4541 const src = inst_data.src();
4542 const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
4543 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
4544 const field_name = sema.code.nullTerminatedString(extra.field_name_start);
4545 const object_ptr = sema.resolveInst(extra.lhs);
4546 return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src);
4547}
4548
4561fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {4549fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
4562 const tracy = trace(@src());4550 const tracy = trace(@src());
4563 defer tracy.end();4551 defer tracy.end();
...@@ -4584,6 +4572,19 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp...@@ -4584,6 +4572,19 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp
4584 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);4572 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
4585}4573}
45864574
4575fn zirFieldCallBindNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
4576 const tracy = trace(@src());
4577 defer tracy.end();
4578
4579 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
4580 const src = inst_data.src();
4581 const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
4582 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
4583 const object_ptr = sema.resolveInst(extra.lhs);
4584 const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
4585 return sema.fieldCallBind(block, src, object_ptr, field_name, field_name_src);
4586}
4587
4587fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {4588fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
4588 const tracy = trace(@src());4589 const tracy = trace(@src());
4589 defer tracy.end();4590 defer tracy.end();
...@@ -9484,6 +9485,148 @@ fn fieldPtr(...@@ -9484,6 +9485,148 @@ fn fieldPtr(
9484 return mod.fail(&block.base, src, "type '{}' does not support field access", .{object_ty});9485 return mod.fail(&block.base, src, "type '{}' does not support field access", .{object_ty});
9485}9486}
94869487
9488fn fieldCallBind(
9489 sema: *Sema,
9490 block: *Scope.Block,
9491 src: LazySrcLoc,
9492 raw_ptr: Air.Inst.Ref,
9493 field_name: []const u8,
9494 field_name_src: LazySrcLoc,
9495) CompileError!Air.Inst.Ref {
9496 // When editing this function, note that there is corresponding logic to be edited
9497 // in `fieldVal`. This function takes a pointer and returns a pointer.
9498
9499 const mod = sema.mod;
9500 const raw_ptr_src = src; // TODO better source location
9501 const raw_ptr_ty = sema.typeOf(raw_ptr);
9502 const inner_ty = if (raw_ptr_ty.zigTypeTag() == .Pointer and raw_ptr_ty.ptrSize() == .One)
9503 raw_ptr_ty.childType()
9504 else
9505 return mod.fail(&block.base, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty});
9506
9507 // Optionally dereference a second pointer to get the concrete type.
9508 const is_double_ptr = inner_ty.zigTypeTag() == .Pointer and inner_ty.ptrSize() == .One;
9509 const concrete_ty = if (is_double_ptr) inner_ty.childType() else inner_ty;
9510 const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty;
9511 const object_ptr = if (is_double_ptr)
9512 try sema.analyzeLoad(block, src, raw_ptr, src)
9513 else
9514 raw_ptr;
9515
9516 const arena = sema.arena;
9517 find_field: {
9518 switch (concrete_ty.zigTypeTag()) {
9519 .Struct => {
9520 const struct_ty = try sema.resolveTypeFields(block, src, concrete_ty);
9521 const struct_obj = struct_ty.castTag(.@"struct").?.data;
9522
9523 const field_index = struct_obj.fields.getIndex(field_name) orelse
9524 break :find_field;
9525 const field = struct_obj.fields.values()[field_index];
9526
9527 const ptr_field_ty = try Type.ptr(arena, .{
9528 .pointee_type = field.ty,
9529 .mutable = ptr_ty.ptrIsMutable(),
9530 .@"addrspace" = ptr_ty.ptrAddressSpace(),
9531 });
9532
9533 if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| {
9534 const pointer = try sema.addConstant(
9535 ptr_field_ty,
9536 try Value.Tag.field_ptr.create(arena, .{
9537 .container_ptr = struct_ptr_val,
9538 .field_index = field_index,
9539 }),
9540 );
9541 return sema.analyzeLoad(block, src, pointer, src);
9542 }
9543
9544 try sema.requireRuntimeBlock(block, src);
9545 const ptr_inst = ptr_inst: {
9546 const tag: Air.Inst.Tag = switch (field_index) {
9547 0 => .struct_field_ptr_index_0,
9548 1 => .struct_field_ptr_index_1,
9549 2 => .struct_field_ptr_index_2,
9550 3 => .struct_field_ptr_index_3,
9551 else => {
9552 break :ptr_inst try block.addInst(.{
9553 .tag = .struct_field_ptr,
9554 .data = .{ .ty_pl = .{
9555 .ty = try sema.addType(ptr_field_ty),
9556 .payload = try sema.addExtra(Air.StructField{
9557 .struct_operand = object_ptr,
9558 .field_index = @intCast(u32, field_index),
9559 }),
9560 } },
9561 });
9562 },
9563 };
9564 break :ptr_inst try block.addInst(.{
9565 .tag = tag,
9566 .data = .{ .ty_op = .{
9567 .ty = try sema.addType(ptr_field_ty),
9568 .operand = object_ptr,
9569 } },
9570 });
9571 };
9572 return sema.analyzeLoad(block, src, ptr_inst, src);
9573 },
9574 .Union => return sema.mod.fail(&block.base, src, "TODO implement field calls on unions", .{}),
9575 .Type => {
9576 const namespace = try sema.analyzeLoad(block, src, object_ptr, src);
9577 return sema.fieldVal(block, src, namespace, field_name, field_name_src);
9578 },
9579 else => {},
9580 }
9581 }
9582
9583 // If we get here, we need to look for a decl in the struct type instead.
9584 switch (concrete_ty.zigTypeTag()) {
9585 .Struct, .Opaque, .Union, .Enum => {
9586 if (concrete_ty.getNamespace()) |namespace| {
9587 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
9588 const decl_val = try sema.analyzeLoad(block, src, inst, src);
9589 const decl_type = sema.typeOf(decl_val);
9590 if (decl_type.zigTypeTag() == .Fn and
9591 decl_type.fnParamLen() >= 1)
9592 {
9593 const first_param_type = decl_type.fnParamType(0);
9594 const first_param_tag = first_param_type.tag();
9595 // zig fmt: off
9596 if (first_param_tag == .var_args_param or
9597 first_param_tag == .generic_poison or (
9598 first_param_type.zigTypeTag() == .Pointer and
9599 first_param_type.ptrSize() == .One and
9600 first_param_type.childType().eql(concrete_ty)))
9601 {
9602 // zig fmt: on
9603 // TODO: bound fn calls on rvalues should probably
9604 // generate a by-value argument somehow.
9605 const ty = Type.Tag.bound_fn.init();
9606 const value = try Value.Tag.bound_fn.create(arena, .{
9607 .func_inst = decl_val,
9608 .arg0_inst = object_ptr,
9609 });
9610 return sema.addConstant(ty, value);
9611 } else if (first_param_type.eql(concrete_ty)) {
9612 var deref = try sema.analyzeLoad(block, src, object_ptr, src);
9613 const ty = Type.Tag.bound_fn.init();
9614 const value = try Value.Tag.bound_fn.create(arena, .{
9615 .func_inst = decl_val,
9616 .arg0_inst = deref,
9617 });
9618 return sema.addConstant(ty, value);
9619 }
9620 }
9621 }
9622 }
9623 },
9624 else => {},
9625 }
9626
9627 return mod.fail(&block.base, src, "type '{}' has no field or member function named '{s}'", .{ concrete_ty, field_name });
9628}
9629
9487fn namespaceLookup(9630fn namespaceLookup(
9488 sema: *Sema,9631 sema: *Sema,
9489 block: *Scope.Block,9632 block: *Scope.Block,
...@@ -9850,14 +9993,14 @@ fn coerce(...@@ -9850,14 +9993,14 @@ fn coerce(
9850 if (dest_type.eql(inst_ty))9993 if (dest_type.eql(inst_ty))
9851 return inst;9994 return inst;
98529995
9853 const in_memory_result = coerceInMemoryAllowed(dest_type, inst_ty, false);9996 const mod = sema.mod;
9997 const arena = sema.arena;
9998
9999 const in_memory_result = coerceInMemoryAllowed(dest_type, inst_ty, false, mod.getTarget());
9854 if (in_memory_result == .ok) {10000 if (in_memory_result == .ok) {
9855 return sema.bitcast(block, dest_type, inst, inst_src);10001 return sema.bitcast(block, dest_type, inst, inst_src);
9856 }10002 }
985710003
9858 const mod = sema.mod;
9859 const arena = sema.arena;
9860
9861 // undefined to anything10004 // undefined to anything
9862 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {10005 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
9863 if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {10006 if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {
...@@ -9898,7 +10041,7 @@ fn coerce(...@@ -9898,7 +10041,7 @@ fn coerce(
9898 if (inst_ty.ptrAddressSpace() != dest_type.ptrAddressSpace()) break :src_array_ptr;10041 if (inst_ty.ptrAddressSpace() != dest_type.ptrAddressSpace()) break :src_array_ptr;
989910042
9900 const dst_elem_type = dest_type.elemType();10043 const dst_elem_type = dest_type.elemType();
9901 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut)) {10044 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, mod.getTarget())) {
9902 .ok => {},10045 .ok => {},
9903 .no_match => break :src_array_ptr,10046 .no_match => break :src_array_ptr,
9904 }10047 }
...@@ -10024,7 +10167,7 @@ const InMemoryCoercionResult = enum {...@@ -10024,7 +10167,7 @@ const InMemoryCoercionResult = enum {
10024/// * sentinel-terminated pointers can coerce into `[*]`10167/// * sentinel-terminated pointers can coerce into `[*]`
10025/// TODO improve this function to report recursive compile errors like it does in stage1.10168/// TODO improve this function to report recursive compile errors like it does in stage1.
10026/// look at the function types_match_const_cast_only10169/// look at the function types_match_const_cast_only
10027fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool) InMemoryCoercionResult {10170fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult {
10028 if (dest_type.eql(src_type))10171 if (dest_type.eql(src_type))
10029 return .ok;10172 return .ok;
1003010173
...@@ -10034,7 +10177,7 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool) InM...@@ -10034,7 +10177,7 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool) InM
10034 const dest_info = dest_type.ptrInfo().data;10177 const dest_info = dest_type.ptrInfo().data;
10035 const src_info = src_type.ptrInfo().data;10178 const src_info = src_type.ptrInfo().data;
1003610179
10037 const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable);10180 const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target);
10038 if (child == .no_match) {10181 if (child == .no_match) {
10039 return child;10182 return child;
10040 }10183 }
...@@ -10081,11 +10224,19 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool) InM...@@ -10081,11 +10224,19 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool) InM
10081 return .no_match;10224 return .no_match;
10082 }10225 }
1008310226
10084 assert(src_info.@"align" != 0);10227 // If both pointers have alignment 0, it means they both want ABI alignment.
10085 assert(dest_info.@"align" != 0);10228 // In this case, if they share the same child type, no need to resolve
10229 // pointee type alignment. Otherwise both pointee types must have their alignment
10230 // resolved and we compare the alignment numerically.
10231 if (src_info.@"align" != 0 or dest_info.@"align" != 0 or
10232 !dest_info.pointee_type.eql(src_info.pointee_type))
10233 {
10234 const src_align = src_type.ptrAlignment(target);
10235 const dest_align = dest_type.ptrAlignment(target);
1008610236
10087 if (dest_info.@"align" > src_info.@"align") {10237 if (dest_align > src_align) {
10088 return .no_match;10238 return .no_match;
10239 }
10089 }10240 }
1009010241
10091 return .ok;10242 return .ok;
...@@ -11606,6 +11757,7 @@ fn typeHasOnePossibleValue(...@@ -11606,6 +11757,7 @@ fn typeHasOnePossibleValue(
11606 .single_const_pointer,11757 .single_const_pointer,
11607 .single_mut_pointer,11758 .single_mut_pointer,
11608 .pointer,11759 .pointer,
11760 .bound_fn,
11609 => return null,11761 => return null,
1161011762
11611 .@"struct" => {11763 .@"struct" => {
src/Zir.zig+44-33
...@@ -70,6 +70,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en...@@ -70,6 +70,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en
70 u32 => code.extra[i],70 u32 => code.extra[i],
71 Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),71 Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),
72 i32 => @bitCast(i32, code.extra[i]),72 i32 => @bitCast(i32, code.extra[i]),
73 Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]),
73 else => @compileError("bad field type"),74 else => @compileError("bad field type"),
74 };75 };
75 i += 1;76 i += 1;
...@@ -222,17 +223,9 @@ pub const Inst = struct {...@@ -222,17 +223,9 @@ pub const Inst = struct {
222 break_inline,223 break_inline,
223 /// Uses the `node` union field.224 /// Uses the `node` union field.
224 breakpoint,225 breakpoint,
225 /// Function call with modifier `.auto`.226 /// Function call.
226 /// Uses `pl_node`. AST node is the function call. Payload is `Call`.227 /// Uses `pl_node`. AST node is the function call. Payload is `Call`.
227 call,228 call,
228 /// Same as `call` but it also does `ensure_result_used` on the return value.
229 call_chkused,
230 /// Same as `call` but with modifier `.compile_time`.
231 call_compile_time,
232 /// Same as `call` but with modifier `.no_suspend`.
233 call_nosuspend,
234 /// Same as `call` but with modifier `.async_kw`.
235 call_async,
236 /// `<`229 /// `<`
237 /// Uses the `pl_node` union field. Payload is `Bin`.230 /// Uses the `pl_node` union field. Payload is `Bin`.
238 cmp_lt,231 cmp_lt,
...@@ -327,6 +320,15 @@ pub const Inst = struct {...@@ -327,6 +320,15 @@ pub const Inst = struct {
327 /// This instruction also accepts a pointer.320 /// This instruction also accepts a pointer.
328 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.321 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
329 field_val,322 field_val,
323 /// Given a pointer to a struct or object that contains virtual fields, returns the
324 /// named field. If there is no named field, searches in the type for a decl that
325 /// matches the field name. The decl is resolved and we ensure that it's a function
326 /// which can accept the object as the first parameter, with one pointer fixup. If
327 /// all of that works, this instruction produces a special "bound function" value
328 /// which contains both the function and the saved first parameter value.
329 /// Bound functions may only be used as the function parameter to a `call` or
330 /// `builtin_call` instruction. Any other use is invalid zir and may crash the compiler.
331 field_call_bind,
330 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer332 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
331 /// to the named field. The field name is a comptime instruction. Used by @field.333 /// to the named field. The field name is a comptime instruction. Used by @field.
332 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.334 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
...@@ -335,6 +337,15 @@ pub const Inst = struct {...@@ -335,6 +337,15 @@ pub const Inst = struct {
335 /// The field name is a comptime instruction. Used by @field.337 /// The field name is a comptime instruction. Used by @field.
336 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.338 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
337 field_val_named,339 field_val_named,
340 /// Given a pointer to a struct or object that contains virtual fields, returns the
341 /// named field. If there is no named field, searches in the type for a decl that
342 /// matches the field name. The decl is resolved and we ensure that it's a function
343 /// which can accept the object as the first parameter, with one pointer fixup. If
344 /// all of that works, this instruction produces a special "bound function" value
345 /// which contains both the function and the saved first parameter value.
346 /// Bound functions may only be used as the function parameter to a `call` or
347 /// `builtin_call` instruction. Any other use is invalid zir and may crash the compiler.
348 field_call_bind_named,
338 /// Returns a function type, or a function instance, depending on whether349 /// Returns a function type, or a function instance, depending on whether
339 /// the body_len is 0. Calling convention is auto.350 /// the body_len is 0. Calling convention is auto.
340 /// Uses the `pl_node` union field. `payload_index` points to a `Func`.351 /// Uses the `pl_node` union field. `payload_index` points to a `Func`.
...@@ -395,14 +406,6 @@ pub const Inst = struct {...@@ -395,14 +406,6 @@ pub const Inst = struct {
395 /// Twos complement wrapping integer multiplication.406 /// Twos complement wrapping integer multiplication.
396 /// Uses the `pl_node` union field. Payload is `Bin`.407 /// Uses the `pl_node` union field. Payload is `Bin`.
397 mulwrap,408 mulwrap,
398 /// Given a reference to a function and a parameter index, returns the
399 /// type of the parameter. The only usage of this instruction is for the
400 /// result location of parameters of function calls. In the case of a function's
401 /// parameter type being `anytype`, it is the type coercion's job to detect this
402 /// scenario and skip the coercion, so that semantic analysis of this instruction
403 /// is not in a position where it must create an invalid type.
404 /// Uses the `param_type` union field.
405 param_type,
406 /// Turns an R-Value into a const L-Value. In other words, it takes a value,409 /// Turns an R-Value into a const L-Value. In other words, it takes a value,
407 /// stores it in a memory location, and returns a const pointer to it. If the value410 /// stores it in a memory location, and returns a const pointer to it. If the value
408 /// is `comptime`, the memory location is global static constant data. Otherwise,411 /// is `comptime`, the memory location is global static constant data. Otherwise,
...@@ -988,10 +991,6 @@ pub const Inst = struct {...@@ -988,10 +991,6 @@ pub const Inst = struct {
988 .breakpoint,991 .breakpoint,
989 .fence,992 .fence,
990 .call,993 .call,
991 .call_chkused,
992 .call_compile_time,
993 .call_nosuspend,
994 .call_async,
995 .cmp_lt,994 .cmp_lt,
996 .cmp_lte,995 .cmp_lte,
997 .cmp_eq,996 .cmp_eq,
...@@ -1017,8 +1016,10 @@ pub const Inst = struct {...@@ -1017,8 +1016,10 @@ pub const Inst = struct {
1017 .export_value,1016 .export_value,
1018 .field_ptr,1017 .field_ptr,
1019 .field_val,1018 .field_val,
1019 .field_call_bind,
1020 .field_ptr_named,1020 .field_ptr_named,
1021 .field_val_named,1021 .field_val_named,
1022 .field_call_bind_named,
1022 .func,1023 .func,
1023 .func_inferred,1024 .func_inferred,
1024 .has_decl,1025 .has_decl,
...@@ -1034,7 +1035,6 @@ pub const Inst = struct {...@@ -1034,7 +1035,6 @@ pub const Inst = struct {
1034 .mod_rem,1035 .mod_rem,
1035 .mul,1036 .mul,
1036 .mulwrap,1037 .mulwrap,
1037 .param_type,
1038 .ref,1038 .ref,
1039 .shl,1039 .shl,
1040 .shr,1040 .shr,
...@@ -1247,10 +1247,6 @@ pub const Inst = struct {...@@ -1247,10 +1247,6 @@ pub const Inst = struct {
1247 .break_inline = .@"break",1247 .break_inline = .@"break",
1248 .breakpoint = .node,1248 .breakpoint = .node,
1249 .call = .pl_node,1249 .call = .pl_node,
1250 .call_chkused = .pl_node,
1251 .call_compile_time = .pl_node,
1252 .call_nosuspend = .pl_node,
1253 .call_async = .pl_node,
1254 .cmp_lt = .pl_node,1250 .cmp_lt = .pl_node,
1255 .cmp_lte = .pl_node,1251 .cmp_lte = .pl_node,
1256 .cmp_eq = .pl_node,1252 .cmp_eq = .pl_node,
...@@ -1282,6 +1278,8 @@ pub const Inst = struct {...@@ -1282,6 +1278,8 @@ pub const Inst = struct {
1282 .field_val = .pl_node,1278 .field_val = .pl_node,
1283 .field_ptr_named = .pl_node,1279 .field_ptr_named = .pl_node,
1284 .field_val_named = .pl_node,1280 .field_val_named = .pl_node,
1281 .field_call_bind = .pl_node,
1282 .field_call_bind_named = .pl_node,
1285 .func = .pl_node,1283 .func = .pl_node,
1286 .func_inferred = .pl_node,1284 .func_inferred = .pl_node,
1287 .import = .str_tok,1285 .import = .str_tok,
...@@ -1301,7 +1299,6 @@ pub const Inst = struct {...@@ -1301,7 +1299,6 @@ pub const Inst = struct {
1301 .mod_rem = .pl_node,1299 .mod_rem = .pl_node,
1302 .mul = .pl_node,1300 .mul = .pl_node,
1303 .mulwrap = .pl_node,1301 .mulwrap = .pl_node,
1304 .param_type = .param_type,
1305 .ref = .un_tok,1302 .ref = .un_tok,
1306 .ret_node = .un_node,1303 .ret_node = .un_node,
1307 .ret_load = .un_node,1304 .ret_load = .un_node,
...@@ -2170,10 +2167,6 @@ pub const Inst = struct {...@@ -2170,10 +2167,6 @@ pub const Inst = struct {
2170 /// Points to a `Block`.2167 /// Points to a `Block`.
2171 payload_index: u32,2168 payload_index: u32,
2172 },2169 },
2173 param_type: struct {
2174 callee: Ref,
2175 param_index: u32,
2176 },
2177 @"unreachable": struct {2170 @"unreachable": struct {
2178 /// Offset from Decl AST node index.2171 /// Offset from Decl AST node index.
2179 /// `Tag` determines which kind of AST node this points to.2172 /// `Tag` determines which kind of AST node this points to.
...@@ -2244,7 +2237,6 @@ pub const Inst = struct {...@@ -2244,7 +2237,6 @@ pub const Inst = struct {
2244 ptr_type,2237 ptr_type,
2245 int_type,2238 int_type,
2246 bool_br,2239 bool_br,
2247 param_type,
2248 @"unreachable",2240 @"unreachable",
2249 @"break",2241 @"break",
2250 switch_capture,2242 switch_capture,
...@@ -2372,8 +2364,27 @@ pub const Inst = struct {...@@ -2372,8 +2364,27 @@ pub const Inst = struct {
2372 /// Stored inside extra, with trailing arguments according to `args_len`.2364 /// Stored inside extra, with trailing arguments according to `args_len`.
2373 /// Each argument is a `Ref`.2365 /// Each argument is a `Ref`.
2374 pub const Call = struct {2366 pub const Call = struct {
2367 // Note: Flags *must* come first so that unusedResultExpr
2368 // can find it when it goes to modify them.
2369 flags: Flags,
2375 callee: Ref,2370 callee: Ref,
2376 args_len: u32,2371
2372 pub const Flags = packed struct {
2373 /// std.builtin.CallOptions.Modifier in packed form
2374 pub const PackedModifier = u3;
2375 pub const PackedArgsLen = u28;
2376
2377 packed_modifier: PackedModifier,
2378 ensure_result_used: bool = false,
2379 args_len: PackedArgsLen,
2380
2381 comptime {
2382 if (@sizeOf(Flags) != 4 or @bitSizeOf(Flags) != 32)
2383 @compileError("Layout of Call.Flags needs to be updated!");
2384 if (@bitSizeOf(std.builtin.CallOptions.Modifier) != @bitSizeOf(PackedModifier))
2385 @compileError("Call.Flags.PackedModifier needs to be updated!");
2386 }
2387 };
2377 };2388 };
23782389
2379 pub const BuiltinCall = struct {2390 pub const BuiltinCall = struct {
src/print_zir.zig+22-20
...@@ -179,7 +179,6 @@ const Writer = struct {...@@ -179,7 +179,6 @@ const Writer = struct {
179 => try self.writeBoolBr(stream, inst),179 => try self.writeBoolBr(stream, inst),
180180
181 .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst),181 .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst),
182 .param_type => try self.writeParamType(stream, inst),
183 .ptr_type_simple => try self.writePtrTypeSimple(stream, inst),182 .ptr_type_simple => try self.writePtrTypeSimple(stream, inst),
184 .ptr_type => try self.writePtrType(stream, inst),183 .ptr_type => try self.writePtrType(stream, inst),
185 .int => try self.writeInt(stream, inst),184 .int => try self.writeInt(stream, inst),
...@@ -195,8 +194,6 @@ const Writer = struct {...@@ -195,8 +194,6 @@ const Writer = struct {
195194
196 .elem_ptr_node,195 .elem_ptr_node,
197 .elem_val_node,196 .elem_val_node,
198 .field_ptr_named,
199 .field_val_named,
200 .slice_start,197 .slice_start,
201 .slice_end,198 .slice_end,
202 .slice_sentinel,199 .slice_sentinel,
...@@ -288,12 +285,7 @@ const Writer = struct {...@@ -288,12 +285,7 @@ const Writer = struct {
288 .@"export" => try self.writePlNodeExport(stream, inst),285 .@"export" => try self.writePlNodeExport(stream, inst),
289 .export_value => try self.writePlNodeExportValue(stream, inst),286 .export_value => try self.writePlNodeExportValue(stream, inst),
290287
291 .call,288 .call => try self.writePlNodeCall(stream, inst),
292 .call_chkused,
293 .call_compile_time,
294 .call_nosuspend,
295 .call_async,
296 => try self.writePlNodeCall(stream, inst),
297289
298 .block,290 .block,
299 .block_inline,291 .block_inline,
...@@ -328,8 +320,14 @@ const Writer = struct {...@@ -328,8 +320,14 @@ const Writer = struct {
328320
329 .field_ptr,321 .field_ptr,
330 .field_val,322 .field_val,
323 .field_call_bind,
331 => try self.writePlNodeField(stream, inst),324 => try self.writePlNodeField(stream, inst),
332325
326 .field_ptr_named,
327 .field_val_named,
328 .field_call_bind_named,
329 => try self.writePlNodeFieldNamed(stream, inst),
330
333 .as_node => try self.writeAs(stream, inst),331 .as_node => try self.writeAs(stream, inst),
334332
335 .breakpoint,333 .breakpoint,
...@@ -481,16 +479,6 @@ const Writer = struct {...@@ -481,16 +479,6 @@ const Writer = struct {
481 try stream.writeAll("TODO)");479 try stream.writeAll("TODO)");
482 }480 }
483481
484 fn writeParamType(
485 self: *Writer,
486 stream: anytype,
487 inst: Zir.Inst.Index,
488 ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
489 const inst_data = self.code.instructions.items(.data)[inst].param_type;
490 try self.writeInstRef(stream, inst_data.callee);
491 try stream.print(", {d})", .{inst_data.param_index});
492 }
493
494 fn writePtrTypeSimple(482 fn writePtrTypeSimple(
495 self: *Writer,483 self: *Writer,
496 stream: anytype,484 stream: anytype,
...@@ -881,8 +869,12 @@ const Writer = struct {...@@ -881,8 +869,12 @@ const Writer = struct {
881 fn writePlNodeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {869 fn writePlNodeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
882 const inst_data = self.code.instructions.items(.data)[inst].pl_node;870 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
883 const extra = self.code.extraData(Zir.Inst.Call, inst_data.payload_index);871 const extra = self.code.extraData(Zir.Inst.Call, inst_data.payload_index);
884 const args = self.code.refSlice(extra.end, extra.data.args_len);872 const args = self.code.refSlice(extra.end, extra.data.flags.args_len);
885873
874 if (extra.data.flags.ensure_result_used) {
875 try stream.writeAll("nodiscard ");
876 }
877 try stream.print(".{s}, ", .{@tagName(@intToEnum(std.builtin.CallOptions.Modifier, extra.data.flags.packed_modifier))});
886 try self.writeInstRef(stream, extra.data.callee);878 try self.writeInstRef(stream, extra.data.callee);
887 try stream.writeAll(", [");879 try stream.writeAll(", [");
888 for (args) |arg, i| {880 for (args) |arg, i| {
...@@ -1637,6 +1629,16 @@ const Writer = struct {...@@ -1637,6 +1629,16 @@ const Writer = struct {
1637 try self.writeSrc(stream, inst_data.src());1629 try self.writeSrc(stream, inst_data.src());
1638 }1630 }
16391631
1632 fn writePlNodeFieldNamed(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1633 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1634 const extra = self.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
1635 try self.writeInstRef(stream, extra.lhs);
1636 try stream.writeAll(", ");
1637 try self.writeInstRef(stream, extra.field_name);
1638 try stream.writeAll(") ");
1639 try self.writeSrc(stream, inst_data.src());
1640 }
1641
1640 fn writeAs(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {1642 fn writeAs(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1641 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1643 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1642 const extra = self.code.extraData(Zir.Inst.As, inst_data.payload_index).data;1644 const extra = self.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
src/type.zig+60-2
...@@ -138,6 +138,7 @@ pub const Type = extern union {...@@ -138,6 +138,7 @@ pub const Type = extern union {
138 .type_info,138 .type_info,
139 => return .Union,139 => return .Union,
140140
141 .bound_fn => unreachable,
141 .var_args_param => unreachable, // can be any type142 .var_args_param => unreachable, // can be any type
142 }143 }
143 }144 }
...@@ -771,6 +772,7 @@ pub const Type = extern union {...@@ -771,6 +772,7 @@ pub const Type = extern union {
771 .type_info,772 .type_info,
772 .@"anyframe",773 .@"anyframe",
773 .generic_poison,774 .generic_poison,
775 .bound_fn,
774 => unreachable,776 => unreachable,
775777
776 .array_u8,778 .array_u8,
...@@ -936,6 +938,7 @@ pub const Type = extern union {...@@ -936,6 +938,7 @@ pub const Type = extern union {
936 .comptime_float,938 .comptime_float,
937 .noreturn,939 .noreturn,
938 .var_args_param,940 .var_args_param,
941 .bound_fn,
939 => return writer.writeAll(@tagName(t)),942 => return writer.writeAll(@tagName(t)),
940943
941 .enum_literal => return writer.writeAll("@Type(.EnumLiteral)"),944 .enum_literal => return writer.writeAll("@Type(.EnumLiteral)"),
...@@ -1248,6 +1251,7 @@ pub const Type = extern union {...@@ -1248,6 +1251,7 @@ pub const Type = extern union {
1248 .var_args_param => unreachable,1251 .var_args_param => unreachable,
1249 .inferred_alloc_mut => unreachable,1252 .inferred_alloc_mut => unreachable,
1250 .inferred_alloc_const => unreachable,1253 .inferred_alloc_const => unreachable,
1254 .bound_fn => unreachable,
12511255
1252 .array_u8,1256 .array_u8,
1253 .array_u8_sentinel_0,1257 .array_u8_sentinel_0,
...@@ -1479,6 +1483,7 @@ pub const Type = extern union {...@@ -1479,6 +1483,7 @@ pub const Type = extern union {
1479 .empty_struct_literal,1483 .empty_struct_literal,
1480 .@"opaque",1484 .@"opaque",
1481 .type_info,1485 .type_info,
1486 .bound_fn,
1482 => false,1487 => false,
14831488
1484 .inferred_alloc_const => unreachable,1489 .inferred_alloc_const => unreachable,
...@@ -1489,7 +1494,9 @@ pub const Type = extern union {...@@ -1489,7 +1494,9 @@ pub const Type = extern union {
1489 }1494 }
14901495
1491 pub fn isNoReturn(self: Type) bool {1496 pub fn isNoReturn(self: Type) bool {
1492 const definitely_correct_result = self.zigTypeTag() == .NoReturn;1497 const definitely_correct_result =
1498 self.tag_if_small_enough != .bound_fn and
1499 self.zigTypeTag() == .NoReturn;
1493 const fast_result = self.tag_if_small_enough == Tag.noreturn;1500 const fast_result = self.tag_if_small_enough == Tag.noreturn;
1494 assert(fast_result == definitely_correct_result);1501 assert(fast_result == definitely_correct_result);
1495 return fast_result;1502 return fast_result;
...@@ -1736,6 +1743,7 @@ pub const Type = extern union {...@@ -1736,6 +1743,7 @@ pub const Type = extern union {
1736 .@"opaque",1743 .@"opaque",
1737 .var_args_param,1744 .var_args_param,
1738 .type_info,1745 .type_info,
1746 .bound_fn,
1739 => unreachable,1747 => unreachable,
17401748
1741 .generic_poison => unreachable,1749 .generic_poison => unreachable,
...@@ -1768,6 +1776,7 @@ pub const Type = extern union {...@@ -1768,6 +1776,7 @@ pub const Type = extern union {
1768 .var_args_param => unreachable,1776 .var_args_param => unreachable,
1769 .generic_poison => unreachable,1777 .generic_poison => unreachable,
1770 .type_info => unreachable,1778 .type_info => unreachable,
1779 .bound_fn => unreachable,
17711780
1772 .@"struct" => {1781 .@"struct" => {
1773 const s = self.castTag(.@"struct").?.data;1782 const s = self.castTag(.@"struct").?.data;
...@@ -1951,6 +1960,7 @@ pub const Type = extern union {...@@ -1951,6 +1960,7 @@ pub const Type = extern union {
1951 .@"opaque" => unreachable,1960 .@"opaque" => unreachable,
1952 .var_args_param => unreachable,1961 .var_args_param => unreachable,
1953 .generic_poison => unreachable,1962 .generic_poison => unreachable,
1963 .bound_fn => unreachable,
19541964
1955 .@"struct" => {1965 .@"struct" => {
1956 @panic("TODO bitSize struct");1966 @panic("TODO bitSize struct");
...@@ -2353,6 +2363,51 @@ pub const Type = extern union {...@@ -2353,6 +2363,51 @@ pub const Type = extern union {
2353 }2363 }
2354 }2364 }
23552365
2366 /// Returns if type can be used for a runtime variable
2367 pub fn isValidVarType(self: Type, is_extern: bool) bool {
2368 var ty = self;
2369 while (true) switch (ty.zigTypeTag()) {
2370 .Bool,
2371 .Int,
2372 .Float,
2373 .ErrorSet,
2374 .Enum,
2375 .Frame,
2376 .AnyFrame,
2377 => return true,
2378
2379 .Opaque => return is_extern,
2380 .BoundFn,
2381 .ComptimeFloat,
2382 .ComptimeInt,
2383 .EnumLiteral,
2384 .NoReturn,
2385 .Type,
2386 .Void,
2387 .Undefined,
2388 .Null,
2389 => return false,
2390
2391 .Optional => {
2392 var buf: Payload.ElemType = undefined;
2393 return ty.optionalChild(&buf).isValidVarType(is_extern);
2394 },
2395 .Pointer, .Array, .Vector => ty = ty.elemType(),
2396 .ErrorUnion => ty = ty.errorUnionPayload(),
2397
2398 .Fn => @panic("TODO fn isValidVarType"),
2399 .Struct => {
2400 // TODO this is not always correct; introduce lazy value mechanism
2401 // and here we need to force a resolve of "type requires comptime".
2402 return true;
2403 },
2404 .Union => @panic("TODO union isValidVarType"),
2405 };
2406 }
2407
2408 /// For *[N]T, returns [N]T.
2409 /// For *T, returns T.
2410 /// For [*]T, returns T.
2356 pub fn childType(ty: Type) Type {2411 pub fn childType(ty: Type) Type {
2357 return switch (ty.tag()) {2412 return switch (ty.tag()) {
2358 .vector => ty.castTag(.vector).?.data.elem_type,2413 .vector => ty.castTag(.vector).?.data.elem_type,
...@@ -2934,6 +2989,7 @@ pub const Type = extern union {...@@ -2934,6 +2989,7 @@ pub const Type = extern union {
2934 .single_const_pointer,2989 .single_const_pointer,
2935 .single_mut_pointer,2990 .single_mut_pointer,
2936 .pointer,2991 .pointer,
2992 .bound_fn,
2937 => return null,2993 => return null,
29382994
2939 .@"struct" => {2995 .@"struct" => {
...@@ -3480,6 +3536,7 @@ pub const Type = extern union {...@@ -3480,6 +3536,7 @@ pub const Type = extern union {
3480 inferred_alloc_mut,3536 inferred_alloc_mut,
3481 /// Same as `inferred_alloc_mut` but the local is `var` not `const`.3537 /// Same as `inferred_alloc_mut` but the local is `var` not `const`.
3482 inferred_alloc_const, // See last_no_payload_tag below.3538 inferred_alloc_const, // See last_no_payload_tag below.
3539 bound_fn,
3483 // After this, the tag requires a payload.3540 // After this, the tag requires a payload.
34843541
3485 array_u8,3542 array_u8,
...@@ -3518,7 +3575,7 @@ pub const Type = extern union {...@@ -3518,7 +3575,7 @@ pub const Type = extern union {
3518 enum_full,3575 enum_full,
3519 enum_nonexhaustive,3576 enum_nonexhaustive,
35203577
3521 pub const last_no_payload_tag = Tag.inferred_alloc_const;3578 pub const last_no_payload_tag = Tag.bound_fn;
3522 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;3579 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
35233580
3524 pub fn Type(comptime t: Tag) type {3581 pub fn Type(comptime t: Tag) type {
...@@ -3585,6 +3642,7 @@ pub const Type = extern union {...@@ -3585,6 +3642,7 @@ pub const Type = extern union {
3585 .extern_options,3642 .extern_options,
3586 .type_info,3643 .type_info,
3587 .@"anyframe",3644 .@"anyframe",
3645 .bound_fn,
3588 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),3646 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
35893647
3590 .array_u8,3648 .array_u8,
src/value.zig+20
...@@ -159,6 +159,10 @@ pub const Value = extern union {...@@ -159,6 +159,10 @@ pub const Value = extern union {
159 /// Used to coordinate alloc_inferred, store_to_inferred_ptr, and resolve_inferred_alloc159 /// Used to coordinate alloc_inferred, store_to_inferred_ptr, and resolve_inferred_alloc
160 /// instructions for comptime code.160 /// instructions for comptime code.
161 inferred_alloc_comptime,161 inferred_alloc_comptime,
162 /// Used sometimes as the result of field_call_bind. This value is always temporary,
163 /// and refers directly to the air. It will never be referenced by the air itself.
164 /// TODO: This is probably a bad encoding, maybe put temp data in the sema instead.
165 bound_fn,
162166
163 pub const last_no_payload_tag = Tag.empty_array;167 pub const last_no_payload_tag = Tag.empty_array;
164 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;168 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
...@@ -279,6 +283,7 @@ pub const Value = extern union {...@@ -279,6 +283,7 @@ pub const Value = extern union {
279 .inferred_alloc => Payload.InferredAlloc,283 .inferred_alloc => Payload.InferredAlloc,
280 .@"struct" => Payload.Struct,284 .@"struct" => Payload.Struct,
281 .@"union" => Payload.Union,285 .@"union" => Payload.Union,
286 .bound_fn => Payload.BoundFn,
282 };287 };
283 }288 }
284289
...@@ -422,6 +427,7 @@ pub const Value = extern union {...@@ -422,6 +427,7 @@ pub const Value = extern union {
422 .extern_options_type,427 .extern_options_type,
423 .type_info_type,428 .type_info_type,
424 .generic_poison,429 .generic_poison,
430 .bound_fn,
425 => unreachable,431 => unreachable,
426432
427 .ty => {433 .ty => {
...@@ -716,6 +722,10 @@ pub const Value = extern union {...@@ -716,6 +722,10 @@ pub const Value = extern union {
716 try out_stream.writeAll("(opt_payload_ptr)");722 try out_stream.writeAll("(opt_payload_ptr)");
717 val = val.castTag(.opt_payload_ptr).?.data;723 val = val.castTag(.opt_payload_ptr).?.data;
718 },724 },
725 .bound_fn => {
726 const bound_func = val.castTag(.bound_fn).?.data;
727 return out_stream.print("(bound_fn %{}(%{})", .{ bound_func.func_inst, bound_func.arg0_inst });
728 },
719 };729 };
720 }730 }
721731
...@@ -2199,6 +2209,16 @@ pub const Value = extern union {...@@ -2199,6 +2209,16 @@ pub const Value = extern union {
2199 val: Value,2209 val: Value,
2200 },2210 },
2201 };2211 };
2212
2213 pub const BoundFn = struct {
2214 pub const base_tag = Tag.bound_fn;
2215
2216 base: Payload = Payload{ .tag = base_tag },
2217 data: struct {
2218 func_inst: Air.Inst.Ref,
2219 arg0_inst: Air.Inst.Ref,
2220 },
2221 };
2202 };2222 };
22032223
2204 /// Big enough to fit any non-BigInt value2224 /// Big enough to fit any non-BigInt value
test/behavior.zig+1
...@@ -10,6 +10,7 @@ test {...@@ -10,6 +10,7 @@ test {
10 _ = @import("behavior/eval.zig");10 _ = @import("behavior/eval.zig");
11 _ = @import("behavior/generics.zig");11 _ = @import("behavior/generics.zig");
12 _ = @import("behavior/if.zig");12 _ = @import("behavior/if.zig");
13 _ = @import("behavior/member_func.zig");
13 _ = @import("behavior/pointers.zig");14 _ = @import("behavior/pointers.zig");
14 _ = @import("behavior/sizeof_and_typeof.zig");15 _ = @import("behavior/sizeof_and_typeof.zig");
15 _ = @import("behavior/struct.zig");16 _ = @import("behavior/struct.zig");
test/behavior/eval.zig+28
...@@ -155,3 +155,31 @@ fn MakeType(comptime T: type) type {...@@ -155,3 +155,31 @@ fn MakeType(comptime T: type) type {
155 field: T,155 field: T,
156 };156 };
157}157}
158
159test "try to trick eval with runtime if" {
160 try expect(testTryToTrickEvalWithRuntimeIf(true) == 10);
161}
162
163fn testTryToTrickEvalWithRuntimeIf(b: bool) usize {
164 comptime var i: usize = 0;
165 inline while (i < 10) : (i += 1) {
166 const result = if (b) false else true;
167 _ = result;
168 }
169 comptime {
170 return i;
171 }
172}
173
174test "@setEvalBranchQuota" {
175 comptime {
176 // 1001 for the loop and then 1 more for the expect fn call
177 @setEvalBranchQuota(1002);
178 var i = 0;
179 var sum = 0;
180 while (i < 1001) : (i += 1) {
181 sum += i;
182 }
183 try expect(sum == 500500);
184 }
185}
test/behavior/eval_stage1.zig-28
...@@ -109,21 +109,6 @@ test "const slice" {...@@ -109,21 +109,6 @@ test "const slice" {
109 }109 }
110}110}
111111
112test "try to trick eval with runtime if" {
113 try expect(testTryToTrickEvalWithRuntimeIf(true) == 10);
114}
115
116fn testTryToTrickEvalWithRuntimeIf(b: bool) usize {
117 comptime var i: usize = 0;
118 inline while (i < 10) : (i += 1) {
119 const result = if (b) false else true;
120 _ = result;
121 }
122 comptime {
123 return i;
124 }
125}
126
127test "inlined loop has array literal with elided runtime scope on first iteration but not second iteration" {112test "inlined loop has array literal with elided runtime scope on first iteration but not second iteration" {
128 var runtime = [1]i32{3};113 var runtime = [1]i32{3};
129 comptime var i: usize = 0;114 comptime var i: usize = 0;
...@@ -276,19 +261,6 @@ fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) !void {...@@ -276,19 +261,6 @@ fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) !void {
276 try expect(ptr1 == ptr2);261 try expect(ptr1 == ptr2);
277}262}
278263
279test "@setEvalBranchQuota" {
280 comptime {
281 // 1001 for the loop and then 1 more for the expect fn call
282 @setEvalBranchQuota(1002);
283 var i = 0;
284 var sum = 0;
285 while (i < 1001) : (i += 1) {
286 sum += i;
287 }
288 try expect(sum == 500500);
289 }
290}
291
292test "float literal at compile time not lossy" {264test "float literal at compile time not lossy" {
293 try expect(16777216.0 + 1.0 == 16777217.0);265 try expect(16777216.0 + 1.0 == 16777217.0);
294 try expect(9007199254740992.0 + 1.0 == 9007199254740993.0);266 try expect(9007199254740992.0 + 1.0 == 9007199254740993.0);
test/behavior/generics.zig+16
...@@ -118,3 +118,19 @@ pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) type {...@@ -118,3 +118,19 @@ pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) type {
118 prealloc_items: [STATIC_SIZE]T,118 prealloc_items: [STATIC_SIZE]T,
119 };119 };
120}120}
121
122test "const decls in struct" {
123 try expect(GenericDataThing(3).count_plus_one == 4);
124}
125fn GenericDataThing(comptime count: isize) type {
126 return struct {
127 const count_plus_one = count + 1;
128 };
129}
130
131test "use generic param in generic param" {
132 try expect(aGenericFn(i32, 3, 4) == 7);
133}
134fn aGenericFn(comptime T: type, comptime a: T, b: T) T {
135 return a + b;
136}
test/behavior/generics_stage1.zig-16
...@@ -26,22 +26,6 @@ fn GenNode(comptime T: type) type {...@@ -26,22 +26,6 @@ fn GenNode(comptime T: type) type {
26 };26 };
27}27}
2828
29test "const decls in struct" {
30 try expect(GenericDataThing(3).count_plus_one == 4);
31}
32fn GenericDataThing(comptime count: isize) type {
33 return struct {
34 const count_plus_one = count + 1;
35 };
36}
37
38test "use generic param in generic param" {
39 try expect(aGenericFn(i32, 3, 4) == 7);
40}
41fn aGenericFn(comptime T: type, comptime a: T, b: T) T {
42 return a + b;
43}
44
45test "generic fn with implicit cast" {29test "generic fn with implicit cast" {
46 try expect(getFirstByte(u8, &[_]u8{13}) == 13);30 try expect(getFirstByte(u8, &[_]u8{13}) == 13);
47 try expect(getFirstByte(u16, &[_]u16{31 try expect(getFirstByte(u16, &[_]u16{
test/behavior/member_func.zig created+103
...@@ -0,0 +1,103 @@
1const expect = @import("std").testing.expect;
2
3const HasFuncs = struct {
4 state: u32,
5 func_field: fn (u32) u32,
6
7 fn inc(self: *HasFuncs) void {
8 self.state += 1;
9 }
10
11 fn get(self: HasFuncs) u32 {
12 return self.state;
13 }
14
15 fn getPtr(self: *const HasFuncs) *const u32 {
16 return &self.state;
17 }
18
19 fn one(_: u32) u32 {
20 return 1;
21 }
22 fn two(_: u32) u32 {
23 return 2;
24 }
25};
26
27test "standard field calls" {
28 try expect(HasFuncs.one(0) == 1);
29 try expect(HasFuncs.two(0) == 2);
30
31 var v: HasFuncs = undefined;
32 v.state = 0;
33 v.func_field = HasFuncs.one;
34
35 const pv = &v;
36 const pcv: *const HasFuncs = pv;
37
38 try expect(v.get() == 0);
39 v.inc();
40 try expect(v.state == 1);
41 try expect(v.get() == 1);
42
43 pv.inc();
44 try expect(v.state == 2);
45 try expect(pv.get() == 2);
46 try expect(v.getPtr().* == 2);
47 try expect(pcv.get() == 2);
48 try expect(pcv.getPtr().* == 2);
49
50 v.func_field = HasFuncs.one;
51 try expect(v.func_field(0) == 1);
52 try expect(pv.func_field(0) == 1);
53 try expect(pcv.func_field(0) == 1);
54
55 try expect(pcv.func_field(blk: {
56 pv.func_field = HasFuncs.two;
57 break :blk 0;
58 }) == 1);
59
60 v.func_field = HasFuncs.two;
61 try expect(v.func_field(0) == 2);
62 try expect(pv.func_field(0) == 2);
63 try expect(pcv.func_field(0) == 2);
64}
65
66test "@field field calls" {
67 try expect(@field(HasFuncs, "one")(0) == 1);
68 try expect(@field(HasFuncs, "two")(0) == 2);
69
70 var v: HasFuncs = undefined;
71 v.state = 0;
72 v.func_field = HasFuncs.one;
73
74 const pv = &v;
75 const pcv: *const HasFuncs = pv;
76
77 try expect(@field(v, "get")() == 0);
78 @field(v, "inc")();
79 try expect(v.state == 1);
80 try expect(@field(v, "get")() == 1);
81
82 @field(pv, "inc")();
83 try expect(v.state == 2);
84 try expect(@field(pv, "get")() == 2);
85 try expect(@field(v, "getPtr")().* == 2);
86 try expect(@field(pcv, "get")() == 2);
87 try expect(@field(pcv, "getPtr")().* == 2);
88
89 v.func_field = HasFuncs.one;
90 try expect(@field(v, "func_field")(0) == 1);
91 try expect(@field(pv, "func_field")(0) == 1);
92 try expect(@field(pcv, "func_field")(0) == 1);
93
94 try expect(@field(pcv, "func_field")(blk: {
95 pv.func_field = HasFuncs.two;
96 break :blk 0;
97 }) == 1);
98
99 v.func_field = HasFuncs.two;
100 try expect(@field(v, "func_field")(0) == 2);
101 try expect(@field(pv, "func_field")(0) == 2);
102 try expect(@field(pcv, "func_field")(0) == 2);
103}