| author | |
| committer | |
| log | 1cc5d4e758a95be373756e7c32f9bb46d21633c9 |
| tree | 9c142d3d009e1622f27a22c9228a6ff10b878721 |
| parent | 60b6e74468570a124f602a62b6bd2da95ba8c17c |
| signature |
* 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 builtins12 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 tag | 1938 | // 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 | }, |
| 1944 | 1948 | ||
| ... | @@ -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 | |||
| 4982 | fn 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 | } |
| 4992 | 5004 | ||
| 4993 | fn arrayAccess( | 5005 | fn 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); | ||
| 7901 | 7913 | ||
| 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); |
| 7904 | 7916 | ||
| 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 | } |
| 7915 | 7922 | ||
| 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 | }; | 7947 | fn calleeExpr( |
| 7941 | return rvalue(gz, rl, result, node); // TODO function call with result location | 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 | } | ||
| 7942 | } | 8001 | } |
| 7943 | 8002 | ||
| 7944 | pub const simple_types = std.ComptimeStringMap(Zir.Inst.Ref, .{ | 8003 | pub const simple_types = std.ComptimeStringMap(Zir.Inst.Ref, .{ |
| ... | @@ -9607,7 +9666,7 @@ const GenZir = struct { | ... | @@ -9607,7 +9666,7 @@ const GenZir = struct { |
| 9607 | 9666 | ||
| 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); |
| 9623 | 9683 | ||
| 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); |
| 9629 | 9692 | ||
| 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 | } |
| 2033 | 2030 | ||
| 2034 | fn 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 | |||
| 2073 | fn zirStr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 2031 | fn 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); |
| 2800 | 2756 | ||
| 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 functions | 2758 | 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 | } |
| 2808 | 2781 | ||
| 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 location | 3308 | 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-known | 3318 | // 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 | } |
| 4560 | 4535 | ||
| 4536 | fn 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 | |||
| 4561 | fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 4549 | fn 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 | } |
| 4586 | 4574 | ||
| 4575 | fn 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 | |||
| 4587 | fn zirIntCast(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 4588 | fn 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 | } |
| 9486 | 9487 | ||
| 9488 | fn 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 | |||
| 9487 | fn namespaceLookup( | 9630 | fn 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; |
| 9852 | 9995 | ||
| 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 | } |
| 9857 | 10003 | ||
| 9858 | const mod = sema.mod; | ||
| 9859 | const arena = sema.arena; | ||
| 9860 | |||
| 9861 | // undefined to anything | 10004 | // 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; |
| 9899 | 10042 | ||
| 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_only | 10169 | /// look at the function types_match_const_cast_only |
| 10027 | fn coerceInMemoryAllowed(dest_type: Type, src_type: Type, dest_is_mut: bool) InMemoryCoercionResult { | 10170 | fn 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; |
| 10030 | 10173 | ||
| ... | @@ -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; |
| 10036 | 10179 | ||
| 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 | } |
| 10083 | 10226 | ||
| 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); | ||
| 10086 | 10236 | ||
| 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 | } |
| 10090 | 10241 | ||
| 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, |
| 11610 | 11762 | ||
| 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 pointer | 332 | /// 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 whether | 349 | /// 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 value | 410 | /// 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 | }; |
| 2378 | 2389 | ||
| 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), |
| 180 | 180 | ||
| 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 { |
| 195 | 194 | ||
| 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), |
| 290 | 287 | ||
| 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), | ||
| 297 | 289 | ||
| 298 | .block, | 290 | .block, |
| 299 | .block_inline, | 291 | .block_inline, |
| ... | @@ -328,8 +320,14 @@ const Writer = struct { | ... | @@ -328,8 +320,14 @@ const Writer = struct { |
| 328 | 320 | ||
| 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), |
| 332 | 325 | ||
| 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), |
| 334 | 332 | ||
| 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 | } |
| 483 | 481 | ||
| 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); |
| 885 | 873 | ||
| 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 | } |
| 1639 | 1631 | ||
| 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, |
| 140 | 140 | ||
| 141 | .bound_fn => unreachable, | ||
| 141 | .var_args_param => unreachable, // can be any type | 142 | .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, |
| 775 | 777 | ||
| 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)), |
| 940 | 943 | ||
| 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, | ||
| 1251 | 1255 | ||
| 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, |
| 1483 | 1488 | ||
| 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 | } |
| 1490 | 1495 | ||
| 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, |
| 1740 | 1748 | ||
| 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, | ||
| 1771 | 1780 | ||
| 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, | ||
| 1954 | 1964 | ||
| 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 | } |
| 2355 | 2365 | ||
| 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, |
| 2938 | 2994 | ||
| 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. |
| 3484 | 3541 | ||
| 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, |
| 3520 | 3577 | ||
| 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; |
| 3523 | 3580 | ||
| 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"), |
| 3589 | 3647 | ||
| 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_alloc | 159 | /// 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, | ||
| 162 | 166 | ||
| 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 | } |
| 284 | 289 | ||
| ... | @@ -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, |
| 426 | 432 | ||
| 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 | } |
| 721 | 731 | ||
| ... | @@ -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 | }; |
| 2203 | 2223 | ||
| 2204 | /// Big enough to fit any non-BigInt value | 2224 | /// 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 | |||
| 159 | test "try to trick eval with runtime if" { | ||
| 160 | try expect(testTryToTrickEvalWithRuntimeIf(true) == 10); | ||
| 161 | } | ||
| 162 | |||
| 163 | fn 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 | |||
| 174 | test "@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 | } |
| 111 | 111 | ||
| 112 | test "try to trick eval with runtime if" { | ||
| 113 | try expect(testTryToTrickEvalWithRuntimeIf(true) == 10); | ||
| 114 | } | ||
| 115 | |||
| 116 | fn 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 | |||
| 127 | test "inlined loop has array literal with elided runtime scope on first iteration but not second iteration" { | 112 | test "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 | } |
| 278 | 263 | ||
| 279 | test "@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 | |||
| 292 | test "float literal at compile time not lossy" { | 264 | test "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 | |||
| 122 | test "const decls in struct" { | ||
| 123 | try expect(GenericDataThing(3).count_plus_one == 4); | ||
| 124 | } | ||
| 125 | fn GenericDataThing(comptime count: isize) type { | ||
| 126 | return struct { | ||
| 127 | const count_plus_one = count + 1; | ||
| 128 | }; | ||
| 129 | } | ||
| 130 | |||
| 131 | test "use generic param in generic param" { | ||
| 132 | try expect(aGenericFn(i32, 3, 4) == 7); | ||
| 133 | } | ||
| 134 | fn 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 | } |
| 28 | 28 | ||
| 29 | test "const decls in struct" { | ||
| 30 | try expect(GenericDataThing(3).count_plus_one == 4); | ||
| 31 | } | ||
| 32 | fn GenericDataThing(comptime count: isize) type { | ||
| 33 | return struct { | ||
| 34 | const count_plus_one = count + 1; | ||
| 35 | }; | ||
| 36 | } | ||
| 37 | |||
| 38 | test "use generic param in generic param" { | ||
| 39 | try expect(aGenericFn(i32, 3, 4) == 7); | ||
| 40 | } | ||
| 41 | fn aGenericFn(comptime T: type, comptime a: T, b: T) T { | ||
| 42 | return a + b; | ||
| 43 | } | ||
| 44 | |||
| 45 | test "generic fn with implicit cast" { | 29 | test "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 @@ | ||
| 1 | const expect = @import("std").testing.expect; | ||
| 2 | |||
| 3 | const 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 | |||
| 27 | test "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 | |||
| 66 | test "@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 | } | ||