Sema: do not immediately destroy failed generic instantiation
Closes #12535 Closes #12765 Closes #12927
This commit is contained in:
@@ -3540,8 +3540,8 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
|
||||
if (decl.getInnerNamespace()) |namespace| {
|
||||
namespace.destroyDecls(mod);
|
||||
}
|
||||
decl.clearValues(mod);
|
||||
}
|
||||
decl.clearValues(mod);
|
||||
decl.dependants.deinit(gpa);
|
||||
decl.dependencies.deinit(gpa);
|
||||
decl.clearName(gpa);
|
||||
@@ -4610,9 +4610,18 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
|
||||
|
||||
// We need the memory for the Type to go into the arena for the Decl
|
||||
var decl_arena = std.heap.ArenaAllocator.init(gpa);
|
||||
errdefer decl_arena.deinit();
|
||||
const decl_arena_allocator = decl_arena.allocator();
|
||||
|
||||
const decl_arena_state = blk: {
|
||||
errdefer decl_arena.deinit();
|
||||
const s = try decl_arena_allocator.create(std.heap.ArenaAllocator.State);
|
||||
break :blk s;
|
||||
};
|
||||
defer {
|
||||
decl_arena_state.* = decl_arena.state;
|
||||
decl.value_arena = decl_arena_state;
|
||||
}
|
||||
|
||||
var analysis_arena = std.heap.ArenaAllocator.init(gpa);
|
||||
defer analysis_arena.deinit();
|
||||
const analysis_arena_allocator = analysis_arena.allocator();
|
||||
@@ -4681,8 +4690,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
|
||||
// not the struct itself.
|
||||
try sema.resolveTypeLayout(decl_tv.ty);
|
||||
|
||||
const decl_arena_state = try decl_arena_allocator.create(std.heap.ArenaAllocator.State);
|
||||
|
||||
if (decl.is_usingnamespace) {
|
||||
if (!decl_tv.ty.eql(Type.type, mod)) {
|
||||
return sema.fail(&block_scope, ty_src, "expected type, found {}", .{
|
||||
@@ -4701,8 +4708,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
|
||||
decl.@"linksection" = null;
|
||||
decl.has_tv = true;
|
||||
decl.owns_tv = false;
|
||||
decl_arena_state.* = decl_arena.state;
|
||||
decl.value_arena = decl_arena_state;
|
||||
decl.analysis = .complete;
|
||||
decl.generation = mod.generation;
|
||||
|
||||
@@ -4723,16 +4728,14 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
|
||||
if (decl.getFunction()) |prev_func| {
|
||||
prev_is_inline = prev_func.state == .inline_only;
|
||||
}
|
||||
decl.clearValues(mod);
|
||||
}
|
||||
decl.clearValues(mod);
|
||||
|
||||
decl.ty = try decl_tv.ty.copy(decl_arena_allocator);
|
||||
decl.val = try decl_tv.val.copy(decl_arena_allocator);
|
||||
// linksection, align, and addrspace were already set by Sema
|
||||
decl.has_tv = true;
|
||||
decl.owns_tv = owns_tv;
|
||||
decl_arena_state.* = decl_arena.state;
|
||||
decl.value_arena = decl_arena_state;
|
||||
decl.analysis = .complete;
|
||||
decl.generation = mod.generation;
|
||||
|
||||
@@ -4767,8 +4770,8 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
|
||||
var type_changed = true;
|
||||
if (decl.has_tv) {
|
||||
type_changed = !decl.ty.eql(decl_tv.ty, mod);
|
||||
decl.clearValues(mod);
|
||||
}
|
||||
decl.clearValues(mod);
|
||||
|
||||
decl.owns_tv = false;
|
||||
var queue_linker_work = false;
|
||||
@@ -4841,8 +4844,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
|
||||
};
|
||||
};
|
||||
decl.has_tv = true;
|
||||
decl_arena_state.* = decl_arena.state;
|
||||
decl.value_arena = decl_arena_state;
|
||||
decl.analysis = .complete;
|
||||
decl.generation = mod.generation;
|
||||
|
||||
@@ -5447,8 +5448,8 @@ pub fn clearDecl(
|
||||
if (decl.getInnerNamespace()) |namespace| {
|
||||
try namespace.deleteAllDecls(mod, outdated_decls);
|
||||
}
|
||||
decl.clearValues(mod);
|
||||
}
|
||||
decl.clearValues(mod);
|
||||
|
||||
if (decl.deletion_flag) {
|
||||
decl.deletion_flag = false;
|
||||
|
||||
464
src/Sema.zig
464
src/Sema.zig
@@ -7112,7 +7112,6 @@ fn instantiateGenericCall(
|
||||
const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter);
|
||||
const callee = if (!gop.found_existing) callee: {
|
||||
const new_module_func = try gpa.create(Module.Fn);
|
||||
errdefer gpa.destroy(new_module_func);
|
||||
|
||||
// This ensures that we can operate on the hash map before the Module.Fn
|
||||
// struct is fully initialized.
|
||||
@@ -7120,7 +7119,6 @@ fn instantiateGenericCall(
|
||||
new_module_func.generic_owner_decl = module_fn.owner_decl.toOptional();
|
||||
new_module_func.comptime_args = null;
|
||||
gop.key_ptr.* = new_module_func;
|
||||
errdefer assert(mod.monomorphed_funcs.remove(new_module_func));
|
||||
|
||||
try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
|
||||
|
||||
@@ -7128,7 +7126,6 @@ fn instantiateGenericCall(
|
||||
const src_decl_index = namespace.getDeclIndex();
|
||||
const src_decl = mod.declPtr(src_decl_index);
|
||||
const new_decl_index = try mod.allocateNewDecl(namespace, fn_owner_decl.src_node, src_decl.src_scope);
|
||||
errdefer mod.destroyDecl(new_decl_index);
|
||||
const new_decl = mod.declPtr(new_decl_index);
|
||||
// TODO better names for generic function instantiations
|
||||
const decl_name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{
|
||||
@@ -7148,223 +7145,59 @@ fn instantiateGenericCall(
|
||||
new_decl.generation = mod.generation;
|
||||
|
||||
namespace.anon_decls.putAssumeCapacityNoClobber(new_decl_index, {});
|
||||
errdefer assert(namespace.anon_decls.orderedRemove(new_decl_index));
|
||||
|
||||
// The generic function Decl is guaranteed to be the first dependency
|
||||
// of each of its instantiations.
|
||||
assert(new_decl.dependencies.keys().len == 0);
|
||||
try mod.declareDeclDependency(new_decl_index, module_fn.owner_decl);
|
||||
// Resolving the new function type below will possibly declare more decl dependencies
|
||||
// and so we remove them all here in case of error.
|
||||
errdefer {
|
||||
for (new_decl.dependencies.keys()) |dep_index| {
|
||||
const dep = mod.declPtr(dep_index);
|
||||
dep.removeDependant(new_decl_index);
|
||||
}
|
||||
}
|
||||
|
||||
var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
|
||||
errdefer new_decl_arena.deinit();
|
||||
const new_decl_arena_allocator = new_decl_arena.allocator();
|
||||
|
||||
// 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,
|
||||
.perm_arena = new_decl_arena_allocator,
|
||||
.code = fn_zir,
|
||||
.owner_decl = new_decl,
|
||||
.owner_decl_index = new_decl_index,
|
||||
.func = null,
|
||||
.fn_ret_ty = Type.void,
|
||||
.owner_func = null,
|
||||
.comptime_args = try new_decl_arena_allocator.alloc(TypedValue, uncasted_args.len),
|
||||
.comptime_args_fn_inst = module_fn.zir_body_inst,
|
||||
.preallocated_new_func = new_module_func,
|
||||
.is_generic_instantiation = true,
|
||||
.branch_quota = sema.branch_quota,
|
||||
.branch_count = sema.branch_count,
|
||||
};
|
||||
defer child_sema.deinit();
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
var child_block: Block = .{
|
||||
.parent = null,
|
||||
.sema = &child_sema,
|
||||
.src_decl = new_decl_index,
|
||||
.namespace = namespace,
|
||||
.wip_capture_scope = wip_captures.scope,
|
||||
.instructions = .{},
|
||||
.inlining = null,
|
||||
.is_comptime = true,
|
||||
};
|
||||
defer {
|
||||
child_block.instructions.deinit(gpa);
|
||||
child_block.params.deinit(gpa);
|
||||
}
|
||||
|
||||
try child_sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
|
||||
|
||||
var arg_i: usize = 0;
|
||||
for (fn_info.param_body) |inst| {
|
||||
var is_comptime = false;
|
||||
var is_anytype = false;
|
||||
switch (zir_tags[inst]) {
|
||||
.param => {
|
||||
is_comptime = func_ty_info.paramIsComptime(arg_i);
|
||||
},
|
||||
.param_comptime => {
|
||||
is_comptime = true;
|
||||
},
|
||||
.param_anytype => {
|
||||
is_anytype = true;
|
||||
is_comptime = func_ty_info.paramIsComptime(arg_i);
|
||||
},
|
||||
.param_anytype_comptime => {
|
||||
is_anytype = true;
|
||||
is_comptime = true;
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
const arg = uncasted_args[arg_i];
|
||||
if (is_comptime) {
|
||||
const arg_val = (try sema.resolveMaybeUndefVal(arg)).?;
|
||||
const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
|
||||
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
|
||||
} else if (is_anytype) {
|
||||
const arg_ty = sema.typeOf(arg);
|
||||
if (try sema.typeRequiresComptime(arg_ty)) {
|
||||
const arg_val = sema.resolveConstValue(block, .unneeded, arg, "") catch |err| switch (err) {
|
||||
error.NeededSourceLocation => {
|
||||
const decl = sema.mod.declPtr(block.src_decl);
|
||||
const arg_src = Module.argSrc(call_src.node_offset.x, sema.gpa, decl, arg_i, bound_arg_src);
|
||||
_ = try sema.resolveConstValue(block, arg_src, arg, "argument to parameter with comptime-only type must be comptime-known");
|
||||
unreachable;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
const child_arg = try child_sema.addConstant(arg_ty, arg_val);
|
||||
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
|
||||
} else {
|
||||
// We insert into the map an instruction which is runtime-known
|
||||
// but has the type of the argument.
|
||||
const child_arg = try child_block.addInst(.{
|
||||
.tag = .arg,
|
||||
.data = .{ .arg = .{
|
||||
.ty = try child_sema.addType(arg_ty),
|
||||
.src_index = @intCast(u32, arg_i),
|
||||
} },
|
||||
});
|
||||
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
|
||||
const new_func = sema.resolveGenericInstantiationType(
|
||||
block,
|
||||
new_decl_arena_allocator,
|
||||
fn_zir,
|
||||
new_decl,
|
||||
new_decl_index,
|
||||
uncasted_args,
|
||||
module_fn,
|
||||
new_module_func,
|
||||
namespace,
|
||||
func_ty_info,
|
||||
call_src,
|
||||
bound_arg_src,
|
||||
) catch |err| switch (err) {
|
||||
error.GenericPoison, error.ComptimeReturn => {
|
||||
new_decl_arena.deinit();
|
||||
// Resolving the new function type below will possibly declare more decl dependencies
|
||||
// and so we remove them all here in case of error.
|
||||
for (new_decl.dependencies.keys()) |dep_index| {
|
||||
const dep = mod.declPtr(dep_index);
|
||||
dep.removeDependant(new_decl_index);
|
||||
}
|
||||
}
|
||||
arg_i += 1;
|
||||
}
|
||||
|
||||
// Save the error trace as our first action in the function.
|
||||
// If this is unnecessary after all, Liveness will clean it up for us.
|
||||
const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block);
|
||||
child_sema.error_return_trace_index_on_fn_entry = error_return_trace_index;
|
||||
child_block.error_return_trace_index = error_return_trace_index;
|
||||
|
||||
const new_func_inst = child_sema.resolveBody(&child_block, fn_info.param_body, fn_info.param_body_inst) catch |err| {
|
||||
if (err == error.GenericPoison) return error.GenericPoison;
|
||||
// TODO look up the compile error that happened here and attach a note to it
|
||||
// pointing here, at the generic instantiation callsite.
|
||||
if (sema.owner_func) |owner_func| {
|
||||
owner_func.state = .dependency_failure;
|
||||
} else {
|
||||
sema.owner_decl.analysis = .dependency_failure;
|
||||
}
|
||||
return err;
|
||||
assert(namespace.anon_decls.orderedRemove(new_decl_index));
|
||||
mod.destroyDecl(new_decl_index);
|
||||
assert(mod.monomorphed_funcs.remove(new_module_func));
|
||||
gpa.destroy(new_module_func);
|
||||
return err;
|
||||
},
|
||||
else => {
|
||||
{
|
||||
errdefer new_decl_arena.deinit();
|
||||
try new_decl.finalizeNewArena(&new_decl_arena);
|
||||
}
|
||||
// TODO look up the compile error that happened here and attach a note to it
|
||||
// pointing here, at the generic instantiation callsite.
|
||||
if (sema.owner_func) |owner_func| {
|
||||
owner_func.state = .dependency_failure;
|
||||
} else {
|
||||
sema.owner_decl.analysis = .dependency_failure;
|
||||
}
|
||||
return err;
|
||||
},
|
||||
};
|
||||
const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst, "") catch unreachable;
|
||||
const new_func = new_func_val.castTag(.function).?.data;
|
||||
errdefer new_func.deinit(gpa);
|
||||
assert(new_func == new_module_func);
|
||||
|
||||
arg_i = 0;
|
||||
for (fn_info.param_body) |inst| {
|
||||
var is_comptime = false;
|
||||
switch (zir_tags[inst]) {
|
||||
.param => {
|
||||
is_comptime = func_ty_info.paramIsComptime(arg_i);
|
||||
},
|
||||
.param_comptime => {
|
||||
is_comptime = true;
|
||||
},
|
||||
.param_anytype => {
|
||||
is_comptime = func_ty_info.paramIsComptime(arg_i);
|
||||
},
|
||||
.param_anytype_comptime => {
|
||||
is_comptime = true;
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
|
||||
// We populate the Type here regardless because it is needed by
|
||||
// `GenericCallAdapter.eql` as well as function body analysis.
|
||||
// Whether it is anytype is communicated by `isAnytypeParam`.
|
||||
const arg = child_sema.inst_map.get(inst).?;
|
||||
const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator);
|
||||
|
||||
if (try sema.typeRequiresComptime(copied_arg_ty)) {
|
||||
is_comptime = true;
|
||||
}
|
||||
|
||||
if (is_comptime) {
|
||||
const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(arg) catch unreachable).?;
|
||||
child_sema.comptime_args[arg_i] = .{
|
||||
.ty = copied_arg_ty,
|
||||
.val = try arg_val.copy(new_decl_arena_allocator),
|
||||
};
|
||||
} else {
|
||||
child_sema.comptime_args[arg_i] = .{
|
||||
.ty = copied_arg_ty,
|
||||
.val = Value.initTag(.generic_poison),
|
||||
};
|
||||
}
|
||||
|
||||
arg_i += 1;
|
||||
}
|
||||
|
||||
try wip_captures.finalize();
|
||||
|
||||
// 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);
|
||||
// If the call evaluated to a return type that requires comptime, never mind
|
||||
// our generic instantiation. Instead we need to perform a comptime call.
|
||||
const new_fn_info = new_decl.ty.fnInfo();
|
||||
if (try sema.typeRequiresComptime(new_fn_info.return_type)) {
|
||||
return error.ComptimeReturn;
|
||||
}
|
||||
// Similarly, if the call evaluated to a generic type we need to instead
|
||||
// call it inline.
|
||||
if (new_fn_info.is_generic or new_fn_info.cc == .Inline) {
|
||||
return error.GenericPoison;
|
||||
}
|
||||
|
||||
new_decl.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func);
|
||||
new_decl.@"align" = 0;
|
||||
new_decl.has_tv = true;
|
||||
new_decl.owns_tv = true;
|
||||
new_decl.analysis = .complete;
|
||||
|
||||
log.debug("generic function '{s}' instantiated with type {}", .{
|
||||
new_decl.name, new_decl.ty.fmtDebug(),
|
||||
});
|
||||
|
||||
// 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_index);
|
||||
try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func });
|
||||
errdefer new_decl_arena.deinit();
|
||||
|
||||
try new_decl.finalizeNewArena(&new_decl_arena);
|
||||
break :callee new_func;
|
||||
@@ -7444,6 +7277,218 @@ fn instantiateGenericCall(
|
||||
return result;
|
||||
}
|
||||
|
||||
fn resolveGenericInstantiationType(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
new_decl_arena_allocator: Allocator,
|
||||
fn_zir: Zir,
|
||||
new_decl: *Decl,
|
||||
new_decl_index: Decl.Index,
|
||||
uncasted_args: []const Air.Inst.Ref,
|
||||
module_fn: *Module.Fn,
|
||||
new_module_func: *Module.Fn,
|
||||
namespace: *Namespace,
|
||||
func_ty_info: Type.Payload.Function.Data,
|
||||
call_src: LazySrcLoc,
|
||||
bound_arg_src: ?LazySrcLoc,
|
||||
) !*Module.Fn {
|
||||
const mod = sema.mod;
|
||||
const gpa = sema.gpa;
|
||||
|
||||
const zir_tags = fn_zir.instructions.items(.tag);
|
||||
const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);
|
||||
|
||||
// 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,
|
||||
.perm_arena = new_decl_arena_allocator,
|
||||
.code = fn_zir,
|
||||
.owner_decl = new_decl,
|
||||
.owner_decl_index = new_decl_index,
|
||||
.func = null,
|
||||
.fn_ret_ty = Type.void,
|
||||
.owner_func = null,
|
||||
.comptime_args = try new_decl_arena_allocator.alloc(TypedValue, uncasted_args.len),
|
||||
.comptime_args_fn_inst = module_fn.zir_body_inst,
|
||||
.preallocated_new_func = new_module_func,
|
||||
.is_generic_instantiation = true,
|
||||
.branch_quota = sema.branch_quota,
|
||||
.branch_count = sema.branch_count,
|
||||
};
|
||||
defer child_sema.deinit();
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, sema.perm_arena, new_decl.src_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
var child_block: Block = .{
|
||||
.parent = null,
|
||||
.sema = &child_sema,
|
||||
.src_decl = new_decl_index,
|
||||
.namespace = namespace,
|
||||
.wip_capture_scope = wip_captures.scope,
|
||||
.instructions = .{},
|
||||
.inlining = null,
|
||||
.is_comptime = true,
|
||||
};
|
||||
defer {
|
||||
child_block.instructions.deinit(gpa);
|
||||
child_block.params.deinit(gpa);
|
||||
}
|
||||
|
||||
try child_sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
|
||||
|
||||
var arg_i: usize = 0;
|
||||
for (fn_info.param_body) |inst| {
|
||||
var is_comptime = false;
|
||||
var is_anytype = false;
|
||||
switch (zir_tags[inst]) {
|
||||
.param => {
|
||||
is_comptime = func_ty_info.paramIsComptime(arg_i);
|
||||
},
|
||||
.param_comptime => {
|
||||
is_comptime = true;
|
||||
},
|
||||
.param_anytype => {
|
||||
is_anytype = true;
|
||||
is_comptime = func_ty_info.paramIsComptime(arg_i);
|
||||
},
|
||||
.param_anytype_comptime => {
|
||||
is_anytype = true;
|
||||
is_comptime = true;
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
const arg = uncasted_args[arg_i];
|
||||
if (is_comptime) {
|
||||
const arg_val = (try sema.resolveMaybeUndefVal(arg)).?;
|
||||
const child_arg = try child_sema.addConstant(sema.typeOf(arg), arg_val);
|
||||
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
|
||||
} else if (is_anytype) {
|
||||
const arg_ty = sema.typeOf(arg);
|
||||
if (try sema.typeRequiresComptime(arg_ty)) {
|
||||
const arg_val = sema.resolveConstValue(block, .unneeded, arg, "") catch |err| switch (err) {
|
||||
error.NeededSourceLocation => {
|
||||
const decl = sema.mod.declPtr(block.src_decl);
|
||||
const arg_src = Module.argSrc(call_src.node_offset.x, sema.gpa, decl, arg_i, bound_arg_src);
|
||||
_ = try sema.resolveConstValue(block, arg_src, arg, "argument to parameter with comptime-only type must be comptime-known");
|
||||
unreachable;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
const child_arg = try child_sema.addConstant(arg_ty, arg_val);
|
||||
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
|
||||
} else {
|
||||
// We insert into the map an instruction which is runtime-known
|
||||
// but has the type of the argument.
|
||||
const child_arg = try child_block.addInst(.{
|
||||
.tag = .arg,
|
||||
.data = .{ .arg = .{
|
||||
.ty = try child_sema.addType(arg_ty),
|
||||
.src_index = @intCast(u32, arg_i),
|
||||
} },
|
||||
});
|
||||
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
|
||||
}
|
||||
}
|
||||
arg_i += 1;
|
||||
}
|
||||
|
||||
// Save the error trace as our first action in the function.
|
||||
// If this is unnecessary after all, Liveness will clean it up for us.
|
||||
const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block);
|
||||
child_sema.error_return_trace_index_on_fn_entry = error_return_trace_index;
|
||||
child_block.error_return_trace_index = error_return_trace_index;
|
||||
|
||||
const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body, fn_info.param_body_inst);
|
||||
const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst, undefined) catch unreachable;
|
||||
const new_func = new_func_val.castTag(.function).?.data;
|
||||
errdefer new_func.deinit(gpa);
|
||||
assert(new_func == new_module_func);
|
||||
|
||||
arg_i = 0;
|
||||
for (fn_info.param_body) |inst| {
|
||||
var is_comptime = false;
|
||||
switch (zir_tags[inst]) {
|
||||
.param => {
|
||||
is_comptime = func_ty_info.paramIsComptime(arg_i);
|
||||
},
|
||||
.param_comptime => {
|
||||
is_comptime = true;
|
||||
},
|
||||
.param_anytype => {
|
||||
is_comptime = func_ty_info.paramIsComptime(arg_i);
|
||||
},
|
||||
.param_anytype_comptime => {
|
||||
is_comptime = true;
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
|
||||
// We populate the Type here regardless because it is needed by
|
||||
// `GenericCallAdapter.eql` as well as function body analysis.
|
||||
// Whether it is anytype is communicated by `isAnytypeParam`.
|
||||
const arg = child_sema.inst_map.get(inst).?;
|
||||
const copied_arg_ty = try child_sema.typeOf(arg).copy(new_decl_arena_allocator);
|
||||
|
||||
if (try sema.typeRequiresComptime(copied_arg_ty)) {
|
||||
is_comptime = true;
|
||||
}
|
||||
|
||||
if (is_comptime) {
|
||||
const arg_val = (child_sema.resolveMaybeUndefValAllowVariables(arg) catch unreachable).?;
|
||||
child_sema.comptime_args[arg_i] = .{
|
||||
.ty = copied_arg_ty,
|
||||
.val = try arg_val.copy(new_decl_arena_allocator),
|
||||
};
|
||||
} else {
|
||||
child_sema.comptime_args[arg_i] = .{
|
||||
.ty = copied_arg_ty,
|
||||
.val = Value.initTag(.generic_poison),
|
||||
};
|
||||
}
|
||||
|
||||
arg_i += 1;
|
||||
}
|
||||
|
||||
try wip_captures.finalize();
|
||||
|
||||
// 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);
|
||||
// If the call evaluated to a return type that requires comptime, never mind
|
||||
// our generic instantiation. Instead we need to perform a comptime call.
|
||||
const new_fn_info = new_decl.ty.fnInfo();
|
||||
if (try sema.typeRequiresComptime(new_fn_info.return_type)) {
|
||||
return error.ComptimeReturn;
|
||||
}
|
||||
// Similarly, if the call evaluated to a generic type we need to instead
|
||||
// call it inline.
|
||||
if (new_fn_info.is_generic or new_fn_info.cc == .Inline) {
|
||||
return error.GenericPoison;
|
||||
}
|
||||
|
||||
new_decl.val = try Value.Tag.function.create(new_decl_arena_allocator, new_func);
|
||||
new_decl.@"align" = 0;
|
||||
new_decl.has_tv = true;
|
||||
new_decl.owns_tv = true;
|
||||
new_decl.analysis = .complete;
|
||||
|
||||
log.debug("generic function '{s}' instantiated with type {}", .{
|
||||
new_decl.name, new_decl.ty.fmtDebug(),
|
||||
});
|
||||
|
||||
// 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_index);
|
||||
try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func });
|
||||
return new_func;
|
||||
}
|
||||
|
||||
fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
|
||||
if (!ty.isSimpleTuple()) return;
|
||||
const tuple = ty.tupleFields();
|
||||
@@ -11506,6 +11551,7 @@ fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.I
|
||||
.dbg_block_begin,
|
||||
.dbg_block_end,
|
||||
.dbg_stmt,
|
||||
.save_err_ret_index,
|
||||
=> {},
|
||||
.@"unreachable" => break inst,
|
||||
else => return,
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
const std = @import("std");
|
||||
const Version = std.SemanticVersion;
|
||||
const print = @import("std").debug.print;
|
||||
|
||||
fn readVersion() Version {
|
||||
const version_file = "foo";
|
||||
const len = std.mem.indexOfAny(u8, version_file, " \n") orelse version_file.len;
|
||||
const version_string = version_file[0..len];
|
||||
return Version.parse(version_string) catch unreachable;
|
||||
}
|
||||
|
||||
const version: Version = readVersion();
|
||||
pub export fn entry() void {
|
||||
print("Version {}.{}.{}+{?s}\n", .{ version.major, version.minor, version.patch, version.build });
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=llvm
|
||||
// target=native
|
||||
//
|
||||
// :9:48: error: caught unexpected error 'InvalidVersion'
|
||||
// :12:37: note: called from here
|
||||
27
test/cases/compile_errors/generic_instantiation_failure.zig
Normal file
27
test/cases/compile_errors/generic_instantiation_failure.zig
Normal file
@@ -0,0 +1,27 @@
|
||||
fn List(comptime Head: type, comptime Tail: type) type {
|
||||
return union {
|
||||
const Self = @This();
|
||||
head: Head,
|
||||
tail: Tail,
|
||||
|
||||
fn AppendReturnType(comptime item: anytype) type {
|
||||
return List(Head, List(@TypeOf(item), void));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn makeList(item: anytype) List(@TypeOf(item), void) {
|
||||
return List(@TypeOf(item), void){ .head = item };
|
||||
}
|
||||
|
||||
pub export fn entry() void {
|
||||
@TypeOf(makeList(42)).AppendReturnType(64);
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=llvm
|
||||
// target=native
|
||||
//
|
||||
// :18:43: error: value of type 'type' ignored
|
||||
// :18:43: note: all non-void values must be used
|
||||
// :18:43: note: this error can be suppressed by assigning the value to '_'
|
||||
@@ -0,0 +1,14 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub export fn entry() void {
|
||||
var ohnoes: *usize = undefined;
|
||||
_ = sliceAsBytes(ohnoes);
|
||||
}
|
||||
fn sliceAsBytes(slice: anytype) std.meta.trait.isPtrTo(.Array)(@TypeOf(slice)) {}
|
||||
|
||||
|
||||
// error
|
||||
// backend=llvm
|
||||
// target=native
|
||||
//
|
||||
// :7:63: error: expected type 'type', found 'bool'
|
||||
Reference in New Issue
Block a user