| author | |
| committer | |
| log | c03a04a58942446b48e9294df991a17a3a6f7b48 |
| tree | 9e306df81d9c1443bee81ccc499d76f620e23923 |
| parent | e9e3a2994696a3131125ebc4b1f0eec7ca5306d9 |
* ZIR encoding for function instructions have a body for the return
type. This lets Sema for generic functions do the same thing it does
for parameters, handling `error.GenericPoison` in the evaluation of
the return type by marking the function as generic.
* Sema: fix missing block around the new Decl arena finalization. This
led to a memory corruption.
* Added some floating point support to the LLVM backend but didn't get
far enough to pass any new tests.9 files changed, 344 insertions(+), 208 deletions(-)
src/AstGen.zig+39-31| ... | ... | @@ -1041,6 +1041,7 @@ fn fnProtoExpr( |
| 1041 | 1041 | fn_proto: ast.full.FnProto, |
| 1042 | 1042 | ) InnerError!Zir.Inst.Ref { |
| 1043 | 1043 | const astgen = gz.astgen; |
| 1044 | const gpa = astgen.gpa; | |
| 1044 | 1045 | const tree = astgen.tree; |
| 1045 | 1046 | const token_tags = tree.tokens.items(.tag); |
| 1046 | 1047 | |
| ... | ... | @@ -1083,7 +1084,6 @@ fn fnProtoExpr( |
| 1083 | 1084 | .param_anytype; |
| 1084 | 1085 | _ = try gz.addStrTok(tag, param_name, name_token); |
| 1085 | 1086 | } else { |
| 1086 | const gpa = astgen.gpa; | |
| 1087 | 1087 | const param_type_node = param.type_expr; |
| 1088 | 1088 | assert(param_type_node != 0); |
| 1089 | 1089 | var param_gz = gz.makeSubBlock(scope); |
| ... | ... | @@ -1113,15 +1113,13 @@ fn fnProtoExpr( |
| 1113 | 1113 | if (is_inferred_error) { |
| 1114 | 1114 | return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{}); |
| 1115 | 1115 | } |
| 1116 | const return_type_inst = try AstGen.expr( | |
| 1117 | gz, | |
| 1118 | scope, | |
| 1119 | .{ .ty = .type_type }, | |
| 1120 | fn_proto.ast.return_type, | |
| 1121 | ); | |
| 1116 | var ret_gz = gz.makeSubBlock(scope); | |
| 1117 | defer ret_gz.instructions.deinit(gpa); | |
| 1118 | const ret_ty = try expr(&ret_gz, scope, coerced_type_rl, fn_proto.ast.return_type); | |
| 1119 | const ret_br = try ret_gz.addBreak(.break_inline, 0, ret_ty); | |
| 1122 | 1120 | |
| 1123 | 1121 | const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0) |
| 1124 | try AstGen.expr( | |
| 1122 | try expr( | |
| 1125 | 1123 | gz, |
| 1126 | 1124 | scope, |
| 1127 | 1125 | .{ .ty = .calling_convention_type }, |
| ... | ... | @@ -1133,7 +1131,8 @@ fn fnProtoExpr( |
| 1133 | 1131 | const result = try gz.addFunc(.{ |
| 1134 | 1132 | .src_node = fn_proto.ast.proto_node, |
| 1135 | 1133 | .param_block = 0, |
| 1136 | .ret_ty = return_type_inst, | |
| 1134 | .ret_ty = ret_gz.instructions.items, | |
| 1135 | .ret_br = ret_br, | |
| 1137 | 1136 | .body = &[0]Zir.Inst.Index{}, |
| 1138 | 1137 | .cc = cc, |
| 1139 | 1138 | .align_inst = align_inst, |
| ... | ... | @@ -3005,12 +3004,10 @@ fn fnDecl( |
| 3005 | 3004 | break :inst try comptimeExpr(&decl_gz, params_scope, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr); |
| 3006 | 3005 | }; |
| 3007 | 3006 | |
| 3008 | const return_type_inst = try AstGen.expr( | |
| 3009 | &decl_gz, | |
| 3010 | params_scope, | |
| 3011 | .{ .ty = .type_type }, | |
| 3012 | fn_proto.ast.return_type, | |
| 3013 | ); | |
| 3007 | var ret_gz = gz.makeSubBlock(params_scope); | |
| 3008 | defer ret_gz.instructions.deinit(gpa); | |
| 3009 | const ret_ty = try expr(&decl_gz, params_scope, coerced_type_rl, fn_proto.ast.return_type); | |
| 3010 | const ret_br = try ret_gz.addBreak(.break_inline, 0, ret_ty); | |
| 3014 | 3011 | |
| 3015 | 3012 | const cc: Zir.Inst.Ref = blk: { |
| 3016 | 3013 | if (fn_proto.ast.callconv_expr != 0) { |
| ... | ... | @@ -3021,7 +3018,7 @@ fn fnDecl( |
| 3021 | 3018 | .{}, |
| 3022 | 3019 | ); |
| 3023 | 3020 | } |
| 3024 | break :blk try AstGen.expr( | |
| 3021 | break :blk try expr( | |
| 3025 | 3022 | &decl_gz, |
| 3026 | 3023 | params_scope, |
| 3027 | 3024 | .{ .ty = .calling_convention_type }, |
| ... | ... | @@ -3046,7 +3043,8 @@ fn fnDecl( |
| 3046 | 3043 | } |
| 3047 | 3044 | break :func try decl_gz.addFunc(.{ |
| 3048 | 3045 | .src_node = decl_node, |
| 3049 | .ret_ty = return_type_inst, | |
| 3046 | .ret_ty = ret_gz.instructions.items, | |
| 3047 | .ret_br = ret_br, | |
| 3050 | 3048 | .param_block = block_inst, |
| 3051 | 3049 | .body = &[0]Zir.Inst.Index{}, |
| 3052 | 3050 | .cc = cc, |
| ... | ... | @@ -3085,7 +3083,8 @@ fn fnDecl( |
| 3085 | 3083 | break :func try decl_gz.addFunc(.{ |
| 3086 | 3084 | .src_node = decl_node, |
| 3087 | 3085 | .param_block = block_inst, |
| 3088 | .ret_ty = return_type_inst, | |
| 3086 | .ret_ty = ret_gz.instructions.items, | |
| 3087 | .ret_br = ret_br, | |
| 3089 | 3088 | .body = fn_gz.instructions.items, |
| 3090 | 3089 | .cc = cc, |
| 3091 | 3090 | .align_inst = .none, // passed in the per-decl data |
| ... | ... | @@ -3430,7 +3429,8 @@ fn testDecl( |
| 3430 | 3429 | const func_inst = try decl_block.addFunc(.{ |
| 3431 | 3430 | .src_node = node, |
| 3432 | 3431 | .param_block = block_inst, |
| 3433 | .ret_ty = .void_type, | |
| 3432 | .ret_ty = &.{}, | |
| 3433 | .ret_br = 0, | |
| 3434 | 3434 | .body = fn_block.instructions.items, |
| 3435 | 3435 | .cc = .none, |
| 3436 | 3436 | .align_inst = .none, |
| ... | ... | @@ -9127,7 +9127,8 @@ const GenZir = struct { |
| 9127 | 9127 | src_node: ast.Node.Index, |
| 9128 | 9128 | body: []const Zir.Inst.Index, |
| 9129 | 9129 | param_block: Zir.Inst.Index, |
| 9130 | ret_ty: Zir.Inst.Ref, | |
| 9130 | ret_ty: []const Zir.Inst.Index, | |
| 9131 | ret_br: Zir.Inst.Index, | |
| 9131 | 9132 | cc: Zir.Inst.Ref, |
| 9132 | 9133 | align_inst: Zir.Inst.Ref, |
| 9133 | 9134 | lib_name: u32, |
| ... | ... | @@ -9137,7 +9138,6 @@ const GenZir = struct { |
| 9137 | 9138 | is_extern: bool, |
| 9138 | 9139 | }) !Zir.Inst.Ref { |
| 9139 | 9140 | assert(args.src_node != 0); |
| 9140 | assert(args.ret_ty != .none); | |
| 9141 | 9141 | const astgen = gz.astgen; |
| 9142 | 9142 | const gpa = astgen.gpa; |
| 9143 | 9143 | |
| ... | ... | @@ -9179,7 +9179,7 @@ const GenZir = struct { |
| 9179 | 9179 | try astgen.extra.ensureUnusedCapacity( |
| 9180 | 9180 | gpa, |
| 9181 | 9181 | @typeInfo(Zir.Inst.ExtendedFunc).Struct.fields.len + |
| 9182 | args.body.len + src_locs.len + | |
| 9182 | args.ret_ty.len + args.body.len + src_locs.len + | |
| 9183 | 9183 | @boolToInt(args.lib_name != 0) + |
| 9184 | 9184 | @boolToInt(args.align_inst != .none) + |
| 9185 | 9185 | @boolToInt(args.cc != .none), |
| ... | ... | @@ -9187,7 +9187,7 @@ const GenZir = struct { |
| 9187 | 9187 | const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedFunc{ |
| 9188 | 9188 | .src_node = gz.nodeIndexToRelative(args.src_node), |
| 9189 | 9189 | .param_block = args.param_block, |
| 9190 | .return_type = args.ret_ty, | |
| 9190 | .ret_body_len = @intCast(u32, args.ret_ty.len), | |
| 9191 | 9191 | .body_len = @intCast(u32, args.body.len), |
| 9192 | 9192 | }); |
| 9193 | 9193 | if (args.lib_name != 0) { |
| ... | ... | @@ -9199,10 +9199,14 @@ const GenZir = struct { |
| 9199 | 9199 | if (args.align_inst != .none) { |
| 9200 | 9200 | astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst)); |
| 9201 | 9201 | } |
| 9202 | astgen.extra.appendSliceAssumeCapacity(args.ret_ty); | |
| 9202 | 9203 | astgen.extra.appendSliceAssumeCapacity(args.body); |
| 9203 | 9204 | astgen.extra.appendSliceAssumeCapacity(src_locs); |
| 9204 | 9205 | |
| 9205 | 9206 | const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len); |
| 9207 | if (args.ret_br != 0) { | |
| 9208 | astgen.instructions.items(.data)[args.ret_br].@"break".block_inst = new_index; | |
| 9209 | } | |
| 9206 | 9210 | astgen.instructions.appendAssumeCapacity(.{ |
| 9207 | 9211 | .tag = .extended, |
| 9208 | 9212 | .data = .{ .extended = .{ |
| ... | ... | @@ -9222,23 +9226,27 @@ const GenZir = struct { |
| 9222 | 9226 | gz.instructions.appendAssumeCapacity(new_index); |
| 9223 | 9227 | return indexToRef(new_index); |
| 9224 | 9228 | } else { |
| 9225 | try gz.astgen.extra.ensureUnusedCapacity( | |
| 9229 | try astgen.extra.ensureUnusedCapacity( | |
| 9226 | 9230 | gpa, |
| 9227 | 9231 | @typeInfo(Zir.Inst.Func).Struct.fields.len + |
| 9228 | args.body.len + src_locs.len, | |
| 9232 | args.ret_ty.len + args.body.len + src_locs.len, | |
| 9229 | 9233 | ); |
| 9230 | 9234 | |
| 9231 | const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Func{ | |
| 9235 | const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{ | |
| 9232 | 9236 | .param_block = args.param_block, |
| 9233 | .return_type = args.ret_ty, | |
| 9237 | .ret_body_len = @intCast(u32, args.ret_ty.len), | |
| 9234 | 9238 | .body_len = @intCast(u32, args.body.len), |
| 9235 | 9239 | }); |
| 9236 | gz.astgen.extra.appendSliceAssumeCapacity(args.body); | |
| 9237 | gz.astgen.extra.appendSliceAssumeCapacity(src_locs); | |
| 9240 | astgen.extra.appendSliceAssumeCapacity(args.ret_ty); | |
| 9241 | astgen.extra.appendSliceAssumeCapacity(args.body); | |
| 9242 | astgen.extra.appendSliceAssumeCapacity(src_locs); | |
| 9238 | 9243 | |
| 9239 | 9244 | const tag: Zir.Inst.Tag = if (args.is_inferred_error) .func_inferred else .func; |
| 9240 | const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); | |
| 9241 | gz.astgen.instructions.appendAssumeCapacity(.{ | |
| 9245 | const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len); | |
| 9246 | if (args.ret_br != 0) { | |
| 9247 | astgen.instructions.items(.data)[args.ret_br].@"break".block_inst = new_index; | |
| 9248 | } | |
| 9249 | astgen.instructions.appendAssumeCapacity(.{ | |
| 9242 | 9250 | .tag = tag, |
| 9243 | 9251 | .data = .{ .pl_node = .{ |
| 9244 | 9252 | .src_node = gz.nodeIndexToRelative(args.src_node), |
src/Module.zig+3| ... | ... | @@ -842,6 +842,9 @@ pub const Fn = struct { |
| 842 | 842 | |
| 843 | 843 | pub fn getInferredErrorSet(func: *Fn) ?*std.StringHashMapUnmanaged(void) { |
| 844 | 844 | const ret_ty = func.owner_decl.ty.fnReturnType(); |
| 845 | if (ret_ty.tag() == .generic_poison) { | |
| 846 | return null; | |
| 847 | } | |
| 845 | 848 | if (ret_ty.zigTypeTag() == .ErrorUnion) { |
| 846 | 849 | if (ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| { |
| 847 | 850 | return &payload.data.map; |
src/Sema.zig+162-122| ... | ... | @@ -2618,128 +2618,130 @@ fn analyzeCall( |
| 2618 | 2618 | break :new_func gop.key_ptr.*; |
| 2619 | 2619 | }; |
| 2620 | 2620 | |
| 2621 | try namespace.anon_decls.ensureUnusedCapacity(gpa, 1); | |
| 2622 | ||
| 2623 | // Create a Decl for the new function. | |
| 2624 | const new_decl = try mod.allocateNewDecl(namespace, module_fn.owner_decl.src_node); | |
| 2625 | // TODO better names for generic function instantiations | |
| 2626 | const name_index = mod.getNextAnonNameIndex(); | |
| 2627 | new_decl.name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{ | |
| 2628 | module_fn.owner_decl.name, name_index, | |
| 2629 | }); | |
| 2630 | new_decl.src_line = module_fn.owner_decl.src_line; | |
| 2631 | new_decl.is_pub = module_fn.owner_decl.is_pub; | |
| 2632 | new_decl.is_exported = module_fn.owner_decl.is_exported; | |
| 2633 | new_decl.has_align = module_fn.owner_decl.has_align; | |
| 2634 | new_decl.has_linksection = module_fn.owner_decl.has_linksection; | |
| 2635 | new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index; | |
| 2636 | new_decl.alive = true; // This Decl is called at runtime. | |
| 2637 | new_decl.has_tv = true; | |
| 2638 | new_decl.owns_tv = true; | |
| 2639 | new_decl.analysis = .in_progress; | |
| 2640 | new_decl.generation = mod.generation; | |
| 2641 | ||
| 2642 | namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {}); | |
| 2643 | ||
| 2644 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | |
| 2645 | errdefer new_decl_arena.deinit(); | |
| 2646 | ||
| 2647 | // Re-run the block that creates the function, with the comptime parameters | |
| 2648 | // pre-populated inside `inst_map`. This causes `param_comptime` and | |
| 2649 | // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a | |
| 2650 | // new, monomorphized function, with the comptime parameters elided. | |
| 2651 | var child_sema: Sema = .{ | |
| 2652 | .mod = mod, | |
| 2653 | .gpa = gpa, | |
| 2654 | .arena = sema.arena, | |
| 2655 | .code = fn_zir, | |
| 2656 | .owner_decl = new_decl, | |
| 2657 | .namespace = namespace, | |
| 2658 | .func = null, | |
| 2659 | .owner_func = null, | |
| 2660 | .comptime_args = try new_decl_arena.allocator.alloc(TypedValue, uncasted_args.len), | |
| 2661 | .comptime_args_fn_inst = module_fn.zir_body_inst, | |
| 2662 | .preallocated_new_func = new_module_func, | |
| 2663 | }; | |
| 2664 | defer child_sema.deinit(); | |
| 2665 | ||
| 2666 | var child_block: Scope.Block = .{ | |
| 2667 | .parent = null, | |
| 2668 | .sema = &child_sema, | |
| 2669 | .src_decl = new_decl, | |
| 2670 | .instructions = .{}, | |
| 2671 | .inlining = null, | |
| 2672 | .is_comptime = true, | |
| 2673 | }; | |
| 2674 | defer { | |
| 2675 | child_block.instructions.deinit(gpa); | |
| 2676 | child_block.params.deinit(gpa); | |
| 2677 | } | |
| 2678 | ||
| 2679 | try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, uncasted_args.len)); | |
| 2680 | var arg_i: usize = 0; | |
| 2681 | for (fn_info.param_body) |inst| { | |
| 2682 | const is_comptime = switch (zir_tags[inst]) { | |
| 2683 | .param_comptime, .param_anytype_comptime => true, | |
| 2684 | .param, .param_anytype => false, | |
| 2685 | else => continue, | |
| 2686 | } or func_ty_info.paramIsComptime(arg_i); | |
| 2687 | const arg_src = call_src; // TODO: better source location | |
| 2688 | const arg = uncasted_args[arg_i]; | |
| 2689 | if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| { | |
| 2690 | const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val); | |
| 2691 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); | |
| 2692 | } else if (is_comptime) { | |
| 2693 | return sema.failWithNeededComptime(block, arg_src); | |
| 2621 | { | |
| 2622 | try namespace.anon_decls.ensureUnusedCapacity(gpa, 1); | |
| 2623 | ||
| 2624 | // Create a Decl for the new function. | |
| 2625 | const new_decl = try mod.allocateNewDecl(namespace, module_fn.owner_decl.src_node); | |
| 2626 | // TODO better names for generic function instantiations | |
| 2627 | const name_index = mod.getNextAnonNameIndex(); | |
| 2628 | new_decl.name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{ | |
| 2629 | module_fn.owner_decl.name, name_index, | |
| 2630 | }); | |
| 2631 | new_decl.src_line = module_fn.owner_decl.src_line; | |
| 2632 | new_decl.is_pub = module_fn.owner_decl.is_pub; | |
| 2633 | new_decl.is_exported = module_fn.owner_decl.is_exported; | |
| 2634 | new_decl.has_align = module_fn.owner_decl.has_align; | |
| 2635 | new_decl.has_linksection = module_fn.owner_decl.has_linksection; | |
| 2636 | new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index; | |
| 2637 | new_decl.alive = true; // This Decl is called at runtime. | |
| 2638 | new_decl.has_tv = true; | |
| 2639 | new_decl.owns_tv = true; | |
| 2640 | new_decl.analysis = .in_progress; | |
| 2641 | new_decl.generation = mod.generation; | |
| 2642 | ||
| 2643 | namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {}); | |
| 2644 | ||
| 2645 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | |
| 2646 | errdefer new_decl_arena.deinit(); | |
| 2647 | ||
| 2648 | // Re-run the block that creates the function, with the comptime parameters | |
| 2649 | // pre-populated inside `inst_map`. This causes `param_comptime` and | |
| 2650 | // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a | |
| 2651 | // new, monomorphized function, with the comptime parameters elided. | |
| 2652 | var child_sema: Sema = .{ | |
| 2653 | .mod = mod, | |
| 2654 | .gpa = gpa, | |
| 2655 | .arena = sema.arena, | |
| 2656 | .code = fn_zir, | |
| 2657 | .owner_decl = new_decl, | |
| 2658 | .namespace = namespace, | |
| 2659 | .func = null, | |
| 2660 | .owner_func = null, | |
| 2661 | .comptime_args = try new_decl_arena.allocator.alloc(TypedValue, uncasted_args.len), | |
| 2662 | .comptime_args_fn_inst = module_fn.zir_body_inst, | |
| 2663 | .preallocated_new_func = new_module_func, | |
| 2664 | }; | |
| 2665 | defer child_sema.deinit(); | |
| 2666 | ||
| 2667 | var child_block: Scope.Block = .{ | |
| 2668 | .parent = null, | |
| 2669 | .sema = &child_sema, | |
| 2670 | .src_decl = new_decl, | |
| 2671 | .instructions = .{}, | |
| 2672 | .inlining = null, | |
| 2673 | .is_comptime = true, | |
| 2674 | }; | |
| 2675 | defer { | |
| 2676 | child_block.instructions.deinit(gpa); | |
| 2677 | child_block.params.deinit(gpa); | |
| 2694 | 2678 | } |
| 2695 | arg_i += 1; | |
| 2696 | } | |
| 2697 | const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body); | |
| 2698 | const new_func_val = try child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst); | |
| 2699 | const new_func = new_func_val.castTag(.function).?.data; | |
| 2700 | assert(new_func == new_module_func); | |
| 2701 | 2679 | |
| 2702 | arg_i = 0; | |
| 2703 | for (fn_info.param_body) |inst| { | |
| 2704 | switch (zir_tags[inst]) { | |
| 2705 | .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, | |
| 2706 | else => continue, | |
| 2680 | try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, uncasted_args.len)); | |
| 2681 | var arg_i: usize = 0; | |
| 2682 | for (fn_info.param_body) |inst| { | |
| 2683 | const is_comptime = switch (zir_tags[inst]) { | |
| 2684 | .param_comptime, .param_anytype_comptime => true, | |
| 2685 | .param, .param_anytype => false, | |
| 2686 | else => continue, | |
| 2687 | } or func_ty_info.paramIsComptime(arg_i); | |
| 2688 | const arg_src = call_src; // TODO: better source location | |
| 2689 | const arg = uncasted_args[arg_i]; | |
| 2690 | if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| { | |
| 2691 | const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val); | |
| 2692 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); | |
| 2693 | } else if (is_comptime) { | |
| 2694 | return sema.failWithNeededComptime(block, arg_src); | |
| 2695 | } | |
| 2696 | arg_i += 1; | |
| 2707 | 2697 | } |
| 2708 | const arg = child_sema.inst_map.get(inst).?; | |
| 2709 | const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(&child_block, .unneeded, arg) catch unreachable).?; | |
| 2698 | const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body); | |
| 2699 | const new_func_val = try child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst); | |
| 2700 | const new_func = new_func_val.castTag(.function).?.data; | |
| 2701 | assert(new_func == new_module_func); | |
| 2702 | ||
| 2703 | arg_i = 0; | |
| 2704 | for (fn_info.param_body) |inst| { | |
| 2705 | switch (zir_tags[inst]) { | |
| 2706 | .param_comptime, .param_anytype_comptime, .param, .param_anytype => {}, | |
| 2707 | else => continue, | |
| 2708 | } | |
| 2709 | const arg = child_sema.inst_map.get(inst).?; | |
| 2710 | const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(&child_block, .unneeded, arg) catch unreachable).?; | |
| 2710 | 2711 | |
| 2711 | if (arg_val.tag() == .generic_poison) { | |
| 2712 | child_sema.comptime_args[arg_i] = .{ | |
| 2713 | .ty = Type.initTag(.noreturn), | |
| 2714 | .val = Value.initTag(.unreachable_value), | |
| 2715 | }; | |
| 2716 | } else { | |
| 2717 | child_sema.comptime_args[arg_i] = .{ | |
| 2718 | .ty = try child_sema.typeOf(arg).copy(&new_decl_arena.allocator), | |
| 2719 | .val = try arg_val.copy(&new_decl_arena.allocator), | |
| 2720 | }; | |
| 2712 | if (arg_val.tag() == .generic_poison) { | |
| 2713 | child_sema.comptime_args[arg_i] = .{ | |
| 2714 | .ty = Type.initTag(.noreturn), | |
| 2715 | .val = Value.initTag(.unreachable_value), | |
| 2716 | }; | |
| 2717 | } else { | |
| 2718 | child_sema.comptime_args[arg_i] = .{ | |
| 2719 | .ty = try child_sema.typeOf(arg).copy(&new_decl_arena.allocator), | |
| 2720 | .val = try arg_val.copy(&new_decl_arena.allocator), | |
| 2721 | }; | |
| 2722 | } | |
| 2723 | ||
| 2724 | arg_i += 1; | |
| 2721 | 2725 | } |
| 2722 | 2726 | |
| 2723 | arg_i += 1; | |
| 2724 | } | |
| 2727 | // Populate the Decl ty/val with the function and its type. | |
| 2728 | new_decl.ty = try child_sema.typeOf(new_func_inst).copy(&new_decl_arena.allocator); | |
| 2729 | new_decl.val = try Value.Tag.function.create(&new_decl_arena.allocator, new_func); | |
| 2730 | new_decl.analysis = .complete; | |
| 2725 | 2731 | |
| 2726 | // Populate the Decl ty/val with the function and its type. | |
| 2727 | new_decl.ty = try child_sema.typeOf(new_func_inst).copy(&new_decl_arena.allocator); | |
| 2728 | new_decl.val = try Value.Tag.function.create(&new_decl_arena.allocator, new_func); | |
| 2729 | new_decl.analysis = .complete; | |
| 2732 | // The generic function Decl is guaranteed to be the first dependency | |
| 2733 | // of each of its instantiations. | |
| 2734 | assert(new_decl.dependencies.keys().len == 0); | |
| 2735 | try mod.declareDeclDependency(new_decl, module_fn.owner_decl); | |
| 2730 | 2736 | |
| 2731 | // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field | |
| 2732 | // will be populated, ensuring it will have `analyzeBody` called with the ZIR | |
| 2733 | // parameters mapped appropriately. | |
| 2734 | try mod.comp.bin_file.allocateDeclIndexes(new_decl); | |
| 2735 | try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func }); | |
| 2737 | // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field | |
| 2738 | // will be populated, ensuring it will have `analyzeBody` called with the ZIR | |
| 2739 | // parameters mapped appropriately. | |
| 2740 | try mod.comp.bin_file.allocateDeclIndexes(new_decl); | |
| 2741 | try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func }); | |
| 2736 | 2742 | |
| 2737 | try new_decl.finalizeNewArena(&new_decl_arena); | |
| 2738 | ||
| 2739 | // The generic function Decl is guaranteed to be the first dependency | |
| 2740 | // of each of its instantiations. | |
| 2741 | assert(new_decl.dependencies.keys().len == 0); | |
| 2742 | try mod.declareDeclDependency(new_decl, module_fn.owner_decl); | |
| 2743 | try new_decl.finalizeNewArena(&new_decl_arena); | |
| 2744 | } | |
| 2743 | 2745 | |
| 2744 | 2746 | break :res try sema.finishGenericCall( |
| 2745 | 2747 | block, |
| ... | ... | @@ -3478,12 +3480,15 @@ fn zirFunc( |
| 3478 | 3480 | |
| 3479 | 3481 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 3480 | 3482 | const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index); |
| 3483 | var extra_index = extra.end; | |
| 3484 | const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 3485 | extra_index += ret_ty_body.len; | |
| 3481 | 3486 | |
| 3482 | 3487 | var body_inst: Zir.Inst.Index = 0; |
| 3483 | 3488 | var src_locs: Zir.Inst.Func.SrcLocs = undefined; |
| 3484 | 3489 | if (extra.data.body_len != 0) { |
| 3485 | 3490 | body_inst = inst; |
| 3486 | const extra_index = extra.end + extra.data.body_len; | |
| 3491 | extra_index += extra.data.body_len; | |
| 3487 | 3492 | src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; |
| 3488 | 3493 | } |
| 3489 | 3494 | |
| ... | ... | @@ -3496,7 +3501,7 @@ fn zirFunc( |
| 3496 | 3501 | block, |
| 3497 | 3502 | inst_data.src_node, |
| 3498 | 3503 | body_inst, |
| 3499 | extra.data.return_type, | |
| 3504 | ret_ty_body, | |
| 3500 | 3505 | cc, |
| 3501 | 3506 | Value.initTag(.null_value), |
| 3502 | 3507 | false, |
| ... | ... | @@ -3512,7 +3517,7 @@ fn funcCommon( |
| 3512 | 3517 | block: *Scope.Block, |
| 3513 | 3518 | src_node_offset: i32, |
| 3514 | 3519 | body_inst: Zir.Inst.Index, |
| 3515 | zir_return_type: Zir.Inst.Ref, | |
| 3520 | ret_ty_body: []const Zir.Inst.Index, | |
| 3516 | 3521 | cc: std.builtin.CallingConvention, |
| 3517 | 3522 | align_val: Value, |
| 3518 | 3523 | var_args: bool, |
| ... | ... | @@ -3523,7 +3528,37 @@ fn funcCommon( |
| 3523 | 3528 | ) CompileError!Air.Inst.Ref { |
| 3524 | 3529 | const src: LazySrcLoc = .{ .node_offset = src_node_offset }; |
| 3525 | 3530 | const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; |
| 3526 | const bare_return_type = try sema.resolveType(block, ret_ty_src, zir_return_type); | |
| 3531 | ||
| 3532 | // The return type body might be a type expression that depends on generic parameters. | |
| 3533 | // In such case we need to use a generic_poison value for the return type and mark | |
| 3534 | // the function as generic. | |
| 3535 | var is_generic = false; | |
| 3536 | const bare_return_type: Type = ret_ty: { | |
| 3537 | if (ret_ty_body.len == 0) break :ret_ty Type.initTag(.void); | |
| 3538 | ||
| 3539 | const err = err: { | |
| 3540 | // Make sure any nested param instructions don't clobber our work. | |
| 3541 | const prev_params = block.params; | |
| 3542 | block.params = .{}; | |
| 3543 | defer { | |
| 3544 | block.params.deinit(sema.gpa); | |
| 3545 | block.params = prev_params; | |
| 3546 | } | |
| 3547 | if (sema.resolveBody(block, ret_ty_body)) |ret_ty_inst| { | |
| 3548 | if (sema.analyzeAsType(block, ret_ty_src, ret_ty_inst)) |ret_ty| { | |
| 3549 | break :ret_ty ret_ty; | |
| 3550 | } else |err| break :err err; | |
| 3551 | } else |err| break :err err; | |
| 3552 | }; | |
| 3553 | switch (err) { | |
| 3554 | error.GenericPoison => { | |
| 3555 | // The type is not available until the generic instantiation. | |
| 3556 | is_generic = true; | |
| 3557 | break :ret_ty Type.initTag(.generic_poison); | |
| 3558 | }, | |
| 3559 | else => |e| return e, | |
| 3560 | } | |
| 3561 | }; | |
| 3527 | 3562 | |
| 3528 | 3563 | const mod = sema.mod; |
| 3529 | 3564 | |
| ... | ... | @@ -3540,8 +3575,9 @@ fn funcCommon( |
| 3540 | 3575 | |
| 3541 | 3576 | const fn_ty: Type = fn_ty: { |
| 3542 | 3577 | // Hot path for some common function types. |
| 3543 | if (block.params.items.len == 0 and !var_args and align_val.tag() == .null_value and | |
| 3544 | !inferred_error_set) | |
| 3578 | // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated. | |
| 3579 | if (!is_generic and block.params.items.len == 0 and !var_args and | |
| 3580 | align_val.tag() == .null_value and !inferred_error_set) | |
| 3545 | 3581 | { |
| 3546 | 3582 | if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { |
| 3547 | 3583 | break :fn_ty Type.initTag(.fn_noreturn_no_args); |
| ... | ... | @@ -3560,7 +3596,6 @@ fn funcCommon( |
| 3560 | 3596 | } |
| 3561 | 3597 | } |
| 3562 | 3598 | |
| 3563 | var is_generic = false; | |
| 3564 | 3599 | const param_types = try sema.arena.alloc(Type, block.params.items.len); |
| 3565 | 3600 | const comptime_params = try sema.arena.alloc(bool, block.params.items.len); |
| 3566 | 3601 | for (block.params.items) |param, i| { |
| ... | ... | @@ -3574,7 +3609,9 @@ fn funcCommon( |
| 3574 | 3609 | return mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{}); |
| 3575 | 3610 | } |
| 3576 | 3611 | |
| 3577 | const return_type = if (!inferred_error_set) bare_return_type else blk: { | |
| 3612 | const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison) | |
| 3613 | bare_return_type | |
| 3614 | else blk: { | |
| 3578 | 3615 | const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, .{ |
| 3579 | 3616 | .func = new_func, |
| 3580 | 3617 | .map = .{}, |
| ... | ... | @@ -6944,6 +6981,9 @@ fn zirFuncExtended( |
| 6944 | 6981 | break :blk align_tv.val; |
| 6945 | 6982 | } else Value.initTag(.null_value); |
| 6946 | 6983 | |
| 6984 | const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 6985 | extra_index += ret_ty_body.len; | |
| 6986 | ||
| 6947 | 6987 | var body_inst: Zir.Inst.Index = 0; |
| 6948 | 6988 | var src_locs: Zir.Inst.Func.SrcLocs = undefined; |
| 6949 | 6989 | if (extra.data.body_len != 0) { |
| ... | ... | @@ -6960,7 +7000,7 @@ fn zirFuncExtended( |
| 6960 | 7000 | block, |
| 6961 | 7001 | extra.data.src_node, |
| 6962 | 7002 | body_inst, |
| 6963 | extra.data.return_type, | |
| 7003 | ret_ty_body, | |
| 6964 | 7004 | cc, |
| 6965 | 7005 | align_val, |
| 6966 | 7006 | is_var_args, |
src/Zir.zig+49-15| ... | ... | @@ -2272,11 +2272,13 @@ pub const Inst = struct { |
| 2272 | 2272 | /// 0. lib_name: u32, // null terminated string index, if has_lib_name is set |
| 2273 | 2273 | /// 1. cc: Ref, // if has_cc is set |
| 2274 | 2274 | /// 2. align: Ref, // if has_align is set |
| 2275 | /// 3. body: Index // for each body_len | |
| 2276 | /// 4. src_locs: Func.SrcLocs // if body_len != 0 | |
| 2275 | /// 3. return_type: Index // for each ret_body_len | |
| 2276 | /// 4. body: Index // for each body_len | |
| 2277 | /// 5. src_locs: Func.SrcLocs // if body_len != 0 | |
| 2277 | 2278 | pub const ExtendedFunc = struct { |
| 2278 | 2279 | src_node: i32, |
| 2279 | return_type: Ref, | |
| 2280 | /// If this is 0 it means a void return type. | |
| 2281 | ret_body_len: u32, | |
| 2280 | 2282 | /// Points to the block that contains the param instructions for this function. |
| 2281 | 2283 | param_block: Index, |
| 2282 | 2284 | body_len: u32, |
| ... | ... | @@ -2312,10 +2314,12 @@ pub const Inst = struct { |
| 2312 | 2314 | }; |
| 2313 | 2315 | |
| 2314 | 2316 | /// Trailing: |
| 2315 | /// 0. body: Index // for each body_len | |
| 2316 | /// 1. src_locs: SrcLocs // if body_len != 0 | |
| 2317 | /// 0. return_type: Index // for each ret_body_len | |
| 2318 | /// 1. body: Index // for each body_len | |
| 2319 | /// 2. src_locs: SrcLocs // if body_len != 0 | |
| 2317 | 2320 | pub const Func = struct { |
| 2318 | return_type: Ref, | |
| 2321 | /// If this is 0 it means a void return type. | |
| 2322 | ret_body_len: u32, | |
| 2319 | 2323 | /// Points to the block that contains the param instructions for this function. |
| 2320 | 2324 | param_block: Index, |
| 2321 | 2325 | body_len: u32, |
| ... | ... | @@ -4344,15 +4348,21 @@ const Writer = struct { |
| 4344 | 4348 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 4345 | 4349 | const src = inst_data.src(); |
| 4346 | 4350 | const extra = self.code.extraData(Inst.Func, inst_data.payload_index); |
| 4347 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; | |
| 4351 | var extra_index = extra.end; | |
| 4352 | ||
| 4353 | const ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 4354 | extra_index += ret_ty_body.len; | |
| 4355 | ||
| 4356 | const body = self.code.extra[extra_index..][0..extra.data.body_len]; | |
| 4357 | extra_index += body.len; | |
| 4358 | ||
| 4348 | 4359 | var src_locs: Zir.Inst.Func.SrcLocs = undefined; |
| 4349 | 4360 | if (body.len != 0) { |
| 4350 | const extra_index = extra.end + body.len; | |
| 4351 | 4361 | src_locs = self.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; |
| 4352 | 4362 | } |
| 4353 | 4363 | return self.writeFuncCommon( |
| 4354 | 4364 | stream, |
| 4355 | extra.data.return_type, | |
| 4365 | ret_ty_body, | |
| 4356 | 4366 | inferred_error_set, |
| 4357 | 4367 | false, |
| 4358 | 4368 | false, |
| ... | ... | @@ -4387,6 +4397,9 @@ const Writer = struct { |
| 4387 | 4397 | break :blk align_inst; |
| 4388 | 4398 | }; |
| 4389 | 4399 | |
| 4400 | const ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 4401 | extra_index += ret_ty_body.len; | |
| 4402 | ||
| 4390 | 4403 | const body = self.code.extra[extra_index..][0..extra.data.body_len]; |
| 4391 | 4404 | extra_index += body.len; |
| 4392 | 4405 | |
| ... | ... | @@ -4396,7 +4409,7 @@ const Writer = struct { |
| 4396 | 4409 | } |
| 4397 | 4410 | return self.writeFuncCommon( |
| 4398 | 4411 | stream, |
| 4399 | extra.data.return_type, | |
| 4412 | ret_ty_body, | |
| 4400 | 4413 | small.is_inferred_error, |
| 4401 | 4414 | small.is_var_args, |
| 4402 | 4415 | small.is_extern, |
| ... | ... | @@ -4478,7 +4491,7 @@ const Writer = struct { |
| 4478 | 4491 | fn writeFuncCommon( |
| 4479 | 4492 | self: *Writer, |
| 4480 | 4493 | stream: anytype, |
| 4481 | ret_ty: Inst.Ref, | |
| 4494 | ret_ty_body: []const Inst.Index, | |
| 4482 | 4495 | inferred_error_set: bool, |
| 4483 | 4496 | var_args: bool, |
| 4484 | 4497 | is_extern: bool, |
| ... | ... | @@ -4488,7 +4501,13 @@ const Writer = struct { |
| 4488 | 4501 | src: LazySrcLoc, |
| 4489 | 4502 | src_locs: Zir.Inst.Func.SrcLocs, |
| 4490 | 4503 | ) !void { |
| 4491 | try self.writeInstRef(stream, ret_ty); | |
| 4504 | try stream.writeAll("ret_ty={\n"); | |
| 4505 | self.indent += 2; | |
| 4506 | try self.writeBody(stream, ret_ty_body); | |
| 4507 | self.indent -= 2; | |
| 4508 | try stream.writeByteNTimes(' ', self.indent); | |
| 4509 | try stream.writeAll("}"); | |
| 4510 | ||
| 4492 | 4511 | try self.writeOptionalInstRef(stream, ", cc=", cc); |
| 4493 | 4512 | try self.writeOptionalInstRef(stream, ", align=", align_inst); |
| 4494 | 4513 | try self.writeFlag(stream, ", vargs", var_args); |
| ... | ... | @@ -4496,9 +4515,9 @@ const Writer = struct { |
| 4496 | 4515 | try self.writeFlag(stream, ", inferror", inferred_error_set); |
| 4497 | 4516 | |
| 4498 | 4517 | if (body.len == 0) { |
| 4499 | try stream.writeAll(", {}) "); | |
| 4518 | try stream.writeAll(", body={}) "); | |
| 4500 | 4519 | } else { |
| 4501 | try stream.writeAll(", {\n"); | |
| 4520 | try stream.writeAll(", body={\n"); | |
| 4502 | 4521 | self.indent += 2; |
| 4503 | 4522 | try self.writeBody(stream, body); |
| 4504 | 4523 | self.indent -= 2; |
| ... | ... | @@ -4932,6 +4951,7 @@ fn findDeclsBody( |
| 4932 | 4951 | |
| 4933 | 4952 | pub const FnInfo = struct { |
| 4934 | 4953 | param_body: []const Inst.Index, |
| 4954 | ret_ty_body: []const Inst.Index, | |
| 4935 | 4955 | body: []const Inst.Index, |
| 4936 | 4956 | total_params_len: u32, |
| 4937 | 4957 | }; |
| ... | ... | @@ -4942,13 +4962,22 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { |
| 4942 | 4962 | const info: struct { |
| 4943 | 4963 | param_block: Inst.Index, |
| 4944 | 4964 | body: []const Inst.Index, |
| 4965 | ret_ty_body: []const Inst.Index, | |
| 4945 | 4966 | } = switch (tags[fn_inst]) { |
| 4946 | 4967 | .func, .func_inferred => blk: { |
| 4947 | 4968 | const inst_data = datas[fn_inst].pl_node; |
| 4948 | 4969 | const extra = zir.extraData(Inst.Func, inst_data.payload_index); |
| 4949 | const body = zir.extra[extra.end..][0..extra.data.body_len]; | |
| 4970 | var extra_index: usize = extra.end; | |
| 4971 | ||
| 4972 | const ret_ty_body = zir.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 4973 | extra_index += ret_ty_body.len; | |
| 4974 | ||
| 4975 | const body = zir.extra[extra_index..][0..extra.data.body_len]; | |
| 4976 | extra_index += body.len; | |
| 4977 | ||
| 4950 | 4978 | break :blk .{ |
| 4951 | 4979 | .param_block = extra.data.param_block, |
| 4980 | .ret_ty_body = ret_ty_body, | |
| 4952 | 4981 | .body = body, |
| 4953 | 4982 | }; |
| 4954 | 4983 | }, |
| ... | ... | @@ -4961,9 +4990,13 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { |
| 4961 | 4990 | extra_index += @boolToInt(small.has_lib_name); |
| 4962 | 4991 | extra_index += @boolToInt(small.has_cc); |
| 4963 | 4992 | extra_index += @boolToInt(small.has_align); |
| 4993 | const ret_ty_body = zir.extra[extra_index..][0..extra.data.ret_body_len]; | |
| 4994 | extra_index += ret_ty_body.len; | |
| 4964 | 4995 | const body = zir.extra[extra_index..][0..extra.data.body_len]; |
| 4996 | extra_index += body.len; | |
| 4965 | 4997 | break :blk .{ |
| 4966 | 4998 | .param_block = extra.data.param_block, |
| 4999 | .ret_ty_body = ret_ty_body, | |
| 4967 | 5000 | .body = body, |
| 4968 | 5001 | }; |
| 4969 | 5002 | }, |
| ... | ... | @@ -4983,6 +5016,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { |
| 4983 | 5016 | } |
| 4984 | 5017 | return .{ |
| 4985 | 5018 | .param_body = param_body, |
| 5019 | .ret_ty_body = info.ret_ty_body, | |
| 4986 | 5020 | .body = info.body, |
| 4987 | 5021 | .total_params_len = total_params_len, |
| 4988 | 5022 | }; |
src/codegen/llvm.zig+15-1| ... | ... | @@ -575,6 +575,14 @@ pub const DeclGen = struct { |
| 575 | 575 | const info = t.intInfo(self.module.getTarget()); |
| 576 | 576 | return self.context.intType(info.bits); |
| 577 | 577 | }, |
| 578 | .Float => switch (t.floatBits(self.module.getTarget())) { | |
| 579 | 16 => return self.context.halfType(), | |
| 580 | 32 => return self.context.floatType(), | |
| 581 | 64 => return self.context.doubleType(), | |
| 582 | 80 => return self.context.x86FP80Type(), | |
| 583 | 128 => return self.context.fp128Type(), | |
| 584 | else => unreachable, | |
| 585 | }, | |
| 578 | 586 | .Bool => return self.context.intType(1), |
| 579 | 587 | .Pointer => { |
| 580 | 588 | if (t.isSlice()) { |
| ... | ... | @@ -661,7 +669,6 @@ pub const DeclGen = struct { |
| 661 | 669 | |
| 662 | 670 | .BoundFn => @panic("TODO remove BoundFn from the language"), |
| 663 | 671 | |
| 664 | .Float, | |
| 665 | 672 | .Enum, |
| 666 | 673 | .Union, |
| 667 | 674 | .Opaque, |
| ... | ... | @@ -699,6 +706,13 @@ pub const DeclGen = struct { |
| 699 | 706 | } |
| 700 | 707 | return llvm_int; |
| 701 | 708 | }, |
| 709 | .Float => { | |
| 710 | if (tv.ty.floatBits(self.module.getTarget()) <= 64) { | |
| 711 | const llvm_ty = try self.llvmType(tv.ty); | |
| 712 | return llvm_ty.constReal(tv.val.toFloat(f64)); | |
| 713 | } | |
| 714 | return self.todo("bitcast to f128 from an integer", .{}); | |
| 715 | }, | |
| 702 | 716 | .Pointer => switch (tv.val.tag()) { |
| 703 | 717 | .decl_ref => { |
| 704 | 718 | if (tv.ty.isSlice()) { |
src/codegen/llvm/bindings.zig+18| ... | ... | @@ -31,6 +31,21 @@ pub const Context = opaque { |
| 31 | 31 | pub const intType = LLVMIntTypeInContext; |
| 32 | 32 | extern fn LLVMIntTypeInContext(C: *const Context, NumBits: c_uint) *const Type; |
| 33 | 33 | |
| 34 | pub const halfType = LLVMHalfTypeInContext; | |
| 35 | extern fn LLVMHalfTypeInContext(C: *const Context) *const Type; | |
| 36 | ||
| 37 | pub const floatType = LLVMFloatTypeInContext; | |
| 38 | extern fn LLVMFloatTypeInContext(C: *const Context) *const Type; | |
| 39 | ||
| 40 | pub const doubleType = LLVMDoubleTypeInContext; | |
| 41 | extern fn LLVMDoubleTypeInContext(C: *const Context) *const Type; | |
| 42 | ||
| 43 | pub const x86FP80Type = LLVMX86FP80TypeInContext; | |
| 44 | extern fn LLVMX86FP80TypeInContext(C: *const Context) *const Type; | |
| 45 | ||
| 46 | pub const fp128Type = LLVMFP128TypeInContext; | |
| 47 | extern fn LLVMFP128TypeInContext(C: *const Context) *const Type; | |
| 48 | ||
| 34 | 49 | pub const voidType = LLVMVoidTypeInContext; |
| 35 | 50 | extern fn LLVMVoidTypeInContext(C: *const Context) *const Type; |
| 36 | 51 | |
| ... | ... | @@ -127,6 +142,9 @@ pub const Type = opaque { |
| 127 | 142 | pub const constInt = LLVMConstInt; |
| 128 | 143 | extern fn LLVMConstInt(IntTy: *const Type, N: c_ulonglong, SignExtend: Bool) *const Value; |
| 129 | 144 | |
| 145 | pub const constReal = LLVMConstReal; | |
| 146 | extern fn LLVMConstReal(RealTy: *const Type, N: f64) *const Value; | |
| 147 | ||
| 130 | 148 | pub const constArray = LLVMConstArray; |
| 131 | 149 | extern fn LLVMConstArray(ElementTy: *const Type, ConstantVals: [*]*const Value, Length: c_uint) *const Value; |
| 132 | 150 |
src/print_air.zig+1-1| ... | ... | @@ -222,7 +222,7 @@ const Writer = struct { |
| 222 | 222 | const extra = w.air.extraData(Air.Block, ty_pl.payload); |
| 223 | 223 | const body = w.air.extra[extra.end..][0..extra.data.body_len]; |
| 224 | 224 | |
| 225 | try s.writeAll("{\n"); | |
| 225 | try s.print("{}, {{\n", .{w.air.getRefType(ty_pl.ty)}); | |
| 226 | 226 | const old_indent = w.indent; |
| 227 | 227 | w.indent += 2; |
| 228 | 228 | try w.writeBody(s, body); |
test/behavior/generics.zig+56| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | |
| 2 | 3 | const testing = std.testing; |
| 3 | 4 | const expect = testing.expect; |
| 4 | 5 | const expectEqual = testing.expectEqual; |
| ... | ... | @@ -14,3 +15,58 @@ test "one param, explicit comptime" { |
| 14 | 15 | fn checkSize(comptime T: type) usize { |
| 15 | 16 | return @sizeOf(T); |
| 16 | 17 | } |
| 18 | ||
| 19 | test "simple generic fn" { | |
| 20 | try expect(max(i32, 3, -1) == 3); | |
| 21 | try expect(max(u8, 1, 100) == 100); | |
| 22 | if (!builtin.zig_is_stage2) { | |
| 23 | // TODO: stage2 is incorrectly emitting the following: | |
| 24 | // error: cast of value 1.23e-01 to type 'f32' loses information | |
| 25 | try expect(max(f32, 0.123, 0.456) == 0.456); | |
| 26 | } | |
| 27 | try expect(add(2, 3) == 5); | |
| 28 | } | |
| 29 | ||
| 30 | fn max(comptime T: type, a: T, b: T) T { | |
| 31 | if (!builtin.zig_is_stage2) { | |
| 32 | // TODO: stage2 is incorrectly emitting AIR that allocates a result | |
| 33 | // value, stores to it, but then returns void instead of the result. | |
| 34 | return if (a > b) a else b; | |
| 35 | } | |
| 36 | if (a > b) { | |
| 37 | return a; | |
| 38 | } else { | |
| 39 | return b; | |
| 40 | } | |
| 41 | } | |
| 42 | ||
| 43 | fn add(comptime a: i32, b: i32) i32 { | |
| 44 | return (comptime a) + b; | |
| 45 | } | |
| 46 | ||
| 47 | const the_max = max(u32, 1234, 5678); | |
| 48 | test "compile time generic eval" { | |
| 49 | try expect(the_max == 5678); | |
| 50 | } | |
| 51 | ||
| 52 | fn gimmeTheBigOne(a: u32, b: u32) u32 { | |
| 53 | return max(u32, a, b); | |
| 54 | } | |
| 55 | ||
| 56 | fn shouldCallSameInstance(a: u32, b: u32) u32 { | |
| 57 | return max(u32, a, b); | |
| 58 | } | |
| 59 | ||
| 60 | fn sameButWithFloats(a: f64, b: f64) f64 { | |
| 61 | return max(f64, a, b); | |
| 62 | } | |
| 63 | ||
| 64 | test "fn with comptime args" { | |
| 65 | try expect(gimmeTheBigOne(1234, 5678) == 5678); | |
| 66 | try expect(shouldCallSameInstance(34, 12) == 34); | |
| 67 | if (!builtin.zig_is_stage2) { | |
| 68 | // TODO: stage2 llvm backend needs to use fcmp instead of icmp | |
| 69 | // probably AIR should just have different instructions for floats. | |
| 70 | try expect(sameButWithFloats(0.43, 0.49) == 0.49); | |
| 71 | } | |
| 72 | } |
test/behavior/generics_stage1.zig+1-38| ... | ... | @@ -3,44 +3,7 @@ const testing = std.testing; |
| 3 | 3 | const expect = testing.expect; |
| 4 | 4 | const expectEqual = testing.expectEqual; |
| 5 | 5 | |
| 6 | test "simple generic fn" { | |
| 7 | try expect(max(i32, 3, -1) == 3); | |
| 8 | try expect(max(f32, 0.123, 0.456) == 0.456); | |
| 9 | try expect(add(2, 3) == 5); | |
| 10 | } | |
| 11 | ||
| 12 | fn max(comptime T: type, a: T, b: T) T { | |
| 13 | return if (a > b) a else b; | |
| 14 | } | |
| 15 | ||
| 16 | fn add(comptime a: i32, b: i32) i32 { | |
| 17 | return (comptime a) + b; | |
| 18 | } | |
| 19 | ||
| 20 | const the_max = max(u32, 1234, 5678); | |
| 21 | test "compile time generic eval" { | |
| 22 | try expect(the_max == 5678); | |
| 23 | } | |
| 24 | ||
| 25 | fn gimmeTheBigOne(a: u32, b: u32) u32 { | |
| 26 | return max(u32, a, b); | |
| 27 | } | |
| 28 | ||
| 29 | fn shouldCallSameInstance(a: u32, b: u32) u32 { | |
| 30 | return max(u32, a, b); | |
| 31 | } | |
| 32 | ||
| 33 | fn sameButWithFloats(a: f64, b: f64) f64 { | |
| 34 | return max(f64, a, b); | |
| 35 | } | |
| 36 | ||
| 37 | test "fn with comptime args" { | |
| 38 | try expect(gimmeTheBigOne(1234, 5678) == 5678); | |
| 39 | try expect(shouldCallSameInstance(34, 12) == 34); | |
| 40 | try expect(sameButWithFloats(0.43, 0.49) == 0.49); | |
| 41 | } | |
| 42 | ||
| 43 | test "var params" { | |
| 6 | test "anytype params" { | |
| 44 | 7 | try expect(max_i32(12, 34) == 34); |
| 45 | 8 | try expect(max_f64(1.2, 3.4) == 3.4); |
| 46 | 9 | } |