authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-05 16:37:21-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-05 16:37:21-07:00
loge9e3a2994696a3131125ebc4b1f0eec7ca5306d9
treede6bf27daddd48b9ab15144312f558d3cd0c53c2
parentf58cbef1659742e57377d3f8c92a0b9b97af91ad

stage2: implement generic function memoization

Module has a new field `monomorphed_funcs` which stores the set of `*Module.Fn` objects which are generic function instantiations. The hash is based on hashes of comptime values of parameters known to be comptime based on an explicit comptime keyword or must-be-comptime type expressions that can be evaluated without performing monomorphization. This allows function calls to be semantically analyzed cheaply for generic functions which are already instantiated. The table is updated with a single `getOrPutAdapted` in the semantic analysis of `call` instructions, by pre-allocating the `Fn` object and passing it to the child `Sema`.

10 files changed, 679 insertions(+), 493 deletions(-)

src/Module.zig+44
......@@ -61,6 +61,11 @@ export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
6161/// Keys are fully resolved file paths. This table owns the keys and values.
6262import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},
6363
64/// The set of all the generic function instantiations. This is used so that when a generic
65/// function is called twice with the same comptime parameter arguments, both calls dispatch
66/// to the same function.
67monomorphed_funcs: MonomorphedFuncsSet = .{},
68
6469/// We optimize memory usage for a compilation with no compile errors by storing the
6570/// error messages and mapping outside of `Decl`.
6671/// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator.
......@@ -114,6 +119,44 @@ emit_h: ?*GlobalEmitH,
114119
115120test_functions: std.AutoArrayHashMapUnmanaged(*Decl, void) = .{},
116121
122const MonomorphedFuncsSet = std.HashMapUnmanaged(
123 *Fn,
124 void,
125 MonomorphedFuncsContext,
126 std.hash_map.default_max_load_percentage,
127);
128
129const MonomorphedFuncsContext = struct {
130 pub fn eql(ctx: @This(), a: *Fn, b: *Fn) bool {
131 _ = ctx;
132 return a == b;
133 }
134
135 /// Must match `Sema.GenericCallAdapter.hash`.
136 pub fn hash(ctx: @This(), key: *Fn) u64 {
137 _ = ctx;
138 var hasher = std.hash.Wyhash.init(0);
139
140 // The generic function Decl is guaranteed to be the first dependency
141 // of each of its instantiations.
142 const generic_owner_decl = key.owner_decl.dependencies.keys()[0];
143 const generic_func = generic_owner_decl.val.castTag(.function).?.data;
144 std.hash.autoHash(&hasher, @ptrToInt(generic_func));
145
146 // This logic must be kept in sync with the logic in `analyzeCall` that
147 // computes the hash.
148 const comptime_args = key.comptime_args.?;
149 const generic_ty_info = generic_owner_decl.ty.fnInfo();
150 for (generic_ty_info.param_types) |param_ty, i| {
151 if (generic_ty_info.paramIsComptime(i) and param_ty.tag() != .generic_poison) {
152 comptime_args[i].val.hash(param_ty, &hasher);
153 }
154 }
155
156 return hasher.final();
157 }
158};
159
117160/// A `Module` has zero or one of these depending on whether `-femit-h` is enabled.
118161pub const GlobalEmitH = struct {
119162 /// Where to put the output.
......@@ -2205,6 +2248,7 @@ pub fn deinit(mod: *Module) void {
22052248
22062249 mod.error_name_list.deinit(gpa);
22072250 mod.test_functions.deinit(gpa);
2251 mod.monomorphed_funcs.deinit(gpa);
22082252}
22092253
22102254fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
src/Sema.zig+288-172
......@@ -46,6 +46,12 @@ comptime_args: []TypedValue = &.{},
4646/// don't accidentally apply it to a function prototype which is used in the
4747/// type expression of a generic function parameter.
4848comptime_args_fn_inst: Zir.Inst.Index = 0,
49/// When `comptime_args` is provided, this field is also provided. It was used as
50/// the key in the `monomorphed_funcs` set. The `func` instruction is supposed
51/// to use this instead of allocating a fresh one. This avoids an unnecessary
52/// extra hash table lookup in the `monomorphed_funcs` set.
53/// Sema will set this to null when it takes ownership.
54preallocated_new_func: ?*Module.Fn = null,
4955
5056const std = @import("std");
5157const mem = std.mem;
......@@ -2354,6 +2360,40 @@ fn zirCall(
23542360 return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args);
23552361}
23562362
2363const GenericCallAdapter = struct {
2364 generic_fn: *Module.Fn,
2365 precomputed_hash: u64,
2366 func_ty_info: Type.Payload.Function.Data,
2367 comptime_vals: []const Value,
2368
2369 pub fn eql(ctx: @This(), adapted_key: void, other_key: *Module.Fn) bool {
2370 _ = adapted_key;
2371 // The generic function Decl is guaranteed to be the first dependency
2372 // of each of its instantiations.
2373 const generic_owner_decl = other_key.owner_decl.dependencies.keys()[0];
2374 if (ctx.generic_fn.owner_decl != generic_owner_decl) return false;
2375
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.?;
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)) {
2382 return false;
2383 }
2384 }
2385 }
2386 return true;
2387 }
2388
2389 /// The implementation of the hash is in semantic analysis of function calls, so
2390 /// that any errors when computing the hash can be properly reported.
2391 pub fn hash(ctx: @This(), adapted_key: void) u64 {
2392 _ = adapted_key;
2393 return ctx.precomputed_hash;
2394 }
2395};
2396
23572397fn analyzeCall(
23582398 sema: *Sema,
23592399 block: *Scope.Block,
......@@ -2524,193 +2564,192 @@ fn analyzeCall(
25242564 // Check the Module's generic function map with an adapted context, so that we
25252565 // can match against `uncasted_args` rather than doing the work below to create a
25262566 // generic Scope only to junk it if it matches an existing instantiation.
2527 // TODO
2528
25292567 const namespace = module_fn.owner_decl.namespace;
25302568 const fn_zir = namespace.file_scope.zir;
25312569 const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);
25322570 const zir_tags = fn_zir.instructions.items(.tag);
2533 const new_func = new_func: {
2534 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
2535
2536 // Create a Decl for the new function.
2537 const new_decl = try mod.allocateNewDecl(namespace, module_fn.owner_decl.src_node);
2538 // TODO better names for generic function instantiations
2539 const name_index = mod.getNextAnonNameIndex();
2540 new_decl.name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{
2541 module_fn.owner_decl.name, name_index,
2542 });
2543 new_decl.src_line = module_fn.owner_decl.src_line;
2544 new_decl.is_pub = module_fn.owner_decl.is_pub;
2545 new_decl.is_exported = module_fn.owner_decl.is_exported;
2546 new_decl.has_align = module_fn.owner_decl.has_align;
2547 new_decl.has_linksection = module_fn.owner_decl.has_linksection;
2548 new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index;
2549 new_decl.alive = true; // This Decl is called at runtime.
2550 new_decl.has_tv = true;
2551 new_decl.owns_tv = true;
2552 new_decl.analysis = .in_progress;
2553 new_decl.generation = mod.generation;
2554
2555 namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {});
2556
2557 var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
2558 errdefer new_decl_arena.deinit();
2559
2560 // Re-run the block that creates the function, with the comptime parameters
2561 // pre-populated inside `inst_map`. This causes `param_comptime` and
2562 // `param_anytype_comptime` ZIR instructions to be ignored, resulting in a
2563 // new, monomorphized function, with the comptime parameters elided.
2564 var child_sema: Sema = .{
2565 .mod = mod,
2566 .gpa = gpa,
2567 .arena = sema.arena,
2568 .code = fn_zir,
2569 .owner_decl = new_decl,
2570 .namespace = namespace,
2571 .func = null,
2572 .owner_func = null,
2573 .comptime_args = try new_decl_arena.allocator.alloc(TypedValue, uncasted_args.len),
2574 .comptime_args_fn_inst = module_fn.zir_body_inst,
2575 };
2576 defer child_sema.deinit();
2577
2578 var child_block: Scope.Block = .{
2579 .parent = null,
2580 .sema = &child_sema,
2581 .src_decl = new_decl,
2582 .instructions = .{},
2583 .inlining = null,
2584 .is_comptime = true,
2585 };
2586 defer {
2587 child_block.instructions.deinit(gpa);
2588 child_block.params.deinit(gpa);
2589 }
2590
2591 try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, uncasted_args.len));
2592 var arg_i: usize = 0;
2593 for (fn_info.param_body) |inst| {
2594 const is_comptime = switch (zir_tags[inst]) {
2595 .param_comptime, .param_anytype_comptime => true,
2596 .param, .param_anytype => false,
2597 else => continue,
2598 };
2599 // TODO: pass .unneeded to resolveConstValue and then if we get
2600 // error.NeededSourceLocation resolve the arg source location and
2601 // try again.
2602 const arg_src = call_src;
2603 const arg = uncasted_args[arg_i];
2604 if (try sema.resolveMaybeUndefVal(block, arg_src, arg)) |arg_val| {
2605 const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
2606 child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
2607 } else if (is_comptime) {
2608 return sema.failWithNeededComptime(block, arg_src);
2571 const new_module_func = new_func: {
2572 // This hash must match `Module.MonomorphedFuncsContext.hash`.
2573 // For parameters explicitly marked comptime and simple parameter type expressions,
2574 // we know whether a parameter is elided from a monomorphed function, and can
2575 // use it in the hash here. However, for parameter type expressions that are not
2576 // explicitly marked comptime and rely on previous parameter comptime values, we
2577 // don't find out until after generating a monomorphed function whether the parameter
2578 // type ended up being a "must-be-comptime-known" type.
2579 var hasher = std.hash.Wyhash.init(0);
2580 std.hash.autoHash(&hasher, @ptrToInt(module_fn));
2581
2582 const comptime_vals = try sema.arena.alloc(Value, func_ty_info.param_types.len);
2583
2584 for (func_ty_info.param_types) |param_ty, i| {
2585 const is_comptime = func_ty_info.paramIsComptime(i);
2586 if (is_comptime and param_ty.tag() != .generic_poison) {
2587 const arg_src = call_src; // TODO better source location
2588 const casted_arg = try sema.coerce(block, param_ty, uncasted_args[i], arg_src);
2589 if (try sema.resolveMaybeUndefVal(block, arg_src, casted_arg)) |arg_val| {
2590 arg_val.hash(param_ty, &hasher);
2591 comptime_vals[i] = arg_val;
2592 } else {
2593 return sema.failWithNeededComptime(block, arg_src);
2594 }
26092595 }
2610 arg_i += 1;
26112596 }
2612 const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body);
2613 const new_func_val = try child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst);
2614 const new_func = new_func_val.castTag(.function).?.data;
2615
2616 arg_i = 0;
2617 for (fn_info.param_body) |inst| {
2618 switch (zir_tags[inst]) {
2619 .param_comptime, .param_anytype_comptime, .param, .param_anytype => {},
2620 else => continue,
2621 }
2622 const arg = child_sema.inst_map.get(inst).?;
2623 const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(&child_block, .unneeded, arg) catch unreachable).?;
26242597
2625 if (arg_val.tag() == .generic_poison) {
2626 child_sema.comptime_args[arg_i] = .{
2627 .ty = Type.initTag(.noreturn),
2628 .val = Value.initTag(.unreachable_value),
2629 };
2630 } else {
2631 child_sema.comptime_args[arg_i] = .{
2632 .ty = try child_sema.typeOf(arg).copy(&new_decl_arena.allocator),
2633 .val = try arg_val.copy(&new_decl_arena.allocator),
2634 };
2635 }
2636
2637 arg_i += 1;
2598 const adapter: GenericCallAdapter = .{
2599 .generic_fn = module_fn,
2600 .precomputed_hash = hasher.final(),
2601 .func_ty_info = func_ty_info,
2602 .comptime_vals = comptime_vals,
2603 };
2604 const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter);
2605 if (gop.found_existing) {
2606 const callee_func = gop.key_ptr.*;
2607 break :res try sema.finishGenericCall(
2608 block,
2609 call_src,
2610 callee_func,
2611 func_src,
2612 uncasted_args,
2613 fn_info,
2614 zir_tags,
2615 );
26382616 }
2617 gop.key_ptr.* = try gpa.create(Module.Fn);
2618 break :new_func gop.key_ptr.*;
2619 };
26392620
2640 // Populate the Decl ty/val with the function and its type.
2641 new_decl.ty = try child_sema.typeOf(new_func_inst).copy(&new_decl_arena.allocator);
2642 new_decl.val = try Value.Tag.function.create(&new_decl_arena.allocator, new_func);
2643 new_decl.analysis = .complete;
2621 try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
26442622
2645 // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field
2646 // will be populated, ensuring it will have `analyzeBody` called with the ZIR
2647 // parameters mapped appropriately.
2648 try mod.comp.bin_file.allocateDeclIndexes(new_decl);
2649 try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func });
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();
26502665
2651 try new_decl.finalizeNewArena(&new_decl_arena);
2652 break :new_func try sema.analyzeDeclVal(block, func_src, new_decl);
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,
26532673 };
2674 defer {
2675 child_block.instructions.deinit(gpa);
2676 child_block.params.deinit(gpa);
2677 }
26542678
2655 // Save it into the Module's generic function map.
2656 // TODO
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);
2694 }
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);
26572701
2658 // Make a runtime call to the new function, making sure to omit the comptime args.
2659 try sema.requireRuntimeBlock(block, call_src);
2660 const new_func_val = sema.resolveConstValue(block, .unneeded, new_func) catch unreachable;
2661 const new_module_func = new_func_val.castTag(.function).?.data;
2662 const comptime_args = new_module_func.comptime_args.?;
2663 const runtime_args_len = count: {
2664 var count: u32 = 0;
2665 var arg_i: usize = 0;
2666 for (fn_info.param_body) |inst| {
2667 switch (zir_tags[inst]) {
2668 .param_comptime, .param_anytype_comptime, .param, .param_anytype => {
2669 if (comptime_args[arg_i].val.tag() == .unreachable_value) {
2670 count += 1;
2671 }
2672 arg_i += 1;
2673 },
2674 else => continue,
2675 }
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,
26762707 }
2677 break :count count;
2678 };
2679 const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len);
2680 {
2681 const new_fn_ty = new_module_func.owner_decl.ty;
2682 var runtime_i: u32 = 0;
2683 var total_i: u32 = 0;
2684 for (fn_info.param_body) |inst| {
2685 switch (zir_tags[inst]) {
2686 .param_comptime, .param_anytype_comptime, .param, .param_anytype => {},
2687 else => continue,
2688 }
2689 const is_runtime = comptime_args[total_i].val.tag() == .unreachable_value;
2690 if (is_runtime) {
2691 const param_ty = new_fn_ty.fnParamType(runtime_i);
2692 const arg_src = call_src; // TODO: better source location
2693 const uncasted_arg = uncasted_args[total_i];
2694 const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src);
2695 runtime_args[runtime_i] = casted_arg;
2696 runtime_i += 1;
2697 }
2698 total_i += 1;
2708 const arg = child_sema.inst_map.get(inst).?;
2709 const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(&child_block, .unneeded, arg) catch unreachable).?;
2710
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 };
26992721 }
2722
2723 arg_i += 1;
27002724 }
2701 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
2702 runtime_args_len);
2703 const func_inst = try block.addInst(.{
2704 .tag = .call,
2705 .data = .{ .pl_op = .{
2706 .operand = new_func,
2707 .payload = sema.addExtraAssumeCapacity(Air.Call{
2708 .args_len = runtime_args_len,
2709 }),
2710 } },
2711 });
2712 sema.appendRefsAssumeCapacity(runtime_args);
2713 break :res func_inst;
2725
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;
2730
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 });
2736
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
2744 break :res try sema.finishGenericCall(
2745 block,
2746 call_src,
2747 new_module_func,
2748 func_src,
2749 uncasted_args,
2750 fn_info,
2751 zir_tags,
2752 );
27142753 } else res: {
27152754 const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len);
27162755 for (uncasted_args) |uncasted_arg, i| {
......@@ -2745,6 +2784,75 @@ fn analyzeCall(
27452784 return result;
27462785}
27472786
2787fn finishGenericCall(
2788 sema: *Sema,
2789 block: *Scope.Block,
2790 call_src: LazySrcLoc,
2791 callee: *Module.Fn,
2792 func_src: LazySrcLoc,
2793 uncasted_args: []const Air.Inst.Ref,
2794 fn_info: Zir.FnInfo,
2795 zir_tags: []const Zir.Inst.Tag,
2796) CompileError!Air.Inst.Ref {
2797 const callee_inst = try sema.analyzeDeclVal(block, func_src, callee.owner_decl);
2798
2799 // Make a runtime call to the new function, making sure to omit the comptime args.
2800 try sema.requireRuntimeBlock(block, call_src);
2801
2802 const comptime_args = callee.comptime_args.?;
2803 const runtime_args_len = count: {
2804 var count: u32 = 0;
2805 var arg_i: usize = 0;
2806 for (fn_info.param_body) |inst| {
2807 switch (zir_tags[inst]) {
2808 .param_comptime, .param_anytype_comptime, .param, .param_anytype => {
2809 if (comptime_args[arg_i].val.tag() == .unreachable_value) {
2810 count += 1;
2811 }
2812 arg_i += 1;
2813 },
2814 else => continue,
2815 }
2816 }
2817 break :count count;
2818 };
2819 const runtime_args = try sema.arena.alloc(Air.Inst.Ref, runtime_args_len);
2820 {
2821 const new_fn_ty = callee.owner_decl.ty;
2822 var runtime_i: u32 = 0;
2823 var total_i: u32 = 0;
2824 for (fn_info.param_body) |inst| {
2825 switch (zir_tags[inst]) {
2826 .param_comptime, .param_anytype_comptime, .param, .param_anytype => {},
2827 else => continue,
2828 }
2829 const is_runtime = comptime_args[total_i].val.tag() == .unreachable_value;
2830 if (is_runtime) {
2831 const param_ty = new_fn_ty.fnParamType(runtime_i);
2832 const arg_src = call_src; // TODO: better source location
2833 const uncasted_arg = uncasted_args[total_i];
2834 const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src);
2835 runtime_args[runtime_i] = casted_arg;
2836 runtime_i += 1;
2837 }
2838 total_i += 1;
2839 }
2840 }
2841 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
2842 runtime_args_len);
2843 const func_inst = try block.addInst(.{
2844 .tag = .call,
2845 .data = .{ .pl_op = .{
2846 .operand = callee_inst,
2847 .payload = sema.addExtraAssumeCapacity(Air.Call{
2848 .args_len = runtime_args_len,
2849 }),
2850 } },
2851 });
2852 sema.appendRefsAssumeCapacity(runtime_args);
2853 return func_inst;
2854}
2855
27482856fn zirIntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
27492857 _ = block;
27502858 const tracy = trace(@src());
......@@ -3419,7 +3527,15 @@ fn funcCommon(
34193527
34203528 const mod = sema.mod;
34213529
3422 const new_func = if (body_inst == 0) undefined else try sema.gpa.create(Module.Fn);
3530 const new_func: *Module.Fn = new_func: {
3531 if (body_inst == 0) break :new_func undefined;
3532 if (sema.comptime_args_fn_inst == body_inst) {
3533 const new_func = sema.preallocated_new_func.?;
3534 sema.preallocated_new_func = null; // take ownership
3535 break :new_func new_func;
3536 }
3537 break :new_func try sema.gpa.create(Module.Fn);
3538 };
34233539 errdefer if (body_inst != 0) sema.gpa.destroy(new_func);
34243540
34253541 const fn_ty: Type = fn_ty: {
......@@ -3620,7 +3736,7 @@ fn zirParam(
36203736
36213737 try block.params.append(sema.gpa, .{
36223738 .ty = param_ty,
3623 .is_comptime = is_comptime,
3739 .is_comptime = is_comptime or param_ty.requiresComptime(),
36243740 });
36253741 const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison));
36263742 try sema.inst_map.putNoClobber(sema.gpa, inst, result);
src/Zir.zig+4-2
......@@ -4930,11 +4930,13 @@ fn findDeclsBody(
49304930 }
49314931}
49324932
4933pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) struct {
4933pub const FnInfo = struct {
49344934 param_body: []const Inst.Index,
49354935 body: []const Inst.Index,
49364936 total_params_len: u32,
4937} {
4937};
4938
4939pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
49384940 const tags = zir.instructions.items(.tag);
49394941 const datas = zir.instructions.items(.data);
49404942 const info: struct {
src/type.zig+19-15
......@@ -549,8 +549,13 @@ pub const Type = extern union {
549549
550550 pub fn hash(self: Type) u64 {
551551 var hasher = std.hash.Wyhash.init(0);
552 self.hashWithHasher(&hasher);
553 return hasher.final();
554 }
555
556 pub fn hashWithHasher(self: Type, hasher: *std.hash.Wyhash) void {
552557 const zig_type_tag = self.zigTypeTag();
553 std.hash.autoHash(&hasher, zig_type_tag);
558 std.hash.autoHash(hasher, zig_type_tag);
554559 switch (zig_type_tag) {
555560 .Type,
556561 .Void,
......@@ -568,34 +573,34 @@ pub const Type = extern union {
568573 .Int => {
569574 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
570575 if (self.isNamedInt()) {
571 std.hash.autoHash(&hasher, self.tag());
576 std.hash.autoHash(hasher, self.tag());
572577 } else {
573578 // Remaining cases are arbitrary sized integers.
574579 // The target will not be branched upon, because we handled target-dependent cases above.
575580 const info = self.intInfo(@as(Target, undefined));
576 std.hash.autoHash(&hasher, info.signedness);
577 std.hash.autoHash(&hasher, info.bits);
581 std.hash.autoHash(hasher, info.signedness);
582 std.hash.autoHash(hasher, info.bits);
578583 }
579584 },
580585 .Array, .Vector => {
581 std.hash.autoHash(&hasher, self.arrayLen());
582 std.hash.autoHash(&hasher, self.elemType().hash());
586 std.hash.autoHash(hasher, self.arrayLen());
587 std.hash.autoHash(hasher, self.elemType().hash());
583588 // TODO hash array sentinel
584589 },
585590 .Fn => {
586 std.hash.autoHash(&hasher, self.fnReturnType().hash());
587 std.hash.autoHash(&hasher, self.fnCallingConvention());
591 std.hash.autoHash(hasher, self.fnReturnType().hash());
592 std.hash.autoHash(hasher, self.fnCallingConvention());
588593 const params_len = self.fnParamLen();
589 std.hash.autoHash(&hasher, params_len);
594 std.hash.autoHash(hasher, params_len);
590595 var i: usize = 0;
591596 while (i < params_len) : (i += 1) {
592 std.hash.autoHash(&hasher, self.fnParamType(i).hash());
597 std.hash.autoHash(hasher, self.fnParamType(i).hash());
593598 }
594 std.hash.autoHash(&hasher, self.fnIsVarArgs());
599 std.hash.autoHash(hasher, self.fnIsVarArgs());
595600 },
596601 .Optional => {
597602 var buf: Payload.ElemType = undefined;
598 std.hash.autoHash(&hasher, self.optionalChild(&buf).hash());
603 std.hash.autoHash(hasher, self.optionalChild(&buf).hash());
599604 },
600605 .Float,
601606 .Struct,
......@@ -612,7 +617,6 @@ pub const Type = extern union {
612617 // TODO implement more type hashing
613618 },
614619 }
615 return hasher.final();
616620 }
617621
618622 pub const HashContext64 = struct {
......@@ -3373,7 +3377,7 @@ pub const Type = extern union {
33733377 data: Data,
33743378
33753379 // TODO look into optimizing this memory to take fewer bytes
3376 const Data = struct {
3380 pub const Data = struct {
33773381 param_types: []Type,
33783382 comptime_params: [*]bool,
33793383 return_type: Type,
......@@ -3381,7 +3385,7 @@ pub const Type = extern union {
33813385 is_var_args: bool,
33823386 is_generic: bool,
33833387
3384 fn paramIsComptime(self: @This(), i: usize) bool {
3388 pub fn paramIsComptime(self: @This(), i: usize) bool {
33853389 if (!self.is_generic) return false;
33863390 assert(i < self.param_types.len);
33873391 return self.comptime_params[i];
src/value.zig+74-71
......@@ -1117,12 +1117,82 @@ pub const Value = extern union {
11171117 return order(a, b).compare(.eq);
11181118 }
11191119
1120 pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash) void {
1121 switch (ty.zigTypeTag()) {
1122 .BoundFn => unreachable, // TODO remove this from the language
1123
1124 .Void,
1125 .NoReturn,
1126 .Undefined,
1127 .Null,
1128 => {},
1129
1130 .Type => {
1131 var buf: ToTypeBuffer = undefined;
1132 return val.toType(&buf).hashWithHasher(hasher);
1133 },
1134 .Bool => {
1135 std.hash.autoHash(hasher, val.toBool());
1136 },
1137 .Int, .ComptimeInt => {
1138 var space: BigIntSpace = undefined;
1139 const big = val.toBigInt(&space);
1140 std.hash.autoHash(hasher, big.positive);
1141 for (big.limbs) |limb| {
1142 std.hash.autoHash(hasher, limb);
1143 }
1144 },
1145 .Float, .ComptimeFloat => {
1146 @panic("TODO implement hashing float values");
1147 },
1148 .Pointer => {
1149 @panic("TODO implement hashing pointer values");
1150 },
1151 .Array, .Vector => {
1152 @panic("TODO implement hashing array/vector values");
1153 },
1154 .Struct => {
1155 @panic("TODO implement hashing struct values");
1156 },
1157 .Optional => {
1158 @panic("TODO implement hashing optional values");
1159 },
1160 .ErrorUnion => {
1161 @panic("TODO implement hashing error union values");
1162 },
1163 .ErrorSet => {
1164 @panic("TODO implement hashing error set values");
1165 },
1166 .Enum => {
1167 @panic("TODO implement hashing enum values");
1168 },
1169 .Union => {
1170 @panic("TODO implement hashing union values");
1171 },
1172 .Fn => {
1173 @panic("TODO implement hashing function values");
1174 },
1175 .Opaque => {
1176 @panic("TODO implement hashing opaque values");
1177 },
1178 .Frame => {
1179 @panic("TODO implement hashing frame values");
1180 },
1181 .AnyFrame => {
1182 @panic("TODO implement hashing anyframe values");
1183 },
1184 .EnumLiteral => {
1185 @panic("TODO implement hashing enum literal values");
1186 },
1187 }
1188 }
1189
11201190 pub const ArrayHashContext = struct {
11211191 ty: Type,
11221192
1123 pub fn hash(self: @This(), v: Value) u32 {
1193 pub fn hash(self: @This(), val: Value) u32 {
11241194 const other_context: HashContext = .{ .ty = self.ty };
1125 return @truncate(u32, other_context.hash(v));
1195 return @truncate(u32, other_context.hash(val));
11261196 }
11271197 pub fn eql(self: @This(), a: Value, b: Value) bool {
11281198 return a.eql(b, self.ty);
......@@ -1132,76 +1202,9 @@ pub const Value = extern union {
11321202 pub const HashContext = struct {
11331203 ty: Type,
11341204
1135 pub fn hash(self: @This(), v: Value) u64 {
1205 pub fn hash(self: @This(), val: Value) u64 {
11361206 var hasher = std.hash.Wyhash.init(0);
1137
1138 switch (self.ty.zigTypeTag()) {
1139 .BoundFn => unreachable, // TODO remove this from the language
1140
1141 .Void,
1142 .NoReturn,
1143 .Undefined,
1144 .Null,
1145 => {},
1146
1147 .Type => {
1148 var buf: ToTypeBuffer = undefined;
1149 return v.toType(&buf).hash();
1150 },
1151 .Bool => {
1152 std.hash.autoHash(&hasher, v.toBool());
1153 },
1154 .Int, .ComptimeInt => {
1155 var space: BigIntSpace = undefined;
1156 const big = v.toBigInt(&space);
1157 std.hash.autoHash(&hasher, big.positive);
1158 for (big.limbs) |limb| {
1159 std.hash.autoHash(&hasher, limb);
1160 }
1161 },
1162 .Float, .ComptimeFloat => {
1163 @panic("TODO implement hashing float values");
1164 },
1165 .Pointer => {
1166 @panic("TODO implement hashing pointer values");
1167 },
1168 .Array, .Vector => {
1169 @panic("TODO implement hashing array/vector values");
1170 },
1171 .Struct => {
1172 @panic("TODO implement hashing struct values");
1173 },
1174 .Optional => {
1175 @panic("TODO implement hashing optional values");
1176 },
1177 .ErrorUnion => {
1178 @panic("TODO implement hashing error union values");
1179 },
1180 .ErrorSet => {
1181 @panic("TODO implement hashing error set values");
1182 },
1183 .Enum => {
1184 @panic("TODO implement hashing enum values");
1185 },
1186 .Union => {
1187 @panic("TODO implement hashing union values");
1188 },
1189 .Fn => {
1190 @panic("TODO implement hashing function values");
1191 },
1192 .Opaque => {
1193 @panic("TODO implement hashing opaque values");
1194 },
1195 .Frame => {
1196 @panic("TODO implement hashing frame values");
1197 },
1198 .AnyFrame => {
1199 @panic("TODO implement hashing anyframe values");
1200 },
1201 .EnumLiteral => {
1202 @panic("TODO implement hashing enum literal values");
1203 },
1204 }
1207 val.hash(self.ty, &hasher);
12051208 return hasher.final();
12061209 }
12071210
test/behavior.zig+2-1
......@@ -4,6 +4,7 @@ test {
44 // Tests that pass for both.
55 _ = @import("behavior/bool.zig");
66 _ = @import("behavior/basic.zig");
7 _ = @import("behavior/generics.zig");
78
89 if (!builtin.zig_is_stage2) {
910 // Tests that only pass for stage1.
......@@ -94,7 +95,7 @@ test {
9495 _ = @import("behavior/fn_in_struct_in_comptime.zig");
9596 _ = @import("behavior/fn_delegation.zig");
9697 _ = @import("behavior/for.zig");
97 _ = @import("behavior/generics.zig");
98 _ = @import("behavior/generics_stage1.zig");
9899 _ = @import("behavior/hasdecl.zig");
99100 _ = @import("behavior/hasfield.zig");
100101 _ = @import("behavior/if.zig");
test/behavior/basic.zig+70
......@@ -92,3 +92,73 @@ fn first4KeysOfHomeRow() []const u8 {
9292test "return string from function" {
9393 try expect(mem.eql(u8, first4KeysOfHomeRow(), "aoeu"));
9494}
95
96test "hex escape" {
97 try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello"));
98}
99
100test "multiline string" {
101 const s1 =
102 \\one
103 \\two)
104 \\three
105 ;
106 const s2 = "one\ntwo)\nthree";
107 try expect(mem.eql(u8, s1, s2));
108}
109
110test "multiline string comments at start" {
111 const s1 =
112 //\\one
113 \\two)
114 \\three
115 ;
116 const s2 = "two)\nthree";
117 try expect(mem.eql(u8, s1, s2));
118}
119
120test "multiline string comments at end" {
121 const s1 =
122 \\one
123 \\two)
124 //\\three
125 ;
126 const s2 = "one\ntwo)";
127 try expect(mem.eql(u8, s1, s2));
128}
129
130test "multiline string comments in middle" {
131 const s1 =
132 \\one
133 //\\two)
134 \\three
135 ;
136 const s2 = "one\nthree";
137 try expect(mem.eql(u8, s1, s2));
138}
139
140test "multiline string comments at multiple places" {
141 const s1 =
142 \\one
143 //\\two
144 \\three
145 //\\four
146 \\five
147 ;
148 const s2 = "one\nthree\nfive";
149 try expect(mem.eql(u8, s1, s2));
150}
151
152test "call result of if else expression" {
153 try expect(mem.eql(u8, f2(true), "a"));
154 try expect(mem.eql(u8, f2(false), "b"));
155}
156fn f2(x: bool) []const u8 {
157 return (if (x) fA else fB)();
158}
159fn fA() []const u8 {
160 return "a";
161}
162fn fB() []const u8 {
163 return "b";
164}
test/behavior/generics.zig+8-161
......@@ -3,167 +3,14 @@ const testing = std.testing;
33const expect = testing.expect;
44const expectEqual = testing.expectEqual;
55
6test "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);
6test "one param, explicit comptime" {
7 var x: usize = 0;
8 x += checkSize(i32);
9 x += checkSize(bool);
10 x += checkSize(bool);
11 try expect(x == 6);
1012}
1113
12fn max(comptime T: type, a: T, b: T) T {
13 return if (a > b) a else b;
14}
15
16fn add(comptime a: i32, b: i32) i32 {
17 return (comptime a) + b;
18}
19
20const the_max = max(u32, 1234, 5678);
21test "compile time generic eval" {
22 try expect(the_max == 5678);
23}
24
25fn gimmeTheBigOne(a: u32, b: u32) u32 {
26 return max(u32, a, b);
27}
28
29fn shouldCallSameInstance(a: u32, b: u32) u32 {
30 return max(u32, a, b);
31}
32
33fn sameButWithFloats(a: f64, b: f64) f64 {
34 return max(f64, a, b);
35}
36
37test "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
43test "var params" {
44 try expect(max_i32(12, 34) == 34);
45 try expect(max_f64(1.2, 3.4) == 3.4);
46}
47
48test {
49 comptime try expect(max_i32(12, 34) == 34);
50 comptime try expect(max_f64(1.2, 3.4) == 3.4);
51}
52
53fn max_var(a: anytype, b: anytype) @TypeOf(a + b) {
54 return if (a > b) a else b;
55}
56
57fn max_i32(a: i32, b: i32) i32 {
58 return max_var(a, b);
59}
60
61fn max_f64(a: f64, b: f64) f64 {
62 return max_var(a, b);
63}
64
65pub fn List(comptime T: type) type {
66 return SmallList(T, 8);
67}
68
69pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) type {
70 return struct {
71 items: []T,
72 length: usize,
73 prealloc_items: [STATIC_SIZE]T,
74 };
75}
76
77test "function with return type type" {
78 var list: List(i32) = undefined;
79 var list2: List(i32) = undefined;
80 list.length = 10;
81 list2.length = 10;
82 try expect(list.prealloc_items.len == 8);
83 try expect(list2.prealloc_items.len == 8);
84}
85
86test "generic struct" {
87 var a1 = GenNode(i32){
88 .value = 13,
89 .next = null,
90 };
91 var b1 = GenNode(bool){
92 .value = true,
93 .next = null,
94 };
95 try expect(a1.value == 13);
96 try expect(a1.value == a1.getVal());
97 try expect(b1.getVal());
98}
99fn GenNode(comptime T: type) type {
100 return struct {
101 value: T,
102 next: ?*GenNode(T),
103 fn getVal(n: *const GenNode(T)) T {
104 return n.value;
105 }
106 };
107}
108
109test "const decls in struct" {
110 try expect(GenericDataThing(3).count_plus_one == 4);
111}
112fn GenericDataThing(comptime count: isize) type {
113 return struct {
114 const count_plus_one = count + 1;
115 };
116}
117
118test "use generic param in generic param" {
119 try expect(aGenericFn(i32, 3, 4) == 7);
120}
121fn aGenericFn(comptime T: type, comptime a: T, b: T) T {
122 return a + b;
123}
124
125test "generic fn with implicit cast" {
126 try expect(getFirstByte(u8, &[_]u8{13}) == 13);
127 try expect(getFirstByte(u16, &[_]u16{
128 0,
129 13,
130 }) == 0);
131}
132fn getByte(ptr: ?*const u8) u8 {
133 return ptr.?.*;
134}
135fn getFirstByte(comptime T: type, mem: []const T) u8 {
136 return getByte(@ptrCast(*const u8, &mem[0]));
137}
138
139const foos = [_]fn (anytype) bool{
140 foo1,
141 foo2,
142};
143
144fn foo1(arg: anytype) bool {
145 return arg;
146}
147fn foo2(arg: anytype) bool {
148 return !arg;
149}
150
151test "array of generic fns" {
152 try expect(foos[0](true));
153 try expect(!foos[1](true));
154}
155
156test "generic fn keeps non-generic parameter types" {
157 const A = 128;
158
159 const S = struct {
160 fn f(comptime T: type, s: []T) !void {
161 try expect(A != @typeInfo(@TypeOf(s)).Pointer.alignment);
162 }
163 };
164
165 // The compiler monomorphizes `S.f` for `T=u8` on its first use, check that
166 // `x` type not affect `s` parameter type.
167 var x: [16]u8 align(A) = undefined;
168 try S.f(u8, &x);
14fn checkSize(comptime T: type) usize {
15 return @sizeOf(T);
16916}
test/behavior/generics_stage1.zig created+169
......@@ -0,0 +1,169 @@
1const std = @import("std");
2const testing = std.testing;
3const expect = testing.expect;
4const expectEqual = testing.expectEqual;
5
6test "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
12fn max(comptime T: type, a: T, b: T) T {
13 return if (a > b) a else b;
14}
15
16fn add(comptime a: i32, b: i32) i32 {
17 return (comptime a) + b;
18}
19
20const the_max = max(u32, 1234, 5678);
21test "compile time generic eval" {
22 try expect(the_max == 5678);
23}
24
25fn gimmeTheBigOne(a: u32, b: u32) u32 {
26 return max(u32, a, b);
27}
28
29fn shouldCallSameInstance(a: u32, b: u32) u32 {
30 return max(u32, a, b);
31}
32
33fn sameButWithFloats(a: f64, b: f64) f64 {
34 return max(f64, a, b);
35}
36
37test "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
43test "var params" {
44 try expect(max_i32(12, 34) == 34);
45 try expect(max_f64(1.2, 3.4) == 3.4);
46}
47
48test {
49 comptime try expect(max_i32(12, 34) == 34);
50 comptime try expect(max_f64(1.2, 3.4) == 3.4);
51}
52
53fn max_var(a: anytype, b: anytype) @TypeOf(a + b) {
54 return if (a > b) a else b;
55}
56
57fn max_i32(a: i32, b: i32) i32 {
58 return max_var(a, b);
59}
60
61fn max_f64(a: f64, b: f64) f64 {
62 return max_var(a, b);
63}
64
65pub fn List(comptime T: type) type {
66 return SmallList(T, 8);
67}
68
69pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) type {
70 return struct {
71 items: []T,
72 length: usize,
73 prealloc_items: [STATIC_SIZE]T,
74 };
75}
76
77test "function with return type type" {
78 var list: List(i32) = undefined;
79 var list2: List(i32) = undefined;
80 list.length = 10;
81 list2.length = 10;
82 try expect(list.prealloc_items.len == 8);
83 try expect(list2.prealloc_items.len == 8);
84}
85
86test "generic struct" {
87 var a1 = GenNode(i32){
88 .value = 13,
89 .next = null,
90 };
91 var b1 = GenNode(bool){
92 .value = true,
93 .next = null,
94 };
95 try expect(a1.value == 13);
96 try expect(a1.value == a1.getVal());
97 try expect(b1.getVal());
98}
99fn GenNode(comptime T: type) type {
100 return struct {
101 value: T,
102 next: ?*GenNode(T),
103 fn getVal(n: *const GenNode(T)) T {
104 return n.value;
105 }
106 };
107}
108
109test "const decls in struct" {
110 try expect(GenericDataThing(3).count_plus_one == 4);
111}
112fn GenericDataThing(comptime count: isize) type {
113 return struct {
114 const count_plus_one = count + 1;
115 };
116}
117
118test "use generic param in generic param" {
119 try expect(aGenericFn(i32, 3, 4) == 7);
120}
121fn aGenericFn(comptime T: type, comptime a: T, b: T) T {
122 return a + b;
123}
124
125test "generic fn with implicit cast" {
126 try expect(getFirstByte(u8, &[_]u8{13}) == 13);
127 try expect(getFirstByte(u16, &[_]u16{
128 0,
129 13,
130 }) == 0);
131}
132fn getByte(ptr: ?*const u8) u8 {
133 return ptr.?.*;
134}
135fn getFirstByte(comptime T: type, mem: []const T) u8 {
136 return getByte(@ptrCast(*const u8, &mem[0]));
137}
138
139const foos = [_]fn (anytype) bool{
140 foo1,
141 foo2,
142};
143
144fn foo1(arg: anytype) bool {
145 return arg;
146}
147fn foo2(arg: anytype) bool {
148 return !arg;
149}
150
151test "array of generic fns" {
152 try expect(foos[0](true));
153 try expect(!foos[1](true));
154}
155
156test "generic fn keeps non-generic parameter types" {
157 const A = 128;
158
159 const S = struct {
160 fn f(comptime T: type, s: []T) !void {
161 try expect(A != @typeInfo(@TypeOf(s)).Pointer.alignment);
162 }
163 };
164
165 // The compiler monomorphizes `S.f` for `T=u8` on its first use, check that
166 // `x` type not affect `s` parameter type.
167 var x: [16]u8 align(A) = undefined;
168 try S.f(u8, &x);
169}
test/behavior/misc.zig+1-71
......@@ -40,10 +40,6 @@ test "constant equal function pointers" {
4040
4141fn emptyFn() void {}
4242
43test "hex escape" {
44 try expect(mem.eql(u8, "\x68\x65\x6c\x6c\x6f", "hello"));
45}
46
4743test "string concatenation" {
4844 try expect(mem.eql(u8, "OK" ++ " IT " ++ "WORKED", "OK IT WORKED"));
4945}
......@@ -62,59 +58,7 @@ test "string escapes" {
6258 try expectEqualStrings("\u{1234}\u{069}\u{1}", "\xe1\x88\xb4\x69\x01");
6359}
6460
65test "multiline string" {
66 const s1 =
67 \\one
68 \\two)
69 \\three
70 ;
71 const s2 = "one\ntwo)\nthree";
72 try expect(mem.eql(u8, s1, s2));
73}
74
75test "multiline string comments at start" {
76 const s1 =
77 //\\one
78 \\two)
79 \\three
80 ;
81 const s2 = "two)\nthree";
82 try expect(mem.eql(u8, s1, s2));
83}
84
85test "multiline string comments at end" {
86 const s1 =
87 \\one
88 \\two)
89 //\\three
90 ;
91 const s2 = "one\ntwo)";
92 try expect(mem.eql(u8, s1, s2));
93}
94
95test "multiline string comments in middle" {
96 const s1 =
97 \\one
98 //\\two)
99 \\three
100 ;
101 const s2 = "one\nthree";
102 try expect(mem.eql(u8, s1, s2));
103}
104
105test "multiline string comments at multiple places" {
106 const s1 =
107 \\one
108 //\\two
109 \\three
110 //\\four
111 \\five
112 ;
113 const s2 = "one\nthree\nfive";
114 try expect(mem.eql(u8, s1, s2));
115}
116
117test "multiline C string" {
61test "multiline string literal is null terminated" {
11862 const s1 =
11963 \\one
12064 \\two)
......@@ -169,20 +113,6 @@ fn outer() i64 {
169113 return inner();
170114}
171115
172test "call result of if else expression" {
173 try expect(mem.eql(u8, f2(true), "a"));
174 try expect(mem.eql(u8, f2(false), "b"));
175}
176fn f2(x: bool) []const u8 {
177 return (if (x) fA else fB)();
178}
179fn fA() []const u8 {
180 return "a";
181}
182fn fB() []const u8 {
183 return "b";
184}
185
186116test "constant enum initialization with differing sizes" {
187117 try test3_1(test3_foo);
188118 try test3_2(test3_bar);