mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 21:08:36 +00:00
stage2: basic generic functions are working
The general strategy is that Sema will pre-map comptime arguments into the inst_map, and then re-run the block body that contains the `param` and `func` instructions. This re-runs all the parameter type expressions except with comptime values populated. In Sema, param instructions are now handled specially: they detect whether they are comptime-elided or not. If so, they skip putting a value in the inst_map, since it is already pre-populated. If not, then they append to the `fields` field of `Sema` for use with the `func` instruction. So when the block body is re-run, a new function is generated with all the comptime arguments elided, and the new function type has only runtime parameters in it. TODO: give the generated Decls better names than "foo__anon_x". The new function is then added to the work queue to have its body analyzed and a runtime call AIR instruction to the new function is emitted. When the new function gets semantically analyzed, comptime parameters are pre-mapped to the corresponding `comptime_args` values rather than mapped to an `arg` AIR instruction. `comptime_args` is a new field that `Fn` has which is a `TypedValue` for each parameter. This field is non-null for generic function instantiations only. The values are the comptime arguments. For non-comptime parameters, a sentinel value is used. This is because we need to know the information of which parameters are comptime-known. Additionally: * AstGen: align and section expressions are evaluated in the scope that has comptime parameters in it. There are still some TODO items left; see the BRANCH_TODO file.
This commit is contained in:
parent
609b84611d
commit
382d201781
@ -1,7 +1,4 @@
|
||||
* update arg instructions:
|
||||
- generic instantiation inserts Sema map items for the comptime args only, re-runs the
|
||||
Decl ZIR to get the new Fn.
|
||||
* generic function call where it makes a new function
|
||||
* memoize the instantiation in a table
|
||||
* anytype with next parameter expression using it
|
||||
* expressions that depend on comptime stuff need a poison value to use for
|
||||
types when generating the generic function type
|
||||
* comptime anytype
|
||||
|
||||
@ -2906,14 +2906,7 @@ fn fnDecl(
|
||||
const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
|
||||
break :blk token_tags[maybe_inline_token] == .keyword_inline;
|
||||
};
|
||||
const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
|
||||
break :inst try expr(&decl_gz, &decl_gz.base, align_rl, fn_proto.ast.align_expr);
|
||||
};
|
||||
const section_inst: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
|
||||
break :inst try comptimeExpr(&decl_gz, &decl_gz.base, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr);
|
||||
};
|
||||
|
||||
try wip_decls.next(gpa, is_pub, is_export, align_inst != .none, section_inst != .none);
|
||||
try wip_decls.next(gpa, is_pub, is_export, fn_proto.ast.align_expr != 0, fn_proto.ast.section_expr != 0);
|
||||
|
||||
var params_scope = &fn_gz.base;
|
||||
const is_var_args = is_var_args: {
|
||||
@ -2994,6 +2987,13 @@ fn fnDecl(
|
||||
const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
|
||||
const is_inferred_error = token_tags[maybe_bang] == .bang;
|
||||
|
||||
const align_inst: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
|
||||
break :inst try expr(&decl_gz, params_scope, align_rl, fn_proto.ast.align_expr);
|
||||
};
|
||||
const section_inst: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
|
||||
break :inst try comptimeExpr(&decl_gz, params_scope, .{ .ty = .const_slice_u8_type }, fn_proto.ast.section_expr);
|
||||
};
|
||||
|
||||
const return_type_inst = try AstGen.expr(
|
||||
&decl_gz,
|
||||
params_scope,
|
||||
|
||||
@ -757,6 +757,10 @@ pub const Union = struct {
|
||||
pub const Fn = struct {
|
||||
/// The Decl that corresponds to the function itself.
|
||||
owner_decl: *Decl,
|
||||
/// If this is not null, this function is a generic function instantiation, and
|
||||
/// there is a `Value` here for each parameter of the function. Non-comptime
|
||||
/// parameters are marked with an `unreachable_value`.
|
||||
comptime_args: ?[*]TypedValue = null,
|
||||
/// The ZIR instruction that is a function instruction. Use this to find
|
||||
/// the body. We store this rather than the body directly so that when ZIR
|
||||
/// is regenerated on update(), we can map this to the new corresponding
|
||||
@ -3657,10 +3661,13 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
|
||||
// Here we are performing "runtime semantic analysis" for a function body, which means
|
||||
// we must map the parameter ZIR instructions to `arg` AIR instructions.
|
||||
// AIR requires the `arg` parameters to be the first N instructions.
|
||||
const params_len = @intCast(u32, fn_ty.fnParamLen());
|
||||
try inner_block.instructions.ensureTotalCapacity(gpa, params_len);
|
||||
try sema.air_instructions.ensureUnusedCapacity(gpa, params_len * 2); // * 2 for the `addType`
|
||||
try sema.inst_map.ensureUnusedCapacity(gpa, params_len);
|
||||
// This could be a generic function instantiation, however, in which case we need to
|
||||
// map the comptime parameters to constant values and only emit arg AIR instructions
|
||||
// for the runtime ones.
|
||||
const runtime_params_len = @intCast(u32, fn_ty.fnParamLen());
|
||||
try inner_block.instructions.ensureTotalCapacity(gpa, runtime_params_len);
|
||||
try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len * 2); // * 2 for the `addType`
|
||||
try sema.inst_map.ensureUnusedCapacity(gpa, fn_info.total_params_len);
|
||||
|
||||
var param_index: usize = 0;
|
||||
for (fn_info.param_body) |inst| {
|
||||
@ -3678,8 +3685,17 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
|
||||
|
||||
else => continue,
|
||||
};
|
||||
if (func.comptime_args) |comptime_args| {
|
||||
const arg_tv = comptime_args[param_index];
|
||||
if (arg_tv.val.tag() != .unreachable_value) {
|
||||
// We have a comptime value for this parameter.
|
||||
const arg = try sema.addConstant(arg_tv.ty, arg_tv.val);
|
||||
sema.inst_map.putAssumeCapacityNoClobber(inst, arg);
|
||||
param_index += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const param_type = fn_ty.fnParamType(param_index);
|
||||
param_index += 1;
|
||||
const ty_ref = try sema.addType(param_type);
|
||||
const arg_index = @intCast(u32, sema.air_instructions.len);
|
||||
inner_block.instructions.appendAssumeCapacity(arg_index);
|
||||
@ -3691,6 +3707,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
|
||||
} },
|
||||
});
|
||||
sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));
|
||||
param_index += 1;
|
||||
}
|
||||
|
||||
func.state = .in_progress;
|
||||
|
||||
346
src/Sema.zig
346
src/Sema.zig
@ -36,9 +36,14 @@ branch_count: u32 = 0,
|
||||
/// access to the source location set by the previous instruction which did
|
||||
/// contain a mapped source location.
|
||||
src: LazySrcLoc = .{ .token_offset = 0 },
|
||||
next_arg_index: usize = 0,
|
||||
params: std.ArrayListUnmanaged(Param) = .{},
|
||||
decl_val_table: std.AutoHashMapUnmanaged(*Decl, Air.Inst.Ref) = .{},
|
||||
/// `param` instructions are collected here to be used by the `func` instruction.
|
||||
params: std.ArrayListUnmanaged(Param) = .{},
|
||||
/// When doing a generic function instantiation, this array collects a `Value` object for
|
||||
/// each parameter that is comptime known and thus elided from the generated function.
|
||||
/// This memory is allocated by a parent `Sema` and owned by the values arena of the owner_decl.
|
||||
comptime_args: []TypedValue = &.{},
|
||||
next_arg_index: usize = 0,
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
@ -64,8 +69,8 @@ const target_util = @import("target.zig");
|
||||
|
||||
const Param = struct {
|
||||
name: [:0]const u8,
|
||||
/// `none` means `anytype`.
|
||||
ty: Air.Inst.Ref,
|
||||
/// `noreturn` means `anytype`.
|
||||
ty: Type,
|
||||
is_comptime: bool,
|
||||
};
|
||||
|
||||
@ -366,26 +371,6 @@ pub fn analyzeBody(
|
||||
// continue the loop.
|
||||
// We also know that they cannot be referenced later, so we avoid
|
||||
// putting them into the map.
|
||||
.param => {
|
||||
try sema.zirParam(inst, false);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.param_comptime => {
|
||||
try sema.zirParam(inst, true);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.param_anytype => {
|
||||
try sema.zirParamAnytype(inst, false);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.param_anytype_comptime => {
|
||||
try sema.zirParamAnytype(inst, true);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.breakpoint => {
|
||||
try sema.zirBreakpoint(block, inst);
|
||||
i += 1;
|
||||
@ -519,6 +504,88 @@ pub fn analyzeBody(
|
||||
return break_inst;
|
||||
}
|
||||
},
|
||||
.param => blk: {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;
|
||||
const src = inst_data.src();
|
||||
const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data;
|
||||
const param_name = sema.code.nullTerminatedString(extra.name);
|
||||
|
||||
if (sema.nextArgIsComptimeElided()) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO check if param_name shadows a Decl. This only needs to be done if
|
||||
// usingnamespace is implemented.
|
||||
|
||||
const param_ty = try sema.resolveType(block, src, extra.ty);
|
||||
try sema.params.append(sema.gpa, .{
|
||||
.name = param_name,
|
||||
.ty = param_ty,
|
||||
.is_comptime = false,
|
||||
});
|
||||
break :blk try sema.addConstUndef(param_ty);
|
||||
},
|
||||
.param_comptime => blk: {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;
|
||||
const src = inst_data.src();
|
||||
const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data;
|
||||
const param_name = sema.code.nullTerminatedString(extra.name);
|
||||
|
||||
if (sema.nextArgIsComptimeElided()) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO check if param_name shadows a Decl. This only needs to be done if
|
||||
// usingnamespace is implemented.
|
||||
|
||||
const param_ty = try sema.resolveType(block, src, extra.ty);
|
||||
try sema.params.append(sema.gpa, .{
|
||||
.name = param_name,
|
||||
.ty = param_ty,
|
||||
.is_comptime = true,
|
||||
});
|
||||
break :blk try sema.addConstUndef(param_ty);
|
||||
},
|
||||
.param_anytype => blk: {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
|
||||
const param_name = inst_data.get(sema.code);
|
||||
|
||||
if (sema.nextArgIsComptimeElided()) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO check if param_name shadows a Decl. This only needs to be done if
|
||||
// usingnamespace is implemented.
|
||||
|
||||
try sema.params.append(sema.gpa, .{
|
||||
.name = param_name,
|
||||
.ty = Type.initTag(.noreturn),
|
||||
.is_comptime = false,
|
||||
});
|
||||
break :blk try sema.addConstUndef(Type.initTag(.@"undefined"));
|
||||
},
|
||||
.param_anytype_comptime => blk: {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
|
||||
const param_name = inst_data.get(sema.code);
|
||||
|
||||
if (sema.nextArgIsComptimeElided()) {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO check if param_name shadows a Decl. This only needs to be done if
|
||||
// usingnamespace is implemented.
|
||||
|
||||
try sema.params.append(sema.gpa, .{
|
||||
.name = param_name,
|
||||
.ty = Type.initTag(.noreturn),
|
||||
.is_comptime = true,
|
||||
});
|
||||
break :blk try sema.addConstUndef(Type.initTag(.@"undefined"));
|
||||
},
|
||||
};
|
||||
if (sema.typeOf(air_inst).isNoReturn())
|
||||
return always_noreturn;
|
||||
@ -1339,36 +1406,6 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co
|
||||
return sema.analyzeLoad(block, src, result_ptr, result_ptr_src);
|
||||
}
|
||||
|
||||
fn zirParam(sema: *Sema, inst: Zir.Inst.Index, is_comptime: bool) CompileError!void {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;
|
||||
const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data;
|
||||
const param_name = sema.code.nullTerminatedString(extra.name);
|
||||
|
||||
// TODO check if param_name shadows a Decl. This only needs to be done if
|
||||
// usingnamespace is implemented.
|
||||
|
||||
const param_ty = sema.resolveInst(extra.ty);
|
||||
try sema.params.append(sema.gpa, .{
|
||||
.name = param_name,
|
||||
.ty = param_ty,
|
||||
.is_comptime = is_comptime,
|
||||
});
|
||||
}
|
||||
|
||||
fn zirParamAnytype(sema: *Sema, inst: Zir.Inst.Index, is_comptime: bool) CompileError!void {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
|
||||
const param_name = inst_data.get(sema.code);
|
||||
|
||||
// TODO check if param_name shadows a Decl. This only needs to be done if
|
||||
// usingnamespace is implemented.
|
||||
|
||||
try sema.params.append(sema.gpa, .{
|
||||
.name = param_name,
|
||||
.ty = .none,
|
||||
.is_comptime = is_comptime,
|
||||
});
|
||||
}
|
||||
|
||||
fn zirAllocExtended(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
@ -2497,10 +2534,6 @@ fn analyzeCall(
|
||||
sema.func = module_fn;
|
||||
defer sema.func = parent_func;
|
||||
|
||||
const parent_next_arg_index = sema.next_arg_index;
|
||||
sema.next_arg_index = 0;
|
||||
defer sema.next_arg_index = parent_next_arg_index;
|
||||
|
||||
var child_block: Scope.Block = .{
|
||||
.parent = null,
|
||||
.sema = sema,
|
||||
@ -2537,7 +2570,7 @@ fn analyzeCall(
|
||||
}
|
||||
_ = try sema.analyzeBody(&child_block, fn_info.body);
|
||||
break :res try sema.analyzeBlockBody(block, call_src, &child_block, merges);
|
||||
} else if (func_ty_info.is_generic) {
|
||||
} else if (func_ty_info.is_generic) res: {
|
||||
const func_val = try sema.resolveConstValue(block, func_src, func);
|
||||
const module_fn = func_val.castTag(.function).?.data;
|
||||
// Check the Module's generic function map with an adapted context, so that we
|
||||
@ -2545,37 +2578,142 @@ fn analyzeCall(
|
||||
// only to junk it if it matches an existing instantiation.
|
||||
// TODO
|
||||
|
||||
// Create a Decl for the new function.
|
||||
const generic_namespace = try sema.arena.create(Module.Scope.Namespace);
|
||||
generic_namespace.* = .{
|
||||
.parent = block.src_decl.namespace,
|
||||
.file_scope = block.src_decl.namespace.file_scope,
|
||||
.ty = func_ty,
|
||||
const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst);
|
||||
const zir_tags = sema.code.instructions.items(.tag);
|
||||
var non_comptime_args_len: u32 = 0;
|
||||
const new_func = new_func: {
|
||||
const namespace = module_fn.owner_decl.namespace;
|
||||
try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
|
||||
|
||||
// Create a Decl for the new function.
|
||||
const new_decl = try mod.allocateNewDecl(namespace, module_fn.owner_decl.src_node);
|
||||
// TODO better names for generic function instantiations
|
||||
const name_index = mod.getNextAnonNameIndex();
|
||||
new_decl.name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{
|
||||
module_fn.owner_decl.name, name_index,
|
||||
});
|
||||
new_decl.src_line = module_fn.owner_decl.src_line;
|
||||
new_decl.is_pub = module_fn.owner_decl.is_pub;
|
||||
new_decl.is_exported = module_fn.owner_decl.is_exported;
|
||||
new_decl.has_align = module_fn.owner_decl.has_align;
|
||||
new_decl.has_linksection = module_fn.owner_decl.has_linksection;
|
||||
new_decl.zir_decl_index = module_fn.owner_decl.zir_decl_index;
|
||||
new_decl.alive = true; // This Decl is called at runtime.
|
||||
new_decl.has_tv = true;
|
||||
new_decl.owns_tv = true;
|
||||
new_decl.analysis = .in_progress;
|
||||
new_decl.generation = mod.generation;
|
||||
|
||||
namespace.anon_decls.putAssumeCapacityNoClobber(new_decl, {});
|
||||
|
||||
var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
|
||||
errdefer new_decl_arena.deinit();
|
||||
|
||||
// Re-run the block that creates the function, with the comptime parameters
|
||||
// pre-populated inside `inst_map`. This causes `param_comptime` and
|
||||
// `param_anytype_comptime` ZIR instructions to be ignored, resulting in a
|
||||
// new, monomorphized function, with the comptime parameters elided.
|
||||
var child_sema: Sema = .{
|
||||
.mod = mod,
|
||||
.gpa = gpa,
|
||||
.arena = sema.arena,
|
||||
.code = sema.code,
|
||||
.owner_decl = new_decl,
|
||||
.namespace = namespace,
|
||||
.func = null,
|
||||
.owner_func = null,
|
||||
.comptime_args = try new_decl_arena.allocator.alloc(TypedValue, args.len),
|
||||
};
|
||||
defer child_sema.deinit();
|
||||
|
||||
var child_block: Scope.Block = .{
|
||||
.parent = null,
|
||||
.sema = &child_sema,
|
||||
.src_decl = new_decl,
|
||||
.instructions = .{},
|
||||
.inlining = null,
|
||||
.is_comptime = true,
|
||||
};
|
||||
defer child_block.instructions.deinit(gpa);
|
||||
|
||||
try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, args.len));
|
||||
var arg_i: usize = 0;
|
||||
for (fn_info.param_body) |inst| {
|
||||
const is_comptime = switch (zir_tags[inst]) {
|
||||
.param_comptime, .param_anytype_comptime => true,
|
||||
.param, .param_anytype => false, // TODO make true for always comptime types
|
||||
else => continue,
|
||||
};
|
||||
if (is_comptime) {
|
||||
// TODO: pass .unneeded to resolveConstValue and then if we get
|
||||
// error.NeededSourceLocation resolve the arg source location and
|
||||
// try again.
|
||||
const arg_src = call_src;
|
||||
const arg = args[arg_i];
|
||||
const arg_val = try sema.resolveConstValue(block, arg_src, arg);
|
||||
child_sema.comptime_args[arg_i] = .{
|
||||
.ty = try sema.typeOf(arg).copy(&new_decl_arena.allocator),
|
||||
.val = try arg_val.copy(&new_decl_arena.allocator),
|
||||
};
|
||||
const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
|
||||
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
|
||||
} else {
|
||||
non_comptime_args_len += 1;
|
||||
child_sema.comptime_args[arg_i] = .{
|
||||
.ty = Type.initTag(.noreturn),
|
||||
.val = Value.initTag(.unreachable_value),
|
||||
};
|
||||
}
|
||||
arg_i += 1;
|
||||
}
|
||||
const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body);
|
||||
const new_func_val = try child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst);
|
||||
const new_func = new_func_val.castTag(.function).?.data;
|
||||
|
||||
// Populate the Decl ty/val with the function and its type.
|
||||
new_decl.ty = try child_sema.typeOf(new_func_inst).copy(&new_decl_arena.allocator);
|
||||
new_decl.val = try Value.Tag.function.create(&new_decl_arena.allocator, new_func);
|
||||
new_decl.analysis = .complete;
|
||||
|
||||
// Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field
|
||||
// will be populated, ensuring it will have `analyzeBody` called with the ZIR
|
||||
// parameters mapped appropriately.
|
||||
try mod.comp.bin_file.allocateDeclIndexes(new_decl);
|
||||
try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func });
|
||||
|
||||
try new_decl.finalizeNewArena(&new_decl_arena);
|
||||
break :new_func try sema.analyzeDeclVal(block, func_src, new_decl);
|
||||
};
|
||||
const new_decl = try mod.allocateNewDecl(generic_namespace, module_fn.owner_decl.src_node);
|
||||
_ = new_decl;
|
||||
|
||||
// Iterate over the parameters that are comptime, evaluating their type expressions
|
||||
// inside a Scope which contains the previous parameters.
|
||||
//for (args) |arg, arg_i| {
|
||||
//}
|
||||
|
||||
// Create a new Fn with only the runtime-known parameters.
|
||||
// TODO
|
||||
|
||||
// Populate the Decl ty/val with the function and its type.
|
||||
// TODO
|
||||
|
||||
// Queue up a `codegen_func` work item for the new Fn, making sure it will have
|
||||
// `analyzeBody` called with the ZIR parameters mapped appropriately.
|
||||
// TODO
|
||||
|
||||
// Save it into the Module's generic function map.
|
||||
// TODO
|
||||
|
||||
// Call it the same as a runtime function.
|
||||
// TODO
|
||||
return mod.fail(&block.base, func_src, "TODO implement generic fn call", .{});
|
||||
// Make a runtime call to the new function, making sure to omit the comptime args.
|
||||
try sema.requireRuntimeBlock(block, call_src);
|
||||
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
|
||||
non_comptime_args_len);
|
||||
const func_inst = try block.addInst(.{
|
||||
.tag = .call,
|
||||
.data = .{ .pl_op = .{
|
||||
.operand = new_func,
|
||||
.payload = sema.addExtraAssumeCapacity(Air.Call{
|
||||
.args_len = non_comptime_args_len,
|
||||
}),
|
||||
} },
|
||||
});
|
||||
var arg_i: usize = 0;
|
||||
for (fn_info.param_body) |inst| {
|
||||
const is_comptime = switch (zir_tags[inst]) {
|
||||
.param_comptime, .param_anytype_comptime => true,
|
||||
.param, .param_anytype => false, // TODO make true for always comptime types
|
||||
else => continue,
|
||||
};
|
||||
if (is_comptime) {
|
||||
sema.air_extra.appendAssumeCapacity(@enumToInt(args[arg_i]));
|
||||
}
|
||||
arg_i += 1;
|
||||
}
|
||||
break :res func_inst;
|
||||
} else res: {
|
||||
try sema.requireRuntimeBlock(block, call_src);
|
||||
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
|
||||
@ -3302,15 +3440,10 @@ fn funcCommon(
|
||||
const param_types = try sema.arena.alloc(Type, sema.params.items.len);
|
||||
const comptime_params = try sema.arena.alloc(bool, sema.params.items.len);
|
||||
for (sema.params.items) |param, i| {
|
||||
if (param.ty == .none) {
|
||||
if (param.ty.tag() == .noreturn) {
|
||||
param_types[i] = Type.initTag(.noreturn); // indicates anytype
|
||||
} else {
|
||||
// TODO make a compile error from `resolveType` report the source location
|
||||
// of the specific parameter. Will need to take a similar strategy as
|
||||
// `resolveSwitchItemVal` to avoid resolving the source location unless
|
||||
// we actually need to report an error.
|
||||
const param_src = src;
|
||||
param_types[i] = try sema.analyzeAsType(block, param_src, param.ty);
|
||||
param_types[i] = param.ty;
|
||||
}
|
||||
comptime_params[i] = param.is_comptime;
|
||||
any_are_comptime = any_are_comptime or param.is_comptime;
|
||||
@ -3402,6 +3535,7 @@ fn funcCommon(
|
||||
.state = anal_state,
|
||||
.zir_body_inst = body_inst,
|
||||
.owner_decl = sema.owner_decl,
|
||||
.comptime_args = if (sema.comptime_args.len == 0) null else sema.comptime_args.ptr,
|
||||
.lbrace_line = src_locs.lbrace_line,
|
||||
.rbrace_line = src_locs.rbrace_line,
|
||||
.lbrace_column = @truncate(u16, src_locs.columns),
|
||||
@ -6819,19 +6953,12 @@ fn safetyPanic(
|
||||
const msg_inst = msg_inst: {
|
||||
// TODO instead of making a new decl for every panic in the entire compilation,
|
||||
// introduce the concept of a reference-counted decl for these
|
||||
var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
|
||||
errdefer new_decl_arena.deinit();
|
||||
|
||||
const decl_ty = try Type.Tag.array_u8.create(&new_decl_arena.allocator, msg.len);
|
||||
const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, msg);
|
||||
|
||||
const new_decl = try sema.mod.createAnonymousDecl(&block.base, .{
|
||||
.ty = decl_ty,
|
||||
.val = decl_val,
|
||||
});
|
||||
errdefer sema.mod.deleteAnonDecl(&block.base, new_decl);
|
||||
try new_decl.finalizeNewArena(&new_decl_arena);
|
||||
break :msg_inst try sema.analyzeDeclRef(new_decl);
|
||||
var anon_decl = try block.startAnonDecl();
|
||||
defer anon_decl.deinit();
|
||||
break :msg_inst try sema.analyzeDeclRef(try anon_decl.finish(
|
||||
try Type.Tag.array_u8.create(anon_decl.arena(), msg.len),
|
||||
try Value.Tag.bytes.create(anon_decl.arena(), msg),
|
||||
));
|
||||
};
|
||||
|
||||
const casted_msg_inst = try sema.coerce(block, Type.initTag(.const_slice_u8), msg_inst, src);
|
||||
@ -8832,7 +8959,7 @@ fn addConstUndef(sema: *Sema, ty: Type) CompileError!Air.Inst.Ref {
|
||||
return sema.addConstant(ty, Value.initTag(.undef));
|
||||
}
|
||||
|
||||
fn addConstant(sema: *Sema, ty: Type, val: Value) CompileError!Air.Inst.Ref {
|
||||
pub fn addConstant(sema: *Sema, ty: Type, val: Value) SemaError!Air.Inst.Ref {
|
||||
const gpa = sema.gpa;
|
||||
const ty_inst = try sema.addType(ty);
|
||||
try sema.air_values.append(gpa, val);
|
||||
@ -8888,3 +9015,10 @@ fn isComptimeKnown(
|
||||
) !bool {
|
||||
return (try sema.resolveMaybeUndefVal(block, src, inst)) != null;
|
||||
}
|
||||
|
||||
fn nextArgIsComptimeElided(sema: *Sema) bool {
|
||||
if (sema.comptime_args.len == 0) return false;
|
||||
const result = sema.comptime_args[sema.next_arg_index].val.tag() != .unreachable_value;
|
||||
sema.next_arg_index += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
14
src/Zir.zig
14
src/Zir.zig
@ -4909,6 +4909,7 @@ fn findDeclsBody(
|
||||
pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) struct {
|
||||
param_body: []const Inst.Index,
|
||||
body: []const Inst.Index,
|
||||
total_params_len: u32,
|
||||
} {
|
||||
const tags = zir.instructions.items(.tag);
|
||||
const datas = zir.instructions.items(.data);
|
||||
@ -4944,8 +4945,19 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) struct {
|
||||
};
|
||||
assert(tags[info.param_block] == .block or tags[info.param_block] == .block_inline);
|
||||
const param_block = zir.extraData(Inst.Block, datas[info.param_block].pl_node.payload_index);
|
||||
const param_body = zir.extra[param_block.end..][0..param_block.data.body_len];
|
||||
var total_params_len: u32 = 0;
|
||||
for (param_body) |inst| {
|
||||
switch (tags[inst]) {
|
||||
.param, .param_comptime, .param_anytype, .param_anytype_comptime => {
|
||||
total_params_len += 1;
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
}
|
||||
return .{
|
||||
.param_body = zir.extra[param_block.end..][0..param_block.data.body_len],
|
||||
.param_body = param_body,
|
||||
.body = info.body,
|
||||
.total_params_len = total_params_len,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1182,7 +1182,6 @@ pub const Type = extern union {
|
||||
.fn_void_no_args,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.function,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.array_u8_sentinel_0,
|
||||
@ -1207,6 +1206,8 @@ pub const Type = extern union {
|
||||
.anyframe_T,
|
||||
=> true,
|
||||
|
||||
.function => !self.castTag(.function).?.data.is_generic,
|
||||
|
||||
.@"struct" => {
|
||||
// TODO introduce lazy value mechanism
|
||||
const struct_obj = self.castTag(.@"struct").?.data;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user