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 {
5656 u32 => @field(extra, field.name),
5757 Zir.Inst.Ref => @enumToInt(@field(extra, field.name)),
5858 i32 => @bitCast(u32, @field(extra, field.name)),
59 Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)),
5960 else => @compileError("bad field type"),
6061 });
6162 }
......@@ -1934,11 +1935,14 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
19341935 // in the above while loop.
19351936 const zir_tags = gz.astgen.instructions.items(.tag);
19361937 switch (zir_tags[inst]) {
1937 // For some instructions, swap in a slightly different ZIR tag
1938 // For some instructions, modify the zir data
19381939 // so we can avoid a separate ensure_result_used instruction.
1939 .call_chkused => unreachable,
19401940 .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);
19421946 break :b true;
19431947 },
19441948
......@@ -1976,9 +1980,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
19761980 .bool_br_and,
19771981 .bool_br_or,
19781982 .bool_not,
1979 .call_compile_time,
1980 .call_nosuspend,
1981 .call_async,
19821983 .cmp_lt,
19831984 .cmp_lte,
19841985 .cmp_eq,
......@@ -1996,8 +1997,10 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
19961997 .elem_val_node,
19971998 .field_ptr,
19981999 .field_val,
2000 .field_call_bind,
19992001 .field_ptr_named,
20002002 .field_val_named,
2003 .field_call_bind_named,
20012004 .func,
20022005 .func_inferred,
20032006 .int,
......@@ -2012,7 +2015,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
20122015 .mod_rem,
20132016 .mul,
20142017 .mulwrap,
2015 .param_type,
20162018 .ref,
20172019 .shl,
20182020 .shr,
......@@ -4968,6 +4970,21 @@ fn fieldAccess(
49684970 scope: *Scope,
49694971 rl: ResultLoc,
49704972 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,
49714988) InnerError!Zir.Inst.Ref {
49724989 const astgen = gz.astgen;
49734990 const tree = astgen.tree;
......@@ -4978,16 +4995,11 @@ fn fieldAccess(
49784995 const dot_token = main_tokens[node];
49794996 const field_ident = dot_token + 1;
49804997 const str_index = try astgen.identAsString(field_ident);
4981 switch (rl) {
4982 .ref => return gz.addPlNode(.field_ptr, node, Zir.Inst.Field{
4983 .lhs = try expr(gz, scope, .ref, object_node),
4984 .field_name_start = str_index,
4985 }),
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 }
4998
4999 return gz.addPlNode(tag, node, Zir.Inst.Field{
5000 .lhs = try expr(gz, scope, lhs_rl, object_node),
5001 .field_name_start = str_index,
5002 });
49915003}
49925004
49935005fn arrayAccess(
......@@ -7169,16 +7181,15 @@ fn builtinCall(
71697181 return rvalue(gz, rl, result, node);
71707182 },
71717183 .field => {
7172 const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
71737184 if (rl == .ref) {
71747185 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
71757186 .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]),
71777188 });
71787189 }
71797190 const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{
71807191 .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]),
71827193 });
71837194 return rvalue(gz, rl, result, node);
71847195 },
......@@ -7554,7 +7565,7 @@ fn builtinCall(
75547565 },
75557566 .call => {
75567567 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]);
75587569 const args = try expr(gz, scope, .none, params[2]);
75597570 const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{
75607571 .options = options,
......@@ -7897,20 +7908,16 @@ fn callExpr(
78977908 call: Ast.full.Call,
78987909) InnerError!Zir.Inst.Ref {
78997910 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
79027914 const args = try astgen.gpa.alloc(Zir.Inst.Ref, call.ast.params.len);
79037915 defer astgen.gpa.free(args);
79047916
79057917 for (call.ast.params) |param_node, i| {
7906 const param_type = try gz.add(.{
7907 .tag = .param_type,
7908 .data = .{ .param_type = .{
7909 .callee = lhs,
7910 .param_index = @intCast(u32, i),
7911 } },
7912 });
7913 args[i] = try expr(gz, scope, .{ .coerced_ty = param_type }, param_node);
7918 // Parameters are always temporary values, they have no
7919 // meaningful result location. Sema will coerce them.
7920 args[i] = try expr(gz, scope, .none, param_node);
79147921 }
79157922
79167923 const modifier: std.builtin.CallOptions.Modifier = blk: {
......@@ -7925,20 +7932,72 @@ fn callExpr(
79257932 }
79267933 break :blk .auto;
79277934 };
7928 const result: Zir.Inst.Ref = res: {
7929 const tag: Zir.Inst.Tag = switch (modifier) {
7930 .auto => .call,
7931 .async_kw => .call_async,
7932 .never_tail => unreachable,
7933 .never_inline => unreachable,
7934 .no_async => .call_nosuspend,
7935 .always_tail => unreachable,
7936 .always_inline => unreachable,
7937 .compile_time => .call_compile_time,
7938 };
7939 break :res try gz.addCall(tag, lhs, args, node);
7940 };
7941 return rvalue(gz, rl, result, node); // TODO function call with result location
7935 const call_inst = try gz.addCall(modifier, callee, args, node);
7936 return rvalue(gz, rl, call_inst, node); // TODO function call with result location
7937}
7938
7939/// calleeExpr generates the function part of a call expression (f in f(x)), or the
7940/// callee argument to the @call() builtin. If the lhs is a field access or the
7941/// @field() builtin, we need to generate a special field_call_bind instruction
7942/// instead of the normal field_val or field_ptr. If this is a inst.func() call,
7943/// this instruction will capture the value of the first argument before evaluating
7944/// the other arguments. We need to use .ref here to guarantee we will be able to
7945/// promote an lvalue to an address if the first parameter requires it. This
7946/// unfortunately also means we need to take a reference to any types on the lhs.
7947fn calleeExpr(
7948 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 }
79428001}
79438002
79448003pub const simple_types = std.ComptimeStringMap(Zir.Inst.Ref, .{
......@@ -9607,7 +9666,7 @@ const GenZir = struct {
96079666
96089667 fn addCall(
96099668 gz: *GenZir,
9610 tag: Zir.Inst.Tag,
9669 modifier: std.builtin.CallOptions.Modifier,
96119670 callee: Zir.Inst.Ref,
96129671 args: []const Zir.Inst.Ref,
96139672 /// Absolute node index. This function does the conversion to offset from Decl.
......@@ -9616,20 +9675,24 @@ const GenZir = struct {
96169675 assert(callee != .none);
96179676 assert(src_node != 0);
96189677 const gpa = gz.astgen.gpa;
9678 const Call = Zir.Inst.Call;
96199679 try gz.instructions.ensureUnusedCapacity(gpa, 1);
96209680 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 +
96229682 args.len);
96239683
9624 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Call{
9684 const payload_index = gz.astgen.addExtraAssumeCapacity(Call{
96259685 .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 },
96279690 });
96289691 gz.astgen.appendRefsAssumeCapacity(args);
96299692
96309693 const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
96319694 gz.astgen.instructions.appendAssumeCapacity(.{
9632 .tag = tag,
9695 .tag = .call,
96339696 .data = .{ .pl_node = .{
96349697 .src_node = gz.nodeIndexToRelative(src_node),
96359698 .payload_index = payload_index,
src/Sema.zig+223-71
......@@ -185,11 +185,7 @@ pub fn analyzeBody(
185185 .bool_br_and => try sema.zirBoolBr(block, inst, false),
186186 .bool_br_or => try sema.zirBoolBr(block, inst, true),
187187 .c_import => try sema.zirCImport(block, inst),
188 .call => try sema.zirCall(block, inst, .auto, false),
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),
188 .call => try sema.zirCall(block, inst),
193189 .closure_get => try sema.zirClosureGet(block, inst),
194190 .cmp_lt => try sema.zirCmp(block, inst, .lt),
195191 .cmp_lte => try sema.zirCmp(block, inst, .lte),
......@@ -223,6 +219,8 @@ pub fn analyzeBody(
223219 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),
224220 .field_val => try sema.zirFieldVal(block, inst),
225221 .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),
226224 .func => try sema.zirFunc(block, inst, false),
227225 .func_inferred => try sema.zirFunc(block, inst, true),
228226 .import => try sema.zirImport(block, inst),
......@@ -244,7 +242,6 @@ pub fn analyzeBody(
244242 .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false),
245243 .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false),
246244 .optional_type => try sema.zirOptionalType(block, inst),
247 .param_type => try sema.zirParamType(block, inst),
248245 .ptr_type => try sema.zirPtrType(block, inst),
249246 .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst),
250247 .ref => try sema.zirRef(block, inst),
......@@ -2031,45 +2028,6 @@ fn zirStoreNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileE
20312028 return sema.storePtr(block, src, ptr, value);
20322029}
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
20732031fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
20742032 const tracy = trace(@src());
20752033 defer tracy.end();
......@@ -2786,8 +2744,6 @@ fn zirCall(
27862744 sema: *Sema,
27872745 block: *Scope.Block,
27882746 inst: Zir.Inst.Index,
2789 modifier: std.builtin.CallOptions.Modifier,
2790 ensure_result_used: bool,
27912747) CompileError!Air.Inst.Ref {
27922748 const tracy = trace(@src());
27932749 defer tracy.end();
......@@ -2796,14 +2752,31 @@ fn zirCall(
27962752 const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };
27972753 const call_src = inst_data.src();
27982754 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);
2802 // TODO handle function calls of generic functions
2803 const resolved_args = try sema.arena.alloc(Air.Inst.Ref, args.len);
2804 for (args) |zir_arg, i| {
2805 // the args are already casted to the result of a param type instruction.
2806 resolved_args[i] = sema.resolveInst(zir_arg);
2757 const modifier = @intToEnum(std.builtin.CallOptions.Modifier, extra.data.flags.packed_modifier);
2758 const ensure_result_used = extra.data.flags.ensure_result_used;
2759
2760 var func = sema.resolveInst(extra.data.callee);
2761 var resolved_args: []Air.Inst.Ref = undefined;
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 }
28072780 }
28082781
28092782 return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args);
......@@ -3334,14 +3307,16 @@ fn analyzeCall(
33343307 }
33353308 const arg_src = call_src; // TODO: better source location
33363309 const arg = uncasted_args[arg_i];
3337 if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {
3338 const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
3339 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
3340 } else if (is_comptime) {
3341 return sema.failWithNeededComptime(block, arg_src);
3310 if (is_comptime) {
3311 if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {
3312 const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
3313 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
3314 } else {
3315 return sema.failWithNeededComptime(block, arg_src);
3316 }
33423317 } else if (is_anytype) {
33433318 // 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.
33453320 const child_arg = try child_block.addArg(sema.typeOf(arg), 0);
33463321 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
33473322 }
......@@ -4558,6 +4533,19 @@ fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr
45584533 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
45594534}
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
45614549fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
45624550 const tracy = trace(@src());
45634551 defer tracy.end();
......@@ -4584,6 +4572,19 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp
45844572 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src);
45854573}
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
45874588fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
45884589 const tracy = trace(@src());
45894590 defer tracy.end();
......@@ -9484,6 +9485,148 @@ fn fieldPtr(
94849485 return mod.fail(&block.base, src, "type '{}' does not support field access", .{object_ty});
94859486}
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
94879630fn namespaceLookup(
94889631 sema: *Sema,
94899632 block: *Scope.Block,
......@@ -9850,14 +9993,14 @@ fn coerce(
98509993 if (dest_type.eql(inst_ty))
98519994 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());
985410000 if (in_memory_result == .ok) {
985510001 return sema.bitcast(block, dest_type, inst, inst_src);
985610002 }
985710003
9858 const mod = sema.mod;
9859 const arena = sema.arena;
9860
986110004 // undefined to anything
986210005 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
986310006 if (val.isUndef() or inst_ty.zigTypeTag() == .Undefined) {
......@@ -9898,7 +10041,7 @@ fn coerce(
989810041 if (inst_ty.ptrAddressSpace() != dest_type.ptrAddressSpace()) break :src_array_ptr;
989910042
990010043 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())) {
990210045 .ok => {},
990310046 .no_match => break :src_array_ptr,
990410047 }
......@@ -10024,7 +10167,7 @@ const InMemoryCoercionResult = enum {
1002410167/// * sentinel-terminated pointers can coerce into `[*]`
1002510168/// TODO improve this function to report recursive compile errors like it does in stage1.
1002610169/// 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 {
1002810171 if (dest_type.eql(src_type))
1002910172 return .ok;
1003010173
......@@ -10034,7 +10177,7 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool) InM
1003410177 const dest_info = dest_type.ptrInfo().data;
1003510178 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);
1003810181 if (child == .no_match) {
1003910182 return child;
1004010183 }
......@@ -10081,11 +10224,19 @@ fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool) InM
1008110224 return .no_match;
1008210225 }
1008310226
10084 assert(src_info.@"align" != 0);
10085 assert(dest_info.@"align" != 0);
10227 // If both pointers have alignment 0, it means they both want ABI alignment.
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") {
10088 return .no_match;
10237 if (dest_align > src_align) {
10238 return .no_match;
10239 }
1008910240 }
1009010241
1009110242 return .ok;
......@@ -11606,6 +11757,7 @@ fn typeHasOnePossibleValue(
1160611757 .single_const_pointer,
1160711758 .single_mut_pointer,
1160811759 .pointer,
11760 .bound_fn,
1160911761 => return null,
1161011762
1161111763 .@"struct" => {
src/Zir.zig+44-33
......@@ -70,6 +70,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en
7070 u32 => code.extra[i],
7171 Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),
7272 i32 => @bitCast(i32, code.extra[i]),
73 Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]),
7374 else => @compileError("bad field type"),
7475 };
7576 i += 1;
......@@ -222,17 +223,9 @@ pub const Inst = struct {
222223 break_inline,
223224 /// Uses the `node` union field.
224225 breakpoint,
225 /// Function call with modifier `.auto`.
226 /// Function call.
226227 /// Uses `pl_node`. AST node is the function call. Payload is `Call`.
227228 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,
236229 /// `<`
237230 /// Uses the `pl_node` union field. Payload is `Bin`.
238231 cmp_lt,
......@@ -327,6 +320,15 @@ pub const Inst = struct {
327320 /// This instruction also accepts a pointer.
328321 /// Uses `pl_node` field. The AST node is the a.b syntax. Payload is Field.
329322 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,
330332 /// Given a pointer to a struct or object that contains virtual fields, returns a pointer
331333 /// to the named field. The field name is a comptime instruction. Used by @field.
332334 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
......@@ -335,6 +337,15 @@ pub const Inst = struct {
335337 /// The field name is a comptime instruction. Used by @field.
336338 /// Uses `pl_node` field. The AST node is the builtin call. Payload is FieldNamed.
337339 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,
338349 /// Returns a function type, or a function instance, depending on whether
339350 /// the body_len is 0. Calling convention is auto.
340351 /// Uses the `pl_node` union field. `payload_index` points to a `Func`.
......@@ -395,14 +406,6 @@ pub const Inst = struct {
395406 /// Twos complement wrapping integer multiplication.
396407 /// Uses the `pl_node` union field. Payload is `Bin`.
397408 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,
406409 /// Turns an R-Value into a const L-Value. In other words, it takes a value,
407410 /// stores it in a memory location, and returns a const pointer to it. If the value
408411 /// is `comptime`, the memory location is global static constant data. Otherwise,
......@@ -988,10 +991,6 @@ pub const Inst = struct {
988991 .breakpoint,
989992 .fence,
990993 .call,
991 .call_chkused,
992 .call_compile_time,
993 .call_nosuspend,
994 .call_async,
995994 .cmp_lt,
996995 .cmp_lte,
997996 .cmp_eq,
......@@ -1017,8 +1016,10 @@ pub const Inst = struct {
10171016 .export_value,
10181017 .field_ptr,
10191018 .field_val,
1019 .field_call_bind,
10201020 .field_ptr_named,
10211021 .field_val_named,
1022 .field_call_bind_named,
10221023 .func,
10231024 .func_inferred,
10241025 .has_decl,
......@@ -1034,7 +1035,6 @@ pub const Inst = struct {
10341035 .mod_rem,
10351036 .mul,
10361037 .mulwrap,
1037 .param_type,
10381038 .ref,
10391039 .shl,
10401040 .shr,
......@@ -1247,10 +1247,6 @@ pub const Inst = struct {
12471247 .break_inline = .@"break",
12481248 .breakpoint = .node,
12491249 .call = .pl_node,
1250 .call_chkused = .pl_node,
1251 .call_compile_time = .pl_node,
1252 .call_nosuspend = .pl_node,
1253 .call_async = .pl_node,
12541250 .cmp_lt = .pl_node,
12551251 .cmp_lte = .pl_node,
12561252 .cmp_eq = .pl_node,
......@@ -1282,6 +1278,8 @@ pub const Inst = struct {
12821278 .field_val = .pl_node,
12831279 .field_ptr_named = .pl_node,
12841280 .field_val_named = .pl_node,
1281 .field_call_bind = .pl_node,
1282 .field_call_bind_named = .pl_node,
12851283 .func = .pl_node,
12861284 .func_inferred = .pl_node,
12871285 .import = .str_tok,
......@@ -1301,7 +1299,6 @@ pub const Inst = struct {
13011299 .mod_rem = .pl_node,
13021300 .mul = .pl_node,
13031301 .mulwrap = .pl_node,
1304 .param_type = .param_type,
13051302 .ref = .un_tok,
13061303 .ret_node = .un_node,
13071304 .ret_load = .un_node,
......@@ -2170,10 +2167,6 @@ pub const Inst = struct {
21702167 /// Points to a `Block`.
21712168 payload_index: u32,
21722169 },
2173 param_type: struct {
2174 callee: Ref,
2175 param_index: u32,
2176 },
21772170 @"unreachable": struct {
21782171 /// Offset from Decl AST node index.
21792172 /// `Tag` determines which kind of AST node this points to.
......@@ -2244,7 +2237,6 @@ pub const Inst = struct {
22442237 ptr_type,
22452238 int_type,
22462239 bool_br,
2247 param_type,
22482240 @"unreachable",
22492241 @"break",
22502242 switch_capture,
......@@ -2372,8 +2364,27 @@ pub const Inst = struct {
23722364 /// Stored inside extra, with trailing arguments according to `args_len`.
23732365 /// Each argument is a `Ref`.
23742366 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,
23752370 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 };
23772388 };
23782389
23792390 pub const BuiltinCall = struct {
src/print_zir.zig+22-20
......@@ -179,7 +179,6 @@ const Writer = struct {
179179 => try self.writeBoolBr(stream, inst),
180180
181181 .array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst),
182 .param_type => try self.writeParamType(stream, inst),
183182 .ptr_type_simple => try self.writePtrTypeSimple(stream, inst),
184183 .ptr_type => try self.writePtrType(stream, inst),
185184 .int => try self.writeInt(stream, inst),
......@@ -195,8 +194,6 @@ const Writer = struct {
195194
196195 .elem_ptr_node,
197196 .elem_val_node,
198 .field_ptr_named,
199 .field_val_named,
200197 .slice_start,
201198 .slice_end,
202199 .slice_sentinel,
......@@ -288,12 +285,7 @@ const Writer = struct {
288285 .@"export" => try self.writePlNodeExport(stream, inst),
289286 .export_value => try self.writePlNodeExportValue(stream, inst),
290287
291 .call,
292 .call_chkused,
293 .call_compile_time,
294 .call_nosuspend,
295 .call_async,
296 => try self.writePlNodeCall(stream, inst),
288 .call => try self.writePlNodeCall(stream, inst),
297289
298290 .block,
299291 .block_inline,
......@@ -328,8 +320,14 @@ const Writer = struct {
328320
329321 .field_ptr,
330322 .field_val,
323 .field_call_bind,
331324 => 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
333331 .as_node => try self.writeAs(stream, inst),
334332
335333 .breakpoint,
......@@ -481,16 +479,6 @@ const Writer = struct {
481479 try stream.writeAll("TODO)");
482480 }
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
494482 fn writePtrTypeSimple(
495483 self: *Writer,
496484 stream: anytype,
......@@ -881,8 +869,12 @@ const Writer = struct {
881869 fn writePlNodeCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
882870 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
883871 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))});
886878 try self.writeInstRef(stream, extra.data.callee);
887879 try stream.writeAll(", [");
888880 for (args) |arg, i| {
......@@ -1637,6 +1629,16 @@ const Writer = struct {
16371629 try self.writeSrc(stream, inst_data.src());
16381630 }
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
16401642 fn writeAs(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
16411643 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
16421644 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 {
138138 .type_info,
139139 => return .Union,
140140
141 .bound_fn => unreachable,
141142 .var_args_param => unreachable, // can be any type
142143 }
143144 }
......@@ -771,6 +772,7 @@ pub const Type = extern union {
771772 .type_info,
772773 .@"anyframe",
773774 .generic_poison,
775 .bound_fn,
774776 => unreachable,
775777
776778 .array_u8,
......@@ -936,6 +938,7 @@ pub const Type = extern union {
936938 .comptime_float,
937939 .noreturn,
938940 .var_args_param,
941 .bound_fn,
939942 => return writer.writeAll(@tagName(t)),
940943
941944 .enum_literal => return writer.writeAll("@Type(.EnumLiteral)"),
......@@ -1248,6 +1251,7 @@ pub const Type = extern union {
12481251 .var_args_param => unreachable,
12491252 .inferred_alloc_mut => unreachable,
12501253 .inferred_alloc_const => unreachable,
1254 .bound_fn => unreachable,
12511255
12521256 .array_u8,
12531257 .array_u8_sentinel_0,
......@@ -1479,6 +1483,7 @@ pub const Type = extern union {
14791483 .empty_struct_literal,
14801484 .@"opaque",
14811485 .type_info,
1486 .bound_fn,
14821487 => false,
14831488
14841489 .inferred_alloc_const => unreachable,
......@@ -1489,7 +1494,9 @@ pub const Type = extern union {
14891494 }
14901495
14911496 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;
14931500 const fast_result = self.tag_if_small_enough == Tag.noreturn;
14941501 assert(fast_result == definitely_correct_result);
14951502 return fast_result;
......@@ -1736,6 +1743,7 @@ pub const Type = extern union {
17361743 .@"opaque",
17371744 .var_args_param,
17381745 .type_info,
1746 .bound_fn,
17391747 => unreachable,
17401748
17411749 .generic_poison => unreachable,
......@@ -1768,6 +1776,7 @@ pub const Type = extern union {
17681776 .var_args_param => unreachable,
17691777 .generic_poison => unreachable,
17701778 .type_info => unreachable,
1779 .bound_fn => unreachable,
17711780
17721781 .@"struct" => {
17731782 const s = self.castTag(.@"struct").?.data;
......@@ -1951,6 +1960,7 @@ pub const Type = extern union {
19511960 .@"opaque" => unreachable,
19521961 .var_args_param => unreachable,
19531962 .generic_poison => unreachable,
1963 .bound_fn => unreachable,
19541964
19551965 .@"struct" => {
19561966 @panic("TODO bitSize struct");
......@@ -2353,6 +2363,51 @@ pub const Type = extern union {
23532363 }
23542364 }
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.
23562411 pub fn childType(ty: Type) Type {
23572412 return switch (ty.tag()) {
23582413 .vector => ty.castTag(.vector).?.data.elem_type,
......@@ -2934,6 +2989,7 @@ pub const Type = extern union {
29342989 .single_const_pointer,
29352990 .single_mut_pointer,
29362991 .pointer,
2992 .bound_fn,
29372993 => return null,
29382994
29392995 .@"struct" => {
......@@ -3480,6 +3536,7 @@ pub const Type = extern union {
34803536 inferred_alloc_mut,
34813537 /// Same as `inferred_alloc_mut` but the local is `var` not `const`.
34823538 inferred_alloc_const, // See last_no_payload_tag below.
3539 bound_fn,
34833540 // After this, the tag requires a payload.
34843541
34853542 array_u8,
......@@ -3518,7 +3575,7 @@ pub const Type = extern union {
35183575 enum_full,
35193576 enum_nonexhaustive,
35203577
3521 pub const last_no_payload_tag = Tag.inferred_alloc_const;
3578 pub const last_no_payload_tag = Tag.bound_fn;
35223579 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
35233580
35243581 pub fn Type(comptime t: Tag) type {
......@@ -3585,6 +3642,7 @@ pub const Type = extern union {
35853642 .extern_options,
35863643 .type_info,
35873644 .@"anyframe",
3645 .bound_fn,
35883646 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
35893647
35903648 .array_u8,
src/value.zig+20
......@@ -159,6 +159,10 @@ pub const Value = extern union {
159159 /// Used to coordinate alloc_inferred, store_to_inferred_ptr, and resolve_inferred_alloc
160160 /// instructions for comptime code.
161161 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
163167 pub const last_no_payload_tag = Tag.empty_array;
164168 pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
......@@ -279,6 +283,7 @@ pub const Value = extern union {
279283 .inferred_alloc => Payload.InferredAlloc,
280284 .@"struct" => Payload.Struct,
281285 .@"union" => Payload.Union,
286 .bound_fn => Payload.BoundFn,
282287 };
283288 }
284289
......@@ -422,6 +427,7 @@ pub const Value = extern union {
422427 .extern_options_type,
423428 .type_info_type,
424429 .generic_poison,
430 .bound_fn,
425431 => unreachable,
426432
427433 .ty => {
......@@ -716,6 +722,10 @@ pub const Value = extern union {
716722 try out_stream.writeAll("(opt_payload_ptr)");
717723 val = val.castTag(.opt_payload_ptr).?.data;
718724 },
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 },
719729 };
720730 }
721731
......@@ -2199,6 +2209,16 @@ pub const Value = extern union {
21992209 val: Value,
22002210 },
22012211 };
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 };
22022222 };
22032223
22042224 /// Big enough to fit any non-BigInt value
test/behavior.zig+1
......@@ -10,6 +10,7 @@ test {
1010 _ = @import("behavior/eval.zig");
1111 _ = @import("behavior/generics.zig");
1212 _ = @import("behavior/if.zig");
13 _ = @import("behavior/member_func.zig");
1314 _ = @import("behavior/pointers.zig");
1415 _ = @import("behavior/sizeof_and_typeof.zig");
1516 _ = @import("behavior/struct.zig");
test/behavior/eval.zig+28
......@@ -155,3 +155,31 @@ fn MakeType(comptime T: type) type {
155155 field: T,
156156 };
157157}
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" {
109109 }
110110}
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
127112test "inlined loop has array literal with elided runtime scope on first iteration but not second iteration" {
128113 var runtime = [1]i32{3};
129114 comptime var i: usize = 0;
......@@ -276,19 +261,6 @@ fn assertEqualPtrs(ptr1: *const u8, ptr2: *const u8) !void {
276261 try expect(ptr1 == ptr2);
277262}
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
292264test "float literal at compile time not lossy" {
293265 try expect(16777216.0 + 1.0 == 16777217.0);
294266 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 {
118118 prealloc_items: [STATIC_SIZE]T,
119119 };
120120}
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 {
2626 };
2727}
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
4529test "generic fn with implicit cast" {
4630 try expect(getFirstByte(u8, &[_]u8{13}) == 13);
4731 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}