authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-06 16:24:39-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-06 16:24:39-07:00
logede76f4fe31e6fc935ae10f030eb2c97ed36aaaa
treeeb717277c8b38e4963eb7b406a4d01bea1ba72c5
parentea7bdeb67d474526732b117992971603e4065f98

stage2: fix generics with non-comptime anytype parameters

The `comptime_args` field of Fn has a clarified purpose: For generic function instantiations, there is a `TypedValue` here for each parameter of the function: * Non-comptime parameters are marked with a `generic_poison` for the value. * Non-anytype parameters are marked with a `generic_poison` for the type. Sema now has a `fn_ret_ty` field. Doc comments reproduced here: > When semantic analysis needs to know the return type of the function whose body > is being analyzed, this `Type` should be used instead of going through `func`. > This will correctly handle the case of a comptime/inline function call of a > generic function which uses a type expression for the return type. > The type will be `void` in the case that `func` is `null`. Various places in Sema are modified in accordance with this guidance. Fixed `resolveMaybeUndefVal` not returning `error.GenericPoison` when Value Tag of `generic_poison` is encountered. Fixed generic function memoization incorrect equality checking. The logic now clearly deals properly with any combination of anytype and comptime parameters. Fixed not removing generic function instantiation from the table in case a compile errors in the rest of `call` semantic analysis. This required introduction of yet another adapter which I have called `GenericRemoveAdapter`. This one is nice and simple - it's the same hash function (the same precomputed hash is passed in) but the equality function checks pointers rather than doing any logic. Inline/comptime function calls coerce each argument in accordance with the function parameter type expressions. Likewise the return type expression is evaluated and provided (see `fn_ret_ty` above). There's a new compile error "unable to monomorphize function". It's pretty unhelpful and will need to get improved in the future. It happens when a type expression in a generic function did not end up getting resolved at a callsite. This can happen, for example, if a runtime parameter is attempted to be used where it needed to be comptime known: ```zig fn foo(x: anytype) [x]u8 { _ = x; } ``` In this example, even if we pass a number such as `10` for `x`, it is not marked `comptime`, so `x` will have a runtime known value, making the return type unable to resolve. In the LLVM backend I implement cmp instructions for float types to pass some behavior tests that used floats.

6 files changed, 303 insertions(+), 151 deletions(-)

