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 {...@@ -801,8 +801,9 @@ pub const Fn = struct {
801 /// The Decl that corresponds to the function itself.801 /// The Decl that corresponds to the function itself.
802 owner_decl: *Decl,802 owner_decl: *Decl,
803 /// If this is not null, this function is a generic function instantiation, and803 /// 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-comptime804 /// there is a `TypedValue` here for each parameter of the function.
805 /// parameters are marked with an `unreachable_value`.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.
806 comptime_args: ?[*]TypedValue = null,807 comptime_args: ?[*]TypedValue = null,
807 /// The ZIR instruction that is a function instruction. Use this to find808 /// The ZIR instruction that is a function instruction. Use this to find
808 /// the body. We store this rather than the body directly so that when ZIR809 /// 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 {...@@ -2975,6 +2976,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
2975 .owner_decl = new_decl,2976 .owner_decl = new_decl,
2976 .namespace = &struct_obj.namespace,2977 .namespace = &struct_obj.namespace,
2977 .func = null,2978 .func = null,
2979 .fn_ret_ty = Type.initTag(.void),
2978 .owner_func = null,2980 .owner_func = null,
2979 };2981 };
2980 defer sema.deinit();2982 defer sema.deinit();
...@@ -3029,6 +3031,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {...@@ -3029,6 +3031,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
3029 .owner_decl = decl,3031 .owner_decl = decl,
3030 .namespace = decl.namespace,3032 .namespace = decl.namespace,
3031 .func = null,3033 .func = null,
3034 .fn_ret_ty = Type.initTag(.void),
3032 .owner_func = null,3035 .owner_func = null,
3033 };3036 };
3034 defer sema.deinit();3037 defer sema.deinit();
...@@ -3712,6 +3715,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {...@@ -3712,6 +3715,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
3712 .owner_decl = decl,3715 .owner_decl = decl,
3713 .namespace = decl.namespace,3716 .namespace = decl.namespace,
3714 .func = func,3717 .func = func,
3718 .fn_ret_ty = func.owner_decl.ty.fnReturnType(),
3715 .owner_func = func,3719 .owner_func = func,
3716 };3720 };
3717 defer sema.deinit();3721 defer sema.deinit();
...@@ -3764,7 +3768,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {...@@ -3764,7 +3768,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
3764 };3768 };
3765 if (func.comptime_args) |comptime_args| {3769 if (func.comptime_args) |comptime_args| {
3766 const arg_tv = comptime_args[total_param_index];3770 const arg_tv = comptime_args[total_param_index];
3767 if (arg_tv.val.tag() != .unreachable_value) {3771 if (arg_tv.val.tag() != .generic_poison) {
3768 // We have a comptime value for this parameter.3772 // We have a comptime value for this parameter.
3769 const arg = try sema.addConstant(arg_tv.ty, arg_tv.val);3773 const arg = try sema.addConstant(arg_tv.ty, arg_tv.val);
3770 sema.inst_map.putAssumeCapacityNoClobber(inst, arg);3774 sema.inst_map.putAssumeCapacityNoClobber(inst, arg);
...@@ -4447,6 +4451,7 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) CompileError!void...@@ -4447,6 +4451,7 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) CompileError!void
4447 .namespace = &struct_obj.namespace,4451 .namespace = &struct_obj.namespace,
4448 .owner_func = null,4452 .owner_func = null,
4449 .func = null,4453 .func = null,
4454 .fn_ret_ty = Type.initTag(.void),
4450 };4455 };
4451 defer sema.deinit();4456 defer sema.deinit();
44524457
...@@ -4600,6 +4605,7 @@ pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) CompileError!void {...@@ -4600,6 +4605,7 @@ pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) CompileError!void {
4600 .namespace = &union_obj.namespace,4605 .namespace = &union_obj.namespace,
4601 .owner_func = null,4606 .owner_func = null,
4602 .func = null,4607 .func = null,
4608 .fn_ret_ty = Type.initTag(.void),
4603 };4609 };
4604 defer sema.deinit();4610 defer sema.deinit();
46054611
src/Sema.zig+212-107
...@@ -29,6 +29,12 @@ owner_func: ?*Module.Fn,...@@ -29,6 +29,12 @@ owner_func: ?*Module.Fn,
29/// This starts out the same as `owner_func` and then diverges in the case of29/// This starts out the same as `owner_func` and then diverges in the case of
30/// an inline or comptime function call.30/// an inline or comptime function call.
31func: ?*Module.Fn,31func: ?*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,
32branch_quota: u32 = 1000,38branch_quota: u32 = 1000,
33branch_count: u32 = 0,39branch_count: u32 = 0,
34/// This field is updated when a new source location becomes active, so that40/// This field is updated when a new source location becomes active, so that
...@@ -628,6 +634,7 @@ fn analyzeAsType(...@@ -628,6 +634,7 @@ fn analyzeAsType(
628634
629/// May return Value Tags: `variable`, `undef`.635/// May return Value Tags: `variable`, `undef`.
630/// See `resolveConstValue` for an alternative.636/// See `resolveConstValue` for an alternative.
637/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
631fn resolveValue(638fn resolveValue(
632 sema: *Sema,639 sema: *Sema,
633 block: *Scope.Block,640 block: *Scope.Block,
...@@ -679,6 +686,7 @@ fn resolveDefinedValue(...@@ -679,6 +686,7 @@ fn resolveDefinedValue(
679686
680/// Value Tag `variable` causes this function to return `null`.687/// Value Tag `variable` causes this function to return `null`.
681/// Value Tag `undef` causes this function to return the Value.688/// Value Tag `undef` causes this function to return the Value.
689/// Value Tag `generic_poison` causes `error.GenericPoison` to be returned.
682fn resolveMaybeUndefVal(690fn resolveMaybeUndefVal(
683 sema: *Sema,691 sema: *Sema,
684 block: *Scope.Block,692 block: *Scope.Block,
...@@ -686,10 +694,11 @@ fn resolveMaybeUndefVal(...@@ -686,10 +694,11 @@ fn resolveMaybeUndefVal(
686 inst: Air.Inst.Ref,694 inst: Air.Inst.Ref,
687) CompileError!?Value {695) CompileError!?Value {
688 const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) orelse return null;696 const val = (try sema.resolveMaybeUndefValAllowVariables(block, src, inst)) orelse return null;
689 if (val.tag() == .variable) {697 switch (val.tag()) {
690 return null;698 .variable => return null,
699 .generic_poison => return error.GenericPoison,
700 else => return val,
691 }701 }
692 return val;
693}702}
694703
695/// Returns all Value tags including `variable` and `undef`.704/// Returns all Value tags including `variable` and `undef`.
...@@ -1033,6 +1042,7 @@ fn zirEnumDecl(...@@ -1033,6 +1042,7 @@ fn zirEnumDecl(
1033 .namespace = &enum_obj.namespace,1042 .namespace = &enum_obj.namespace,
1034 .owner_func = null,1043 .owner_func = null,
1035 .func = null,1044 .func = null,
1045 .fn_ret_ty = Type.initTag(.void),
1036 .branch_quota = sema.branch_quota,1046 .branch_quota = sema.branch_quota,
1037 .branch_count = sema.branch_count,1047 .branch_count = sema.branch_count,
1038 };1048 };
...@@ -1238,9 +1248,7 @@ fn zirRetPtr(...@@ -1238,9 +1248,7 @@ fn zirRetPtr(
12381248
1239 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };1249 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
1240 try sema.requireFunctionBlock(block, src);1250 try sema.requireFunctionBlock(block, src);
1241 const fn_ty = sema.func.?.owner_decl.ty;1251 const ptr_type = try Module.simplePtrType(sema.arena, sema.fn_ret_ty, true, .One);
1242 const ret_type = fn_ty.fnReturnType();
1243 const ptr_type = try Module.simplePtrType(sema.arena, ret_type, true, .One);
1244 return block.addTy(.alloc, ptr_type);1252 return block.addTy(.alloc, ptr_type);
1245}1253}
12461254
...@@ -1263,9 +1271,7 @@ fn zirRetType(...@@ -1263,9 +1271,7 @@ fn zirRetType(
12631271
1264 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };1272 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
1265 try sema.requireFunctionBlock(block, src);1273 try sema.requireFunctionBlock(block, src);
1266 const fn_ty = sema.func.?.owner_decl.ty;1274 return sema.addType(sema.fn_ret_ty);
1267 const ret_type = fn_ty.fnReturnType();
1268 return sema.addType(ret_type);
1269}1275}
12701276
1271fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {1277fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
...@@ -2364,7 +2370,7 @@ const GenericCallAdapter = struct {...@@ -2364,7 +2370,7 @@ const GenericCallAdapter = struct {
2364 generic_fn: *Module.Fn,2370 generic_fn: *Module.Fn,
2365 precomputed_hash: u64,2371 precomputed_hash: u64,
2366 func_ty_info: Type.Payload.Function.Data,2372 func_ty_info: Type.Payload.Function.Data,
2367 comptime_vals: []const Value,2373 comptime_tvs: []const TypedValue,
23682374
2369 pub fn eql(ctx: @This(), adapted_key: void, other_key: *Module.Fn) bool {2375 pub fn eql(ctx: @This(), adapted_key: void, other_key: *Module.Fn) bool {
2370 _ = adapted_key;2376 _ = adapted_key;
...@@ -2373,12 +2379,22 @@ const GenericCallAdapter = struct {...@@ -2373,12 +2379,22 @@ const GenericCallAdapter = struct {
2373 const generic_owner_decl = other_key.owner_decl.dependencies.keys()[0];2379 const generic_owner_decl = other_key.owner_decl.dependencies.keys()[0];
2374 if (ctx.generic_fn.owner_decl != generic_owner_decl) return false;2380 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.
2378 const other_comptime_args = other_key.comptime_args.?;2382 const other_comptime_args = other_key.comptime_args.?;
2379 for (ctx.func_ty_info.param_types) |param_ty, i| {2383 for (other_comptime_args[0..ctx.func_ty_info.param_types.len]) |other_arg, i| {
2380 if (ctx.func_ty_info.paramIsComptime(i) and param_ty.tag() != .generic_poison) {2384 if (other_arg.ty.tag() != .generic_poison) {
2381 if (!ctx.comptime_vals[i].eql(other_comptime_args[i].val, param_ty)) {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)) {
2382 return false;2398 return false;
2383 }2399 }
2384 }2400 }
...@@ -2394,6 +2410,22 @@ const GenericCallAdapter = struct {...@@ -2394,6 +2410,22 @@ const GenericCallAdapter = struct {
2394 }2410 }
2395};2411};
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
2397fn analyzeCall(2429fn analyzeCall(
2398 sema: *Sema,2430 sema: *Sema,
2399 block: *Scope.Block,2431 block: *Scope.Block,
...@@ -2466,14 +2498,6 @@ fn analyzeCall(...@@ -2466,14 +2498,6 @@ fn analyzeCall(
2466 const is_inline_call = is_comptime_call or modifier == .always_inline or2498 const is_inline_call = is_comptime_call or modifier == .always_inline or
2467 func_ty_info.cc == .Inline;2499 func_ty_info.cc == .Inline;
2468 const result: Air.Inst.Ref = if (is_inline_call) res: {2500 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
2477 const func_val = try sema.resolveConstValue(block, func_src, func);2501 const func_val = try sema.resolveConstValue(block, func_src, func);
2478 const module_fn = switch (func_val.tag()) {2502 const module_fn = switch (func_val.tag()) {
2479 .function => func_val.castTag(.function).?.data,2503 .function => func_val.castTag(.function).?.data,
...@@ -2544,19 +2568,62 @@ fn analyzeCall(...@@ -2544,19 +2568,62 @@ fn analyzeCall(
2544 // This will have return instructions analyzed as break instructions to2568 // This will have return instructions analyzed as break instructions to
2545 // the block_inst above. Here we are performing "comptime/inline semantic analysis"2569 // the block_inst above. Here we are performing "comptime/inline semantic analysis"
2546 // for a function body, which means we must map the parameter ZIR instructions to2570 // 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.
2548 const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst);2574 const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst);
2549 const zir_tags = sema.code.instructions.items(.tag);2575 const zir_tags = sema.code.instructions.items(.tag);
2550 var arg_i: usize = 0;2576 var arg_i: usize = 0;
2551 try sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, args.len));2577 for (fn_info.param_body) |inst| switch (zir_tags[inst]) {
2552 for (fn_info.param_body) |inst| {2578 .param, .param_comptime => {
2553 switch (zir_tags[inst]) {2579 // Evaluate the parameter type expression now that previous ones have
2554 .param, .param_comptime, .param_anytype, .param_anytype_comptime => {},2580 // been mapped, and coerce the corresponding argument to it.
2555 else => continue,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 });
2556 }2620 }
2557 sema.inst_map.putAssumeCapacityNoClobber(inst, args[arg_i]);2621 break :blk bare_return_type;
2558 arg_i += 1;2622 };
2559 }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
2560 _ = try sema.analyzeBody(&child_block, fn_info.body);2627 _ = try sema.analyzeBody(&child_block, fn_info.body);
2561 break :res try sema.analyzeBlockBody(block, call_src, &child_block, merges);2628 break :res try sema.analyzeBlockBody(block, call_src, &child_block, merges);
2562 } else if (func_ty_info.is_generic) res: {2629 } else if (func_ty_info.is_generic) res: {
...@@ -2569,57 +2636,74 @@ fn analyzeCall(...@@ -2569,57 +2636,74 @@ fn analyzeCall(
2569 const fn_zir = namespace.file_scope.zir;2636 const fn_zir = namespace.file_scope.zir;
2570 const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);2637 const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);
2571 const zir_tags = fn_zir.instructions.items(.tag);2638 const zir_tags = fn_zir.instructions.items(.tag);
2572 const new_module_func = new_func: {2639
2573 // This hash must match `Module.MonomorphedFuncsContext.hash`.2640 // This hash must match `Module.MonomorphedFuncsContext.hash`.
2574 // For parameters explicitly marked comptime and simple parameter type expressions,2641 // For parameters explicitly marked comptime and simple parameter type expressions,
2575 // we know whether a parameter is elided from a monomorphed function, and can2642 // 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 not2643 // 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, we2644 // explicitly marked comptime and rely on previous parameter comptime values, we
2578 // don't find out until after generating a monomorphed function whether the parameter2645 // don't find out until after generating a monomorphed function whether the parameter
2579 // type ended up being a "must-be-comptime-known" type.2646 // type ended up being a "must-be-comptime-known" type.
2580 var hasher = std.hash.Wyhash.init(0);2647 var hasher = std.hash.Wyhash.init(0);
2581 std.hash.autoHash(&hasher, @ptrToInt(module_fn));2648 std.hash.autoHash(&hasher, @ptrToInt(module_fn));
25822649
2583 const comptime_vals = try sema.arena.alloc(Value, func_ty_info.param_types.len);2650 const comptime_tvs = try sema.arena.alloc(TypedValue, func_ty_info.param_types.len);
25842651
2585 for (func_ty_info.param_types) |param_ty, i| {2652 for (func_ty_info.param_types) |param_ty, i| {
2586 const is_comptime = func_ty_info.paramIsComptime(i);2653 const is_comptime = func_ty_info.paramIsComptime(i);
2587 if (is_comptime and param_ty.tag() != .generic_poison) {2654 if (is_comptime) {
2588 const arg_src = call_src; // TODO better source location2655 const arg_src = call_src; // TODO better source location
2589 const casted_arg = try sema.coerce(block, param_ty, uncasted_args[i], arg_src);2656 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| {2657 if (try sema.resolveMaybeUndefVal(block, arg_src, casted_arg)) |arg_val| {
2658 if (param_ty.tag() != .generic_poison) {
2591 arg_val.hash(param_ty, &hasher);2659 arg_val.hash(param_ty, &hasher);
2592 comptime_vals[i] = arg_val;
2593 } else {
2594 return sema.failWithNeededComptime(block, arg_src);
2595 }2660 }
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);
2596 }2668 }
2669 } else {
2670 comptime_tvs[i] = .{
2671 .ty = sema.typeOf(uncasted_args[i]),
2672 .val = Value.initTag(.generic_poison),
2673 };
2597 }2674 }
2675 }
25982676
2599 const adapter: GenericCallAdapter = .{2677 const precomputed_hash = hasher.final();
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 };
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;
2622 {2700 {
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
2623 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);2707 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
26242708
2625 // Create a Decl for the new function.2709 // Create a Decl for the new function.
...@@ -2658,6 +2742,7 @@ fn analyzeCall(...@@ -2658,6 +2742,7 @@ fn analyzeCall(
2658 .owner_decl = new_decl,2742 .owner_decl = new_decl,
2659 .namespace = namespace,2743 .namespace = namespace,
2660 .func = null,2744 .func = null,
2745 .fn_ret_ty = Type.initTag(.void),
2661 .owner_func = null,2746 .owner_func = null,
2662 .comptime_args = try new_decl_arena.allocator.alloc(TypedValue, uncasted_args.len),2747 .comptime_args = try new_decl_arena.allocator.alloc(TypedValue, uncasted_args.len),
2663 .comptime_args_fn_inst = module_fn.zir_body_inst,2748 .comptime_args_fn_inst = module_fn.zir_body_inst,
...@@ -2681,11 +2766,25 @@ fn analyzeCall(...@@ -2681,11 +2766,25 @@ fn analyzeCall(
2681 try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, uncasted_args.len));2766 try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, uncasted_args.len));
2682 var arg_i: usize = 0;2767 var arg_i: usize = 0;
2683 for (fn_info.param_body) |inst| {2768 for (fn_info.param_body) |inst| {
2684 const is_comptime = switch (zir_tags[inst]) {2769 var is_comptime = false;
2685 .param_comptime, .param_anytype_comptime => true,2770 var is_anytype = false;
2686 .param, .param_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 },
2687 else => continue,2786 else => continue,
2688 } or func_ty_info.paramIsComptime(arg_i);2787 }
2689 const arg_src = call_src; // TODO: better source location2788 const arg_src = call_src; // TODO: better source location
2690 const arg = uncasted_args[arg_i];2789 const arg = uncasted_args[arg_i];
2691 if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {2790 if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {
...@@ -2693,6 +2792,12 @@ fn analyzeCall(...@@ -2693,6 +2792,12 @@ fn analyzeCall(
2693 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);2792 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
2694 } else if (is_comptime) {2793 } else if (is_comptime) {
2695 return sema.failWithNeededComptime(block, arg_src);2794 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);
2696 }2801 }
2697 arg_i += 1;2802 arg_i += 1;
2698 }2803 }
...@@ -2710,17 +2815,10 @@ fn analyzeCall(...@@ -2710,17 +2815,10 @@ fn analyzeCall(
2710 const arg = child_sema.inst_map.get(inst).?;2815 const arg = child_sema.inst_map.get(inst).?;
2711 const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(&child_block, .unneeded, arg) catch unreachable).?;2816 const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(&child_block, .unneeded, arg) catch unreachable).?;
27122817
2713 if (arg_val.tag() == .generic_poison) {2818 child_sema.comptime_args[arg_i] = .{
2714 child_sema.comptime_args[arg_i] = .{2819 .ty = try child_sema.typeOf(arg).copy(&new_decl_arena.allocator),
2715 .ty = Type.initTag(.noreturn),2820 .val = try arg_val.copy(&new_decl_arena.allocator),
2716 .val = Value.initTag(.unreachable_value),2821 };
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 }
27242822
2725 arg_i += 1;2823 arg_i += 1;
2726 }2824 }
...@@ -2730,6 +2828,18 @@ fn analyzeCall(...@@ -2730,6 +2828,18 @@ fn analyzeCall(
2730 new_decl.val = try Value.Tag.function.create(&new_decl_arena.allocator, new_func);2828 new_decl.val = try Value.Tag.function.create(&new_decl_arena.allocator, new_func);
2731 new_decl.analysis = .complete;2829 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
2733 // The generic function Decl is guaranteed to be the first dependency2843 // The generic function Decl is guaranteed to be the first dependency
2734 // of each of its instantiations.2844 // of each of its instantiations.
2735 assert(new_decl.dependencies.keys().len == 0);2845 assert(new_decl.dependencies.keys().len == 0);
...@@ -2809,7 +2919,7 @@ fn finishGenericCall(...@@ -2809,7 +2919,7 @@ fn finishGenericCall(
2809 for (fn_info.param_body) |inst| {2919 for (fn_info.param_body) |inst| {
2810 switch (zir_tags[inst]) {2920 switch (zir_tags[inst]) {
2811 .param_comptime, .param_anytype_comptime, .param, .param_anytype => {2921 .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) {
2813 count += 1;2923 count += 1;
2814 }2924 }
2815 arg_i += 1;2925 arg_i += 1;
...@@ -2829,7 +2939,7 @@ fn finishGenericCall(...@@ -2829,7 +2939,7 @@ fn finishGenericCall(
2829 .param_comptime, .param_anytype_comptime, .param, .param_anytype => {},2939 .param_comptime, .param_anytype_comptime, .param, .param_anytype => {},
2830 else => continue,2940 else => continue,
2831 }2941 }
2832 const is_runtime = comptime_args[total_i].val.tag() == .unreachable_value;2942 const is_runtime = comptime_args[total_i].val.tag() == .generic_poison;
2833 if (is_runtime) {2943 if (is_runtime) {
2834 const param_ty = new_fn_ty.fnParamType(runtime_i);2944 const param_ty = new_fn_ty.fnParamType(runtime_i);
2835 const arg_src = call_src; // TODO: better source location2945 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...@@ -6162,28 +6272,23 @@ fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
6162fn analyzeRet(6272fn analyzeRet(
6163 sema: *Sema,6273 sema: *Sema,
6164 block: *Scope.Block,6274 block: *Scope.Block,
6165 operand: Air.Inst.Ref,6275 uncasted_operand: Air.Inst.Ref,
6166 src: LazySrcLoc,6276 src: LazySrcLoc,
6167 need_coercion: bool,6277 need_coercion: bool,
6168) CompileError!Zir.Inst.Index {6278) CompileError!Zir.Inst.Index {
6169 const casted_operand = if (!need_coercion) operand else op: {6279 const operand = if (!need_coercion)
6170 const func = sema.func.?;6280 uncasted_operand
6171 const fn_ty = func.owner_decl.ty;6281 else
6172 // TODO: In the case of a comptime/inline function call of a generic function,6282 try sema.coerce(block, sema.fn_ret_ty, uncasted_operand, src);
6173 // this needs to be the resolved return type based on the function parameter type6283
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 };
6179 if (block.inlining) |inlining| {6284 if (block.inlining) |inlining| {
6180 // We are inlining a function call; rewrite the `ret` as a `break`.6285 // We are inlining a function call; rewrite the `ret` as a `break`.
6181 try inlining.merges.results.append(sema.gpa, casted_operand);6286 try inlining.merges.results.append(sema.gpa, operand);
6182 _ = try block.addBr(inlining.merges.block_inst, casted_operand);6287 _ = try block.addBr(inlining.merges.block_inst, operand);
6183 return always_noreturn;6288 return always_noreturn;
6184 }6289 }
61856290
6186 _ = try block.addUnOp(.ret, casted_operand);6291 _ = try block.addUnOp(.ret, operand);
6187 return always_noreturn;6292 return always_noreturn;
6188}6293}
61896294
src/codegen/llvm.zig+26-15
...@@ -1093,21 +1093,32 @@ pub const FuncGen = struct {...@@ -1093,21 +1093,32 @@ pub const FuncGen = struct {
1093 const rhs = try self.resolveInst(bin_op.rhs);1093 const rhs = try self.resolveInst(bin_op.rhs);
1094 const inst_ty = self.air.typeOfIndex(inst);1094 const inst_ty = self.air.typeOfIndex(inst);
10951095
1096 if (!inst_ty.isInt())1096 switch (self.air.typeOf(bin_op.lhs).zigTypeTag()) {
1097 if (inst_ty.tag() != .bool)1097 .Int, .Bool, .Pointer => {
1098 return self.todo("implement 'airCmp' for type {}", .{inst_ty});1098 const is_signed = inst_ty.isSignedInt();
10991099 const operation = switch (op) {
1100 const is_signed = inst_ty.isSignedInt();1100 .eq => .EQ,
1101 const operation = switch (op) {1101 .neq => .NE,
1102 .eq => .EQ,1102 .lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT),
1103 .neq => .NE,1103 .lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE),
1104 .lt => @as(llvm.IntPredicate, if (is_signed) .SLT else .ULT),1104 .gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT),
1105 .lte => @as(llvm.IntPredicate, if (is_signed) .SLE else .ULE),1105 .gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE),
1106 .gt => @as(llvm.IntPredicate, if (is_signed) .SGT else .UGT),1106 };
1107 .gte => @as(llvm.IntPredicate, if (is_signed) .SGE else .UGE),1107 return self.builder.buildICmp(operation, lhs, rhs, "");
1108 };1108 },
11091109 .Float => {
1110 return self.builder.buildICmp(operation, lhs, rhs, "");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 }
1111 }1122 }
11121123
1113 fn airBlock(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {1124 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 {...@@ -409,6 +409,9 @@ pub const Builder = opaque {
409 pub const buildICmp = LLVMBuildICmp;409 pub const buildICmp = LLVMBuildICmp;
410 extern fn LLVMBuildICmp(*const Builder, Op: IntPredicate, LHS: *const Value, RHS: *const Value, Name: [*:0]const u8) *const Value;410 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
412 pub const buildBr = LLVMBuildBr;415 pub const buildBr = LLVMBuildBr;
413 extern fn LLVMBuildBr(*const Builder, Dest: *const BasicBlock) *const Value;416 extern fn LLVMBuildBr(*const Builder, Dest: *const BasicBlock) *const Value;
414417
...@@ -451,7 +454,7 @@ pub const Builder = opaque {...@@ -451,7 +454,7 @@ pub const Builder = opaque {
451 ) *const Value;454 ) *const Value;
452};455};
453456
454pub const IntPredicate = enum(c_int) {457pub const IntPredicate = enum(c_uint) {
455 EQ = 32,458 EQ = 32,
456 NE = 33,459 NE = 33,
457 UGT = 34,460 UGT = 34,
...@@ -464,6 +467,23 @@ pub const IntPredicate = enum(c_int) {...@@ -464,6 +467,23 @@ pub const IntPredicate = enum(c_int) {
464 SLE = 41,467 SLE = 41,
465};468};
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
467pub const BasicBlock = opaque {487pub const BasicBlock = opaque {
468 pub const deleteBasicBlock = LLVMDeleteBasicBlock;488 pub const deleteBasicBlock = LLVMDeleteBasicBlock;
469 extern fn LLVMDeleteBasicBlock(BB: *const BasicBlock) void;489 extern fn LLVMDeleteBasicBlock(BB: *const BasicBlock) void;
test/behavior/generics.zig+35-3
...@@ -64,9 +64,41 @@ fn sameButWithFloats(a: f64, b: f64) f64 {...@@ -64,9 +64,41 @@ fn sameButWithFloats(a: f64, b: f64) f64 {
64test "fn with comptime args" {64test "fn with comptime args" {
65 try expect(gimmeTheBigOne(1234, 5678) == 5678);65 try expect(gimmeTheBigOne(1234, 5678) == 5678);
66 try expect(shouldCallSameInstance(34, 12) == 34);66 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);
67 if (!builtin.zig_is_stage2) {73 if (!builtin.zig_is_stage2) {
68 // TODO: stage2 llvm backend needs to use fcmp instead of icmp74 // TODO: stage2 is incorrectly hitting the following problem:
69 // probably AIR should just have different instructions for floats.75 // error: unable to resolve comptime value
70 try expect(sameButWithFloats(0.43, 0.49) == 0.49);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;
71 }90 }
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);
72}104}
test/behavior/generics_stage1.zig-22
...@@ -3,28 +3,6 @@ const testing = std.testing;...@@ -3,28 +3,6 @@ const testing = std.testing;
3const expect = testing.expect;3const expect = testing.expect;
4const expectEqual = testing.expectEqual;4const 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
28pub fn List(comptime T: type) type {6pub fn List(comptime T: type) type {
29 return SmallList(T, 8);7 return SmallList(T, 8);
30}8}