| author | |
| committer | |
| log | dae4c18aa7999a866b6b145ed3c88cb6c06852be |
| tree | 6aa41102b19c8e0af04bf1dd8af5158c4cc8b3a5 |
| parent | d5f173d28f2991e05dad399ed4d688297e0a5ca7 |
`func_extended` ZIR instructions now have a one of the unused flags used
as a `has_comptime_bits` boolean. When set, it means 1 or more
parameters are `comptime`. In this case, there is a u32 per every 32
parameters (usually just 1 u32) with each bit indicating whether the
corresponding parameter is `comptime`.
Sema uses this information to correctly mark generic functions as
generic. There is now a TODO compile error in place in case a generic
function call happens. A future commit will do the generic function call
implementation.4 files changed, 132 insertions(+), 6 deletions(-)
src/AstGen.zig+65-1| ... | ... | @@ -1064,11 +1064,28 @@ fn fnProtoExpr( |
| 1064 | 1064 | const param_types = try gpa.alloc(Zir.Inst.Ref, param_count); |
| 1065 | 1065 | defer gpa.free(param_types); |
| 1066 | 1066 | |
| 1067 | const bits_per_param = 1; | |
| 1068 | const params_per_u32 = 32 / bits_per_param; | |
| 1069 | // We only need this if there are greater than params_per_u32 fields. | |
| 1070 | var bit_bag = ArrayListUnmanaged(u32){}; | |
| 1071 | defer bit_bag.deinit(gpa); | |
| 1072 | var cur_bit_bag: u32 = 0; | |
| 1067 | 1073 | var is_var_args = false; |
| 1068 | 1074 | { |
| 1069 | 1075 | var param_type_i: usize = 0; |
| 1070 | 1076 | var it = fn_proto.iterate(tree.*); |
| 1071 | 1077 | while (it.next()) |param| : (param_type_i += 1) { |
| 1078 | if (param_type_i % params_per_u32 == 0 and param_type_i != 0) { | |
| 1079 | try bit_bag.append(gpa, cur_bit_bag); | |
| 1080 | cur_bit_bag = 0; | |
| 1081 | } | |
| 1082 | const is_comptime = if (param.comptime_noalias) |token| | |
| 1083 | token_tags[token] == .keyword_comptime | |
| 1084 | else | |
| 1085 | false; | |
| 1086 | cur_bit_bag = (cur_bit_bag >> bits_per_param) | | |
| 1087 | (@as(u32, @boolToInt(is_comptime)) << 31); | |
| 1088 | ||
| 1072 | 1089 | if (param.anytype_ellipsis3) |token| { |
| 1073 | 1090 | switch (token_tags[token]) { |
| 1074 | 1091 | .keyword_anytype => { |
| ... | ... | @@ -1088,6 +1105,11 @@ fn fnProtoExpr( |
| 1088 | 1105 | try expr(gz, scope, .{ .ty = .type_type }, param_type_node); |
| 1089 | 1106 | } |
| 1090 | 1107 | assert(param_type_i == param_count); |
| 1108 | ||
| 1109 | const empty_slot_count = params_per_u32 - (param_type_i % params_per_u32); | |
| 1110 | if (empty_slot_count < params_per_u32) { | |
| 1111 | cur_bit_bag >>= @intCast(u5, empty_slot_count * bits_per_param); | |
| 1112 | } | |
| 1091 | 1113 | } |
| 1092 | 1114 | |
| 1093 | 1115 | const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: { |
| ... | ... | @@ -1131,6 +1153,8 @@ fn fnProtoExpr( |
| 1131 | 1153 | .is_inferred_error = false, |
| 1132 | 1154 | .is_test = false, |
| 1133 | 1155 | .is_extern = false, |
| 1156 | .cur_bit_bag = cur_bit_bag, | |
| 1157 | .bit_bag = bit_bag.items, | |
| 1134 | 1158 | }); |
| 1135 | 1159 | return rvalue(gz, rl, result, fn_proto.ast.proto_node); |
| 1136 | 1160 | } |
| ... | ... | @@ -2916,11 +2940,28 @@ fn fnDecl( |
| 2916 | 2940 | const param_types = try gpa.alloc(Zir.Inst.Ref, param_count); |
| 2917 | 2941 | defer gpa.free(param_types); |
| 2918 | 2942 | |
| 2943 | const bits_per_param = 1; | |
| 2944 | const params_per_u32 = 32 / bits_per_param; | |
| 2945 | // We only need this if there are greater than params_per_u32 fields. | |
| 2946 | var bit_bag = ArrayListUnmanaged(u32){}; | |
| 2947 | defer bit_bag.deinit(gpa); | |
| 2948 | var cur_bit_bag: u32 = 0; | |
| 2919 | 2949 | var is_var_args = false; |
| 2920 | 2950 | { |
| 2921 | 2951 | var param_type_i: usize = 0; |
| 2922 | 2952 | var it = fn_proto.iterate(tree.*); |
| 2923 | 2953 | while (it.next()) |param| : (param_type_i += 1) { |
| 2954 | if (param_type_i % params_per_u32 == 0 and param_type_i != 0) { | |
| 2955 | try bit_bag.append(gpa, cur_bit_bag); | |
| 2956 | cur_bit_bag = 0; | |
| 2957 | } | |
| 2958 | const is_comptime = if (param.comptime_noalias) |token| | |
| 2959 | token_tags[token] == .keyword_comptime | |
| 2960 | else | |
| 2961 | false; | |
| 2962 | cur_bit_bag = (cur_bit_bag >> bits_per_param) | | |
| 2963 | (@as(u32, @boolToInt(is_comptime)) << 31); | |
| 2964 | ||
| 2924 | 2965 | if (param.anytype_ellipsis3) |token| { |
| 2925 | 2966 | switch (token_tags[token]) { |
| 2926 | 2967 | .keyword_anytype => { |
| ... | ... | @@ -2940,6 +2981,11 @@ fn fnDecl( |
| 2940 | 2981 | try expr(&decl_gz, &decl_gz.base, .{ .ty = .type_type }, param_type_node); |
| 2941 | 2982 | } |
| 2942 | 2983 | assert(param_type_i == param_count); |
| 2984 | ||
| 2985 | const empty_slot_count = params_per_u32 - (param_type_i % params_per_u32); | |
| 2986 | if (empty_slot_count < params_per_u32) { | |
| 2987 | cur_bit_bag >>= @intCast(u5, empty_slot_count * bits_per_param); | |
| 2988 | } | |
| 2943 | 2989 | } |
| 2944 | 2990 | |
| 2945 | 2991 | const lib_name: u32 = if (fn_proto.lib_name) |lib_name_token| blk: { |
| ... | ... | @@ -3001,6 +3047,8 @@ fn fnDecl( |
| 3001 | 3047 | .is_inferred_error = false, |
| 3002 | 3048 | .is_test = false, |
| 3003 | 3049 | .is_extern = true, |
| 3050 | .cur_bit_bag = cur_bit_bag, | |
| 3051 | .bit_bag = bit_bag.items, | |
| 3004 | 3052 | }); |
| 3005 | 3053 | } else func: { |
| 3006 | 3054 | if (is_var_args) { |
| ... | ... | @@ -3094,6 +3142,8 @@ fn fnDecl( |
| 3094 | 3142 | .is_inferred_error = is_inferred_error, |
| 3095 | 3143 | .is_test = false, |
| 3096 | 3144 | .is_extern = false, |
| 3145 | .cur_bit_bag = cur_bit_bag, | |
| 3146 | .bit_bag = bit_bag.items, | |
| 3097 | 3147 | }); |
| 3098 | 3148 | }; |
| 3099 | 3149 | |
| ... | ... | @@ -3439,6 +3489,8 @@ fn testDecl( |
| 3439 | 3489 | .is_inferred_error = true, |
| 3440 | 3490 | .is_test = true, |
| 3441 | 3491 | .is_extern = false, |
| 3492 | .cur_bit_bag = 0, | |
| 3493 | .bit_bag = &.{}, | |
| 3442 | 3494 | }); |
| 3443 | 3495 | |
| 3444 | 3496 | _ = try decl_block.addBreak(.break_inline, block_inst, func_inst); |
| ... | ... | @@ -9135,6 +9187,8 @@ const GenZir = struct { |
| 9135 | 9187 | is_inferred_error: bool, |
| 9136 | 9188 | is_test: bool, |
| 9137 | 9189 | is_extern: bool, |
| 9190 | cur_bit_bag: u32, | |
| 9191 | bit_bag: []const u32, | |
| 9138 | 9192 | }) !Zir.Inst.Ref { |
| 9139 | 9193 | assert(args.src_node != 0); |
| 9140 | 9194 | assert(args.ret_ty != .none); |
| ... | ... | @@ -9172,13 +9226,18 @@ const GenZir = struct { |
| 9172 | 9226 | src_locs = &src_locs_buffer; |
| 9173 | 9227 | } |
| 9174 | 9228 | |
| 9229 | const any_are_comptime = args.cur_bit_bag != 0 or for (args.bit_bag) |x| { | |
| 9230 | if (x != 0) break true; | |
| 9231 | } else false; | |
| 9232 | ||
| 9175 | 9233 | if (args.cc != .none or args.lib_name != 0 or |
| 9176 | 9234 | args.is_var_args or args.is_test or args.align_inst != .none or |
| 9177 | args.is_extern) | |
| 9235 | args.is_extern or any_are_comptime) | |
| 9178 | 9236 | { |
| 9179 | 9237 | try astgen.extra.ensureUnusedCapacity( |
| 9180 | 9238 | gpa, |
| 9181 | 9239 | @typeInfo(Zir.Inst.ExtendedFunc).Struct.fields.len + |
| 9240 | @boolToInt(any_are_comptime) + args.bit_bag.len + | |
| 9182 | 9241 | args.param_types.len + args.body.len + src_locs.len + |
| 9183 | 9242 | @boolToInt(args.lib_name != 0) + |
| 9184 | 9243 | @boolToInt(args.align_inst != .none) + |
| ... | ... | @@ -9199,6 +9258,10 @@ const GenZir = struct { |
| 9199 | 9258 | if (args.align_inst != .none) { |
| 9200 | 9259 | astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst)); |
| 9201 | 9260 | } |
| 9261 | if (any_are_comptime) { | |
| 9262 | astgen.extra.appendSliceAssumeCapacity(args.bit_bag); // Likely empty. | |
| 9263 | astgen.extra.appendAssumeCapacity(args.cur_bit_bag); | |
| 9264 | } | |
| 9202 | 9265 | astgen.appendRefsAssumeCapacity(args.param_types); |
| 9203 | 9266 | astgen.extra.appendSliceAssumeCapacity(args.body); |
| 9204 | 9267 | astgen.extra.appendSliceAssumeCapacity(src_locs); |
| ... | ... | @@ -9216,6 +9279,7 @@ const GenZir = struct { |
| 9216 | 9279 | .has_align = args.align_inst != .none, |
| 9217 | 9280 | .is_test = args.is_test, |
| 9218 | 9281 | .is_extern = args.is_extern, |
| 9282 | .has_comptime_bits = any_are_comptime, | |
| 9219 | 9283 | }), |
| 9220 | 9284 | .operand = payload_index, |
| 9221 | 9285 | } }, |
src/Sema.zig+28-1| ... | ... | @@ -104,6 +104,9 @@ pub fn analyzeFnBody( |
| 104 | 104 | extra_index += @boolToInt(small.has_lib_name); |
| 105 | 105 | extra_index += @boolToInt(small.has_cc); |
| 106 | 106 | extra_index += @boolToInt(small.has_align); |
| 107 | if (small.has_comptime_bits) { | |
| 108 | extra_index += (extra.data.param_types_len + 31) / 32; | |
| 109 | } | |
| 107 | 110 | extra_index += extra.data.param_types_len; |
| 108 | 111 | const body = sema.code.extra[extra_index..][0..extra.data.body_len]; |
| 109 | 112 | break :blk body; |
| ... | ... | @@ -2533,6 +2536,9 @@ fn analyzeCall( |
| 2533 | 2536 | |
| 2534 | 2537 | break :res result; |
| 2535 | 2538 | } else res: { |
| 2539 | if (func_ty.fnIsGeneric()) { | |
| 2540 | return sema.mod.fail(&block.base, func_src, "TODO implement generic fn call", .{}); | |
| 2541 | } | |
| 2536 | 2542 | try sema.requireRuntimeBlock(block, call_src); |
| 2537 | 2543 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + |
| 2538 | 2544 | args.len); |
| ... | ... | @@ -3208,6 +3214,7 @@ fn zirFunc( |
| 3208 | 3214 | false, |
| 3209 | 3215 | src_locs, |
| 3210 | 3216 | null, |
| 3217 | &.{}, | |
| 3211 | 3218 | ); |
| 3212 | 3219 | } |
| 3213 | 3220 | |
| ... | ... | @@ -3225,6 +3232,7 @@ fn funcCommon( |
| 3225 | 3232 | is_extern: bool, |
| 3226 | 3233 | src_locs: Zir.Inst.Func.SrcLocs, |
| 3227 | 3234 | opt_lib_name: ?[]const u8, |
| 3235 | comptime_bits: []const u32, | |
| 3228 | 3236 | ) CompileError!Air.Inst.Ref { |
| 3229 | 3237 | const src: LazySrcLoc = .{ .node_offset = src_node_offset }; |
| 3230 | 3238 | const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; |
| ... | ... | @@ -3257,13 +3265,23 @@ fn funcCommon( |
| 3257 | 3265 | } |
| 3258 | 3266 | } |
| 3259 | 3267 | |
| 3268 | var any_are_comptime = false; | |
| 3260 | 3269 | const param_types = try sema.arena.alloc(Type, zir_param_types.len); |
| 3261 | 3270 | for (zir_param_types) |param_type, i| { |
| 3262 | 3271 | // TODO make a compile error from `resolveType` report the source location |
| 3263 | 3272 | // of the specific parameter. Will need to take a similar strategy as |
| 3264 | 3273 | // `resolveSwitchItemVal` to avoid resolving the source location unless |
| 3265 | 3274 | // we actually need to report an error. |
| 3266 | param_types[i] = try sema.resolveType(block, src, param_type); | |
| 3275 | const param_src = src; | |
| 3276 | param_types[i] = try sema.resolveType(block, param_src, param_type); | |
| 3277 | ||
| 3278 | any_are_comptime = any_are_comptime or blk: { | |
| 3279 | if (comptime_bits.len == 0) | |
| 3280 | break :blk false; | |
| 3281 | const bag = comptime_bits[i / 32]; | |
| 3282 | const is_comptime = @truncate(u1, bag >> @intCast(u5, i % 32)) != 0; | |
| 3283 | break :blk is_comptime; | |
| 3284 | }; | |
| 3267 | 3285 | } |
| 3268 | 3286 | |
| 3269 | 3287 | if (align_val.tag() != .null_value) { |
| ... | ... | @@ -3286,6 +3304,7 @@ fn funcCommon( |
| 3286 | 3304 | .return_type = return_type, |
| 3287 | 3305 | .cc = cc, |
| 3288 | 3306 | .is_var_args = var_args, |
| 3307 | .is_generic = any_are_comptime, | |
| 3289 | 3308 | }); |
| 3290 | 3309 | }; |
| 3291 | 3310 | |
| ... | ... | @@ -6526,6 +6545,13 @@ fn zirFuncExtended( |
| 6526 | 6545 | break :blk align_tv.val; |
| 6527 | 6546 | } else Value.initTag(.null_value); |
| 6528 | 6547 | |
| 6548 | const comptime_bits: []const u32 = if (!small.has_comptime_bits) &.{} else blk: { | |
| 6549 | const amt = (extra.data.param_types_len + 31) / 32; | |
| 6550 | const bit_bags = sema.code.extra[extra_index..][0..amt]; | |
| 6551 | extra_index += amt; | |
| 6552 | break :blk bit_bags; | |
| 6553 | }; | |
| 6554 | ||
| 6529 | 6555 | const param_types = sema.code.refSlice(extra_index, extra.data.param_types_len); |
| 6530 | 6556 | extra_index += param_types.len; |
| 6531 | 6557 | |
| ... | ... | @@ -6554,6 +6580,7 @@ fn zirFuncExtended( |
| 6554 | 6580 | is_extern, |
| 6555 | 6581 | src_locs, |
| 6556 | 6582 | lib_name, |
| 6583 | comptime_bits, | |
| 6557 | 6584 | ); |
| 6558 | 6585 | } |
| 6559 | 6586 |
src/Zir.zig+24-4| ... | ... | @@ -2226,9 +2226,13 @@ pub const Inst = struct { |
| 2226 | 2226 | /// 0. lib_name: u32, // null terminated string index, if has_lib_name is set |
| 2227 | 2227 | /// 1. cc: Ref, // if has_cc is set |
| 2228 | 2228 | /// 2. align: Ref, // if has_align is set |
| 2229 | /// 3. param_type: Ref // for each param_types_len | |
| 2230 | /// 4. body: Index // for each body_len | |
| 2231 | /// 5. src_locs: Func.SrcLocs // if body_len != 0 | |
| 2229 | /// 3. comptime_bits: u32 // for every 32 parameters, if has_comptime_bits is set | |
| 2230 | /// - sets of 1 bit: | |
| 2231 | /// 0bX: whether corresponding parameter is comptime | |
| 2232 | /// 4. param_type: Ref // for each param_types_len | |
| 2233 | /// - `none` indicates that the param type is `anytype`. | |
| 2234 | /// 5. body: Index // for each body_len | |
| 2235 | /// 6. src_locs: Func.SrcLocs // if body_len != 0 | |
| 2232 | 2236 | pub const ExtendedFunc = struct { |
| 2233 | 2237 | src_node: i32, |
| 2234 | 2238 | return_type: Ref, |
| ... | ... | @@ -2243,7 +2247,8 @@ pub const Inst = struct { |
| 2243 | 2247 | has_align: bool, |
| 2244 | 2248 | is_test: bool, |
| 2245 | 2249 | is_extern: bool, |
| 2246 | _: u9 = undefined, | |
| 2250 | has_comptime_bits: bool, | |
| 2251 | _: u8 = undefined, | |
| 2247 | 2252 | }; |
| 2248 | 2253 | }; |
| 2249 | 2254 | |
| ... | ... | @@ -4291,6 +4296,7 @@ const Writer = struct { |
| 4291 | 4296 | body, |
| 4292 | 4297 | src, |
| 4293 | 4298 | src_locs, |
| 4299 | &.{}, | |
| 4294 | 4300 | ); |
| 4295 | 4301 | } |
| 4296 | 4302 | |
| ... | ... | @@ -4317,6 +4323,13 @@ const Writer = struct { |
| 4317 | 4323 | break :blk align_inst; |
| 4318 | 4324 | }; |
| 4319 | 4325 | |
| 4326 | const comptime_bits: []const u32 = if (!small.has_comptime_bits) &.{} else blk: { | |
| 4327 | const amt = (extra.data.param_types_len + 31) / 32; | |
| 4328 | const bit_bags = self.code.extra[extra_index..][0..amt]; | |
| 4329 | extra_index += amt; | |
| 4330 | break :blk bit_bags; | |
| 4331 | }; | |
| 4332 | ||
| 4320 | 4333 | const param_types = self.code.refSlice(extra_index, extra.data.param_types_len); |
| 4321 | 4334 | extra_index += param_types.len; |
| 4322 | 4335 | |
| ... | ... | @@ -4339,6 +4352,7 @@ const Writer = struct { |
| 4339 | 4352 | body, |
| 4340 | 4353 | src, |
| 4341 | 4354 | src_locs, |
| 4355 | comptime_bits, | |
| 4342 | 4356 | ); |
| 4343 | 4357 | } |
| 4344 | 4358 | |
| ... | ... | @@ -4422,10 +4436,16 @@ const Writer = struct { |
| 4422 | 4436 | body: []const Inst.Index, |
| 4423 | 4437 | src: LazySrcLoc, |
| 4424 | 4438 | src_locs: Zir.Inst.Func.SrcLocs, |
| 4439 | comptime_bits: []const u32, | |
| 4425 | 4440 | ) !void { |
| 4426 | 4441 | try stream.writeAll("["); |
| 4427 | 4442 | for (param_types) |param_type, i| { |
| 4428 | 4443 | if (i != 0) try stream.writeAll(", "); |
| 4444 | if (comptime_bits.len != 0) { | |
| 4445 | const bag = comptime_bits[i / 32]; | |
| 4446 | const is_comptime = @truncate(u1, bag >> @intCast(u5, i % 32)) != 0; | |
| 4447 | try self.writeFlag(stream, "comptime ", is_comptime); | |
| 4448 | } | |
| 4429 | 4449 | try self.writeInstRef(stream, param_type); |
| 4430 | 4450 | } |
| 4431 | 4451 | try stream.writeAll("], "); |
src/type.zig+15| ... | ... | @@ -764,6 +764,7 @@ pub const Type = extern union { |
| 764 | 764 | .param_types = param_types, |
| 765 | 765 | .cc = payload.cc, |
| 766 | 766 | .is_var_args = payload.is_var_args, |
| 767 | .is_generic = payload.is_generic, | |
| 767 | 768 | }); |
| 768 | 769 | }, |
| 769 | 770 | .pointer => { |
| ... | ... | @@ -2407,6 +2408,19 @@ pub const Type = extern union { |
| 2407 | 2408 | }; |
| 2408 | 2409 | } |
| 2409 | 2410 | |
| 2411 | /// Asserts the type is a function. | |
| 2412 | pub fn fnIsGeneric(self: Type) bool { | |
| 2413 | return switch (self.tag()) { | |
| 2414 | .fn_noreturn_no_args => false, | |
| 2415 | .fn_void_no_args => false, | |
| 2416 | .fn_naked_noreturn_no_args => false, | |
| 2417 | .fn_ccc_void_no_args => false, | |
| 2418 | .function => self.castTag(.function).?.data.is_generic, | |
| 2419 | ||
| 2420 | else => unreachable, | |
| 2421 | }; | |
| 2422 | } | |
| 2423 | ||
| 2410 | 2424 | pub fn isNumeric(self: Type) bool { |
| 2411 | 2425 | return switch (self.tag()) { |
| 2412 | 2426 | .f16, |
| ... | ... | @@ -3214,6 +3228,7 @@ pub const Type = extern union { |
| 3214 | 3228 | return_type: Type, |
| 3215 | 3229 | cc: std.builtin.CallingConvention, |
| 3216 | 3230 | is_var_args: bool, |
| 3231 | is_generic: bool, | |
| 3217 | 3232 | }, |
| 3218 | 3233 | }; |
| 3219 | 3234 |