src/Module.zig+9-3
......@@ -801,8 +801,9 @@ pub const Fn = struct {
801801 /// The Decl that corresponds to the function itself.
802802 owner_decl: *Decl,
803803 /// If this is not null, this function is a generic function instantiation, and
804 /// there is a `Value` here for each parameter of the function. Non-comptime
805 /// parameters are marked with an `unreachable_value`.
804 /// there is a `TypedValue` here for each parameter of the function.
805 /// Non-comptime parameters are marked with a `generic_poison` for the value.
806 /// Non-anytype parameters are marked with a `generic_poison` for the type.
806807 comptime_args: ?[*]TypedValue = null,
807808 /// The ZIR instruction that is a function instruction. Use this to find
808809 /// the body. We store this rather than the body directly so that when ZIR
......@@ -2975,6 +2976,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
29752976 .owner_decl = new_decl,
29762977 .namespace = &struct_obj.namespace,
29772978 .func = null,
2979 .fn_ret_ty = Type.initTag(.void),
29782980 .owner_func = null,
29792981 };
29802982 defer sema.deinit();
......@@ -3029,6 +3031,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
30293031 .owner_decl = decl,
30303032 .namespace = decl.namespace,
30313033 .func = null,
3034 .fn_ret_ty = Type.initTag(.void),
30323035 .owner_func = null,
30333036 };
30343037 defer sema.deinit();
......@@ -3712,6 +3715,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
37123715 .owner_decl = decl,
37133716 .namespace = decl.namespace,
37143717 .func = func,
3718 .fn_ret_ty = func.owner_decl.ty.fnReturnType(),
37153719 .owner_func = func,
37163720 };
37173721 defer sema.deinit();
......@@ -3764,7 +3768,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
37643768 };
37653769 if (func.comptime_args) |comptime_args| {
37663770 const arg_tv = comptime_args[total_param_index];
3767 if (arg_tv.val.tag() != .unreachable_value) {
3771 if (arg_tv.val.tag() != .generic_poison) {
37683772 // We have a comptime value for this parameter.
37693773 const arg = try sema.addConstant(arg_tv.ty, arg_tv.val);
37703774 sema.inst_map.putAssumeCapacityNoClobber(inst, arg);
......@@ -4447,6 +4451,7 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) CompileError!void
44474451 .namespace = &struct_obj.namespace,
44484452 .owner_func = null,
44494453 .func = null,
4454 .fn_ret_ty = Type.initTag(.void),
44504455 };
44514456 defer sema.deinit();
44524457
......@@ -4600,6 +4605,7 @@ pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) CompileError!void {
46004605 .namespace = &union_obj.namespace,
46014606 .owner_func = null,
46024607 .func = null,
4608 .fn_ret_ty = Type.initTag(.void),
46034609 };
46044610 defer sema.deinit();
46054611
src/Sema.zig+212-107
......@@ -29,6 +29,12 @@ owner_func: ?*Module.Fn,
2929/// This starts out the same as `owner_func` and then diverges in the case of
3030/// an inline or comptime function call.
3131func: ?*Module.Fn,
32/// When semantic analysis needs to know the return type of the function whose body
33/// is being analyzed, this `Type` should be used instead of going through `func`.
34/// This will correctly handle the case of a comptime/inline function call of a
35/// generic function which uses a type expression for the return type.
36/// The type will be `void` in the case that `func` is `null`.
37fn_ret_ty: Type,
3238branch_quota: u32 = 1000,
3339branch_count: u32 = 0,
3440/// This field is updated when a new source location becomes active, so that
......@@ -628,6 +634,7 @@ fn analyzeAsType(
628634
629635/// May return Value Tags: `variable`, `undef`.
630636/// See `resolveConstValue` for an alternative.
637/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
631638fn resolveValue(
632639 sema: *Sema,
633640 block: *Scope.Block,
......@@ -679,6 +686,7 @@ fn resolveDefinedValue(
679686
680687/// Value Tag `variable` causes this function to return `null`.
681688/// Value Tag `undef` causes this function to return the Value.
689/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
682690fn resolveMaybeUndefVal(
683691 sema: *Sema,
684692 block: *Scope.Block,
......@@ -686,10 +694,11 @@ fn resolveMaybeUndefVal(
686694 inst: Air.Inst.Ref,
687695) CompileError!?Value {
688696 const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) orelse return null;
689 if (val.tag() == .variable) {
690 return null;
697 switch (val.tag()) {
698 .variable => return null,
699 .generic_poison => return error.GenericPoison,
700 else => return val,
691701 }
692 return val;
693702}
694703
695704/// Returns all Value tags including `variable` and `undef`.
......@@ -1033,6 +1042,7 @@ fn zirEnumDecl(
10331042 .namespace = &enum_obj.namespace,
10341043 .owner_func = null,
10351044 .func = null,
1045 .fn_ret_ty = Type.initTag(.void),
10361046 .branch_quota = sema.branch_quota,
10371047 .branch_count = sema.branch_count,
10381048 };
......@@ -1238,9 +1248,7 @@ fn zirRetPtr(
12381248
12391249 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
12401250 try sema.requireFunctionBlock(block, src);
1241 const fn_ty = sema.func.?.owner_decl.ty;
1242 const ret_type = fn_ty.fnReturnType();
1243 const ptr_type = try Module.simplePtrType(sema.arena, ret_type, true, .One);
1251 const ptr_type = try Module.simplePtrType(sema.arena, sema.fn_ret_ty, true, .One);
12441252 return block.addTy(.alloc, ptr_type);
12451253}
12461254
......@@ -1263,9 +1271,7 @@ fn zirRetType(
12631271
12641272 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
12651273 try sema.requireFunctionBlock(block, src);
1266 const fn_ty = sema.func.?.owner_decl.ty;
1267 const ret_type = fn_ty.fnReturnType();
1268 return sema.addType(ret_type);
1274 return sema.addType(sema.fn_ret_ty);
12691275}
12701276
12711277fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
......@@ -2364,7 +2370,7 @@ const GenericCallAdapter = struct {
23642370 generic_fn: *Module.Fn,
23652371 precomputed_hash: u64,
23662372 func_ty_info: Type.Payload.Function.Data,
2367 comptime_vals: []const Value,
2373 comptime_tvs: []const TypedValue,
23682374
23692375 pub fn eql(ctx: @This(), adapted_key: void, other_key: *Module.Fn) bool {
23702376 _ = adapted_key;
......@@ -2373,12 +2379,22 @@ const GenericCallAdapter = struct {
23732379 const generic_owner_decl = other_key.owner_decl.dependencies.keys()[0];
23742380 if (ctx.generic_fn.owner_decl != generic_owner_decl) return false;
23752381
2376 // This logic must be kept in sync with the logic in `analyzeCall` that
2377 // computes the hash.
23782382 const other_comptime_args = other_key.comptime_args.?;
2379 for (ctx.func_ty_info.param_types) |param_ty, i| {
2380 if (ctx.func_ty_info.paramIsComptime(i) and param_ty.tag() != .generic_poison) {
2381 if (!ctx.comptime_vals[i].eql(other_comptime_args[i].val, param_ty)) {
2383 for (other_comptime_args[0..ctx.func_ty_info.param_types.len]) |other_arg, i| {
2384 if (other_arg.ty.tag() != .generic_poison) {
2385 // anytype parameter
2386 if (!other_arg.ty.eql(ctx.comptime_tvs[i].ty)) {
2387 return false;
2388 }
2389 }
2390 if (other_arg.val.tag() != .generic_poison) {
2391 // comptime parameter
2392 if (ctx.comptime_tvs[i].val.tag() == .generic_poison) {
2393 // No match because the instantiation has a comptime parameter
2394 // but the callsite does not.
2395 return false;
2396 }
2397 if (!other_arg.val.eql(ctx.comptime_tvs[i].val, other_arg.ty)) {
23822398 return false;
23832399 }
23842400 }
......@@ -2394,6 +2410,22 @@ const GenericCallAdapter = struct {
23942410 }
23952411};
23962412
2413const GenericRemoveAdapter = struct {
2414 precomputed_hash: u64,
2415
2416 pub fn eql(ctx: @This(), adapted_key: *Module.Fn, other_key: *Module.Fn) bool {
2417 _ = ctx;
2418 return adapted_key == other_key;
2419 }
2420
2421 /// The implementation of the hash is in semantic analysis of function calls, so
2422 /// that any errors when computing the hash can be properly reported.
2423 pub fn hash(ctx: @This(), adapted_key: *Module.Fn) u64 {
2424 _ = adapted_key;
2425 return ctx.precomputed_hash;
2426 }
2427};
2428
23972429fn analyzeCall(
23982430 sema: *Sema,
23992431 block: *Scope.Block,
......@@ -2466,14 +2498,6 @@ fn analyzeCall(
24662498 const is_inline_call = is_comptime_call or modifier == .always_inline or
24672499 func_ty_info.cc == .Inline;
24682500 const result: Air.Inst.Ref = if (is_inline_call) res: {
2469 // TODO look into not allocating this args array
2470 const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len);
2471 for (uncasted_args) |uncasted_arg, i| {
2472 const param_ty = func_ty.fnParamType(i);
2473 const arg_src = call_src; // TODO: better source location
2474 args[i] = try sema.coerce(block, param_ty, uncasted_arg, arg_src);
2475 }
2476
24772501 const func_val = try sema.resolveConstValue(block, func_src, func);
24782502 const module_fn = switch (func_val.tag()) {
24792503 .function => func_val.castTag(.function).?.data,
......@@ -2544,19 +2568,62 @@ fn analyzeCall(
25442568 // This will have return instructions analyzed as break instructions to
25452569 // the block_inst above. Here we are performing "comptime/inline semantic analysis"
25462570 // for a function body, which means we must map the parameter ZIR instructions to
2547 // the AIR instructions of the callsite.
2571 // the AIR instructions of the callsite. The callee could be a generic function
2572 // which means its parameter type expressions must be resolved in order and used
2573 // to successively coerce the arguments.
25482574 const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst);
25492575 const zir_tags = sema.code.instructions.items(.tag);
25502576 var arg_i: usize = 0;
2551 try sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, args.len));
2552 for (fn_info.param_body) |inst| {
2553 switch (zir_tags[inst]) {
2554 .param, .param_comptime, .param_anytype, .param_anytype_comptime => {},
2555 else => continue,
2577 for (fn_info.param_body) |inst| switch (zir_tags[inst]) {
2578 .param, .param_comptime => {
2579 // Evaluate the parameter type expression now that previous ones have
2580 // been mapped, and coerce the corresponding argument to it.
2581 const pl_tok = sema.code.instructions.items(.data)[inst].pl_tok;
2582 const param_src = pl_tok.src();
2583 const extra = sema.code.extraData(Zir.Inst.Param, pl_tok.payload_index);
2584 const param_body = sema.code.extra[extra.end..][0..extra.data.body_len];
2585 const param_ty_inst = try sema.resolveBody(&child_block, param_body);
2586 const param_ty = try sema.analyzeAsType(&child_block, param_src, param_ty_inst);
2587 const arg_src = call_src; // TODO: better source location
2588 const casted_arg = try sema.coerce(&child_block, param_ty, uncasted_args[arg_i], arg_src);
2589 try sema.inst_map.putNoClobber(gpa, inst, casted_arg);
2590 arg_i += 1;
2591 continue;
2592 },
2593 .param_anytype, .param_anytype_comptime => {
2594 // No coercion needed.
2595 try sema.inst_map.putNoClobber(gpa, inst, uncasted_args[arg_i]);
2596 arg_i += 1;
2597 continue;
2598 },
2599 else => continue,
2600 };
2601
2602 // In case it is a generic function with an expression for the return type that depends
2603 // on parameters, we must now do the same for the return type as we just did with
2604 // each of the parameters, resolving the return type and providing it to the child
2605 // `Sema` so that it can be used for the `ret_ptr` instruction.
2606 const ret_ty_inst = try sema.resolveBody(&child_block, fn_info.ret_ty_body);
2607 const ret_ty_src = func_src; // TODO better source location
2608 const bare_return_type = try sema.analyzeAsType(&child_block, ret_ty_src, ret_ty_inst);
2609 // If the function has an inferred error set, `bare_return_type` is the payload type only.
2610 const fn_ret_ty = blk: {
2611 // TODO instead of reusing the function's inferred error set, this code should
2612 // create a temporary error set which is used for the comptime/inline function
2613 // call alone, independent from the runtime instantiation.
2614 if (func_ty_info.return_type.castTag(.error_union)) |payload| {
2615 const error_set_ty = payload.data.error_set;
2616 break :blk try Type.Tag.error_union.create(sema.arena, .{
2617 .error_set = error_set_ty,
2618 .payload = bare_return_type,
2619 });
25562620 }
2557 sema.inst_map.putAssumeCapacityNoClobber(inst, args[arg_i]);
2558 arg_i += 1;
2559 }
2621 break :blk bare_return_type;
2622 };
2623 const parent_fn_ret_ty = sema.fn_ret_ty;
2624 sema.fn_ret_ty = fn_ret_ty;
2625 defer sema.fn_ret_ty = parent_fn_ret_ty;
2626
25602627 _ = try sema.analyzeBody(&child_block, fn_info.body);
25612628 break :res try sema.analyzeBlockBody(block, call_src, &child_block, merges);
25622629 } else if (func_ty_info.is_generic) res: {
......@@ -2569,57 +2636,74 @@ fn analyzeCall(
25692636 const fn_zir = namespace.file_scope.zir;
25702637 const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);
25712638 const zir_tags = fn_zir.instructions.items(.tag);
2572 const new_module_func = new_func: {
2573 // This hash must match `Module.MonomorphedFuncsContext.hash`.
2574 // For parameters explicitly marked comptime and simple parameter type expressions,
2575 // we know whether a parameter is elided from a monomorphed function, and can
2576 // use it in the hash here. However, for parameter type expressions that are not
2577 // explicitly marked comptime and rely on previous parameter comptime values, we
2578 // don't find out until after generating a monomorphed function whether the parameter
2579 // type ended up being a "must-be-comptime-known" type.
2580 var hasher = std.hash.Wyhash.init(0);
2581 std.hash.autoHash(&hasher, @ptrToInt(module_fn));
2582
2583 const comptime_vals = try sema.arena.alloc(Value, func_ty_info.param_types.len);
2584
2585 for (func_ty_info.param_types) |param_ty, i| {
2586 const is_comptime = func_ty_info.paramIsComptime(i);
2587 if (is_comptime and param_ty.tag() != .generic_poison) {
2588 const arg_src = call_src; // TODO better source location
2589 const casted_arg = try sema.coerce(block, param_ty, uncasted_args[i], arg_src);
2590 if (try sema.resolveMaybeUndefVal(block, arg_src, casted_arg)) |arg_val| {
2639
2640 // This hash must match `Module.MonomorphedFuncsContext.hash`.
2641 // For parameters explicitly marked comptime and simple parameter type expressions,
2642 // we know whether a parameter is elided from a monomorphed function, and can
2643 // use it in the hash here. However, for parameter type expressions that are not
2644 // explicitly marked comptime and rely on previous parameter comptime values, we
2645 // don't find out until after generating a monomorphed function whether the parameter
2646 // type ended up being a "must-be-comptime-known" type.
2647 var hasher = std.hash.Wyhash.init(0);
2648 std.hash.autoHash(&hasher, @ptrToInt(module_fn));
2649
2650 const comptime_tvs = try sema.arena.alloc(TypedValue, func_ty_info.param_types.len);
2651
2652 for (func_ty_info.param_types) |param_ty, i| {
2653 const is_comptime = func_ty_info.paramIsComptime(i);
2654 if (is_comptime) {
2655 const arg_src = call_src; // TODO better source location
2656 const casted_arg = try sema.coerce(block, param_ty, uncasted_args[i], arg_src);
2657 if (try sema.resolveMaybeUndefVal(block, arg_src, casted_arg)) |arg_val| {
2658 if (param_ty.tag() != .generic_poison) {
25912659 arg_val.hash(param_ty, &hasher);
2592 comptime_vals[i] = arg_val;
2593 } else {
2594 return sema.failWithNeededComptime(block, arg_src);
25952660 }
2661 comptime_tvs[i] = .{
2662 // This will be different than `param_ty` in the case of `generic_poison`.
2663 .ty = sema.typeOf(casted_arg),
2664 .val = arg_val,
2665 };
2666 } else {
2667 return sema.failWithNeededComptime(block, arg_src);
25962668 }
2669 } else {
2670 comptime_tvs[i] = .{
2671 .ty = sema.typeOf(uncasted_args[i]),
2672 .val = Value.initTag(.generic_poison),
2673 };
25972674 }
2675 }
25982676
2599 const adapter: GenericCallAdapter = .{
2600 .generic_fn = module_fn,
2601 .precomputed_hash = hasher.final(),
2602 .func_ty_info = func_ty_info,
2603 .comptime_vals = comptime_vals,
2604 };
2605 const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter);
2606 if (gop.found_existing) {
2607 const callee_func = gop.key_ptr.*;
2608 break :res try sema.finishGenericCall(
2609 block,
2610 call_src,
2611 callee_func,
2612 func_src,
2613 uncasted_args,
2614 fn_info,
2615 zir_tags,
2616 );
2617 }
2618 gop.key_ptr.* = try gpa.create(Module.Fn);
2619 break :new_func gop.key_ptr.*;
2620 };
2677 const precomputed_hash = hasher.final();
26212678
2679 const adapter: GenericCallAdapter = .{
2680 .generic_fn = module_fn,
2681 .precomputed_hash = precomputed_hash,
2682 .func_ty_info = func_ty_info,
2683 .comptime_tvs = comptime_tvs,
2684 };
2685 const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter);
2686 if (gop.found_existing) {
2687 const callee_func = gop.key_ptr.*;
2688 break :res try sema.finishGenericCall(
2689 block,
2690 call_src,
2691 callee_func,
2692 func_src,
2693 uncasted_args,
2694 fn_info,
2695 zir_tags,
2696 );
2697 }
2698 const new_module_func = try gpa.create(Module.Fn);
2699 gop.key_ptr.* = new_module_func;
26222700 {
2701 errdefer gpa.destroy(new_module_func);
2702 const remove_adapter: GenericRemoveAdapter = .{
2703 .precomputed_hash = precomputed_hash,
2704 };
2705 errdefer assert(mod.monomorphed_funcs.removeAdapted(new_module_func, remove_adapter));
2706
26232707 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
26242708
26252709 // Create a Decl for the new function.
......@@ -2658,6 +2742,7 @@ fn analyzeCall(
26582742 .owner_decl = new_decl,
26592743 .namespace = namespace,
26602744 .func = null,
2745 .fn_ret_ty = Type.initTag(.void),
26612746 .owner_func = null,
26622747 .comptime_args = try new_decl_arena.allocator.alloc(TypedValue, uncasted_args.len),
26632748 .comptime_args_fn_inst = module_fn.zir_body_inst,
......@@ -2681,11 +2766,25 @@ fn analyzeCall(
26812766 try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, uncasted_args.len));
26822767 var arg_i: usize = 0;
26832768 for (fn_info.param_body) |inst| {
2684 const is_comptime = switch (zir_tags[inst]) {
2685 .param_comptime, .param_anytype_comptime => true,
2686 .param, .param_anytype => false,
2769 var is_comptime = false;
2770 var is_anytype = false;
2771 switch (zir_tags[inst]) {
2772 .param => {
2773 is_comptime = func_ty_info.paramIsComptime(arg_i);
2774 },
2775 .param_comptime => {
2776 is_comptime = true;
2777 },
2778 .param_anytype => {
2779 is_anytype = true;
2780 is_comptime = func_ty_info.paramIsComptime(arg_i);
2781 },
2782 .param_anytype_comptime => {
2783 is_anytype = true;
2784 is_comptime = true;
2785 },
26872786 else => continue,
2688 } or func_ty_info.paramIsComptime(arg_i);
2787 }
26892788 const arg_src = call_src; // TODO: better source location
26902789 const arg = uncasted_args[arg_i];
26912790 if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {
......@@ -2693,6 +2792,12 @@ fn analyzeCall(
26932792 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
26942793 } else if (is_comptime) {
26952794 return sema.failWithNeededComptime(block, arg_src);
2795 } else if (is_anytype) {
2796 const child_arg = try child_sema.addConstant(
2797 sema.typeOf(arg),
2798 Value.initTag(.generic_poison),
2799 );
2800 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
26962801 }
26972802 arg_i += 1;
26982803 }
......@@ -2710,17 +2815,10 @@ fn analyzeCall(
27102815 const arg = child_sema.inst_map.get(inst).?;
27112816 const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(&child_block, .unneeded, arg) catch unreachable).?;
27122817
2713 if (arg_val.tag() == .generic_poison) {
2714 child_sema.comptime_args[arg_i] = .{
2715 .ty = Type.initTag(.noreturn),
2716 .val = Value.initTag(.unreachable_value),
2717 };
2718 } else {
2719 child_sema.comptime_args[arg_i] = .{
2720 .ty = try child_sema.typeOf(arg).copy(&new_decl_arena.allocator),
2721 .val = try arg_val.copy(&new_decl_arena.allocator),
2722 };
2723 }
2818 child_sema.comptime_args[arg_i] = .{
2819 .ty = try child_sema.typeOf(arg).copy(&new_decl_arena.allocator),
2820 .val = try arg_val.copy(&new_decl_arena.allocator),
2821 };
27242822
27252823 arg_i += 1;
27262824 }
......@@ -2730,6 +2828,18 @@ fn analyzeCall(
27302828 new_decl.val = try Value.Tag.function.create(&new_decl_arena.allocator, new_func);
27312829 new_decl.analysis = .complete;
27322830
2831 if (new_decl.ty.fnInfo().is_generic) {
2832 // TODO improve this error message. This can happen because of the parameter
2833 // type expression or return type expression depending on runtime-provided values.
2834 // The error message should be emitted in zirParam or funcCommon when it
2835 // is determined that we are trying to instantiate a generic function.
2836 return mod.fail(&block.base, call_src, "unable to monomorphize function", .{});
2837 }
2838
2839 log.debug("generic function '{s}' instantiated with type {}", .{
2840 new_decl.name, new_decl.ty,
2841 });
2842
27332843 // The generic function Decl is guaranteed to be the first dependency
27342844 // of each of its instantiations.
27352845 assert(new_decl.dependencies.keys().len == 0);
......@@ -2809,7 +2919,7 @@ fn finishGenericCall(
28092919 for (fn_info.param_body) |inst| {
28102920 switch (zir_tags[inst]) {
28112921 .param_comptime, .param_anytype_comptime, .param, .param_anytype => {
2812 if (comptime_args[arg_i].val.tag() == .unreachable_value) {
2922 if (comptime_args[arg_i].val.tag() == .generic_poison) {
28132923 count += 1;
28142924 }
28152925 arg_i += 1;
......@@ -2829,7 +2939,7 @@ fn finishGenericCall(
28292939 .param_comptime, .param_anytype_comptime, .param, .param_anytype => {},
28302940 else => continue,
28312941 }
2832 const is_runtime = comptime_args[total_i].val.tag() == .unreachable_value;
2942 const is_runtime = comptime_args[total_i].val.tag() == .generic_poison;
28332943 if (is_runtime) {
28342944 const param_ty = new_fn_ty.fnParamType(runtime_i);
28352945 const arg_src = call_src; // TODO: better source location
......@@ -6162,28 +6272,23 @@ fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
61626272fn analyzeRet(
61636273 sema: *Sema,
61646274 block: *Scope.Block,
6165 operand: Air.Inst.Ref,
6275 uncasted_operand: Air.Inst.Ref,
61666276 src: LazySrcLoc,
61676277 need_coercion: bool,
61686278) CompileError!Zir.Inst.Index {
6169 const casted_operand = if (!need_coercion) operand else op: {
6170 const func = sema.func.?;
6171 const fn_ty = func.owner_decl.ty;
6172 // TODO: In the case of a comptime/inline function call of a generic function,
6173 // this needs to be the resolved return type based on the function parameter type
6174 // expressions being evaluated with comptime arguments passed in. Otherwise, this
6175 // ends up being .generic_poison and failing the comptime/inline function call analysis.
6176 const fn_ret_ty = fn_ty.fnReturnType();
6177 break :op try sema.coerce(block, fn_ret_ty, operand, src);
6178 };
6279 const operand = if (!need_coercion)
6280 uncasted_operand
6281 else
6282 try sema.coerce(block, sema.fn_ret_ty, uncasted_operand, src);
6283
61796284 if (block.inlining) |inlining| {
61806285 // We are inlining a function call; rewrite the `ret` as a `break`.
6181 try inlining.merges.results.append(sema.gpa, casted_operand);
6182 _ = try block.addBr(inlining.merges.block_inst, casted_operand);
6286 try inlining.merges.results.append(sema.gpa, operand);
6287 _ = try block.addBr(inlining.merges.block_inst, operand);
61836288 return always_noreturn;
61846289 }
61856290
6186 _ = try block.addUnOp(.ret, casted_operand);
6291 _ = try block.addUnOp(.ret, operand);
61876292 return always_noreturn;
61886293}
61896294
src/codegen/llvm.zig+26-15
......@@ -1093,21 +1093,32 @@ pub const FuncGen = struct {
10931093 const rhs = try self.resolveInst(bin_op.rhs);
10941094 const inst_ty = self.air.typeOfIndex(inst);
10951095
1096 if (!inst_ty.isInt())
1097 if (inst_ty.tag() != .bool)
1098 return self.todo("implement 'airCmp' for type {}", .{inst_ty});
1099
1100 const is_signed = inst_ty.isSignedInt();
1101 const operation = switch (op) {
1102 .eq => .EQ,
1103 .neq => .NE,
1104 .lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT),
1105 .lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE),
1106 .gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT),
1107 .gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE),
1108 };
1109
1110 return self.builder.buildICmp(operation, lhs, rhs, "");
1096 switch (self.air.typeOf(bin_op.lhs).zigTypeTag()) {
1097 .Int, .Bool, .Pointer => {
1098 const is_signed = inst_ty.isSignedInt();
1099 const operation = switch (op) {
1100 .eq => .EQ,
1101 .neq => .NE,
1102 .lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT),
1103 .lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE),
1104 .gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT),
1105 .gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE),
1106 };
1107 return self.builder.buildICmp(operation, lhs, rhs, "");
1108 },
1109 .Float => {
1110 const operation: llvm.RealPredicate = switch (op) {
1111 .eq => .OEQ,
1112 .neq => .UNE,
1113 .lt => .OLT,
1114 .lte => .OLE,
1115 .gt => .OGT,
1116 .gte => .OGE,
1117 };
1118 return self.builder.buildFCmp(operation, lhs, rhs, "");
1119 },
1120 else => unreachable,
1121 }
11111122 }
11121123
11131124 fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
src/codegen/llvm/bindings.zig+21-1
......@@ -409,6 +409,9 @@ pub const Builder = opaque {
409409 pub const buildICmp = LLVMBuildICmp;
410410 extern fn LLVMBuildICmp(*const Builder, Op: IntPredicate, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
411411
412 pub const buildFCmp = LLVMBuildFCmp;
413 extern fn LLVMBuildFCmp(*const Builder, Op: RealPredicate, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;
414
412415 pub const buildBr = LLVMBuildBr;
413416 extern fn LLVMBuildBr(*const Builder, Dest: *const BasicBlock) *const Value;
414417
......@@ -451,7 +454,7 @@ pub const Builder = opaque {
451454 ) *const Value;
452455};
453456
454pub const IntPredicate = enum(c_int) {
457pub const IntPredicate = enum(c_uint) {
455458 EQ = 32,
456459 NE = 33,
457460 UGT = 34,
......@@ -464,6 +467,23 @@ pub const IntPredicate = enum(c_int) {
464467 SLE = 41,
465468};
466469
470pub const RealPredicate = enum(c_uint) {
471 OEQ = 1,
472 OGT = 2,
473 OGE = 3,
474 OLT = 4,
475 OLE = 5,
476 ONE = 6,
477 ORD = 7,
478 UNO = 8,
479 UEQ = 9,
480 UGT = 10,
481 UGE = 11,
482 ULT = 12,
483 ULE = 13,
484 UNE = 14,
485};
486
467487pub const BasicBlock = opaque {
468488 pub const deleteBasicBlock = LLVMDeleteBasicBlock;
469489 extern fn LLVMDeleteBasicBlock(BB: *const BasicBlock) void;
test/behavior/generics.zig+35-3
......@@ -64,9 +64,41 @@ fn sameButWithFloats(a: f64, b: f64) f64 {
6464test "fn with comptime args" {
6565 try expect(gimmeTheBigOne(1234, 5678) == 5678);
6666 try expect(shouldCallSameInstance(34, 12) == 34);
67 try expect(sameButWithFloats(0.43, 0.49) == 0.49);
68}
69
70test "anytype params" {
71 try expect(max_i32(12, 34) == 34);
72 try expect(max_f64(1.2, 3.4) == 3.4);
6773 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);
74 // TODO: stage2 is incorrectly hitting the following problem:
75 // error: unable to resolve comptime value
76 // return max_anytype(a, b);
77 // ^
78 comptime {
79 try expect(max_i32(12, 34) == 34);
80 try expect(max_f64(1.2, 3.4) == 3.4);
81 }
82 }
83}
84
85fn max_anytype(a: anytype, b: anytype) @TypeOf(a, b) {
86 if (!builtin.zig_is_stage2) {
87 // TODO: stage2 is incorrectly emitting AIR that allocates a result
88 // value, stores to it, but then returns void instead of the result.
89 return if (a > b) a else b;
7190 }
91 if (a > b) {
92 return a;
93 } else {
94 return b;
95 }
96}
97
98fn max_i32(a: i32, b: i32) i32 {
99 return max_anytype(a, b);
100}
101
102fn max_f64(a: f64, b: f64) f64 {
103 return max_anytype(a, b);
72104}
test/behavior/generics_stage1.zig-22
......@@ -3,28 +3,6 @@ const testing = std.testing;
33const expect = testing.expect;
44const expectEqual = testing.expectEqual;
55
6test "anytype params" {
7 try expect(max_i32(12, 34) == 34);
8 try expect(max_f64(1.2, 3.4) == 3.4);
9}
10
11test {
12 comptime try expect(max_i32(12, 34) == 34);
13 comptime try expect(max_f64(1.2, 3.4) == 3.4);
14}
15
16fn max_anytype(a: anytype, b: anytype) @TypeOf(a + b) {
17 return if (a > b) a else b;
18}
19
20fn max_i32(a: i32, b: i32) i32 {
21 return max_anytype(a, b);
22}
23
24fn max_f64(a: f64, b: f64) f64 {
25 return max_anytype(a, b);
26}
27
286pub fn List(comptime T: type) type {
297 return SmallList(T, 8);
308}