aboutsummaryrefslogtreecommitdiff
path: root/src/Module.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-08-04 21:11:31 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-08-04 21:11:31 -0700
commitd4468affb751668e156230c32b29c84684825b4f (patch)
tree3394fc54a11c8c6c01783d7e5ee753c87ce0feda /src/Module.zig
parent382d201781eb57d9e950ad07ce814adc5a68b329 (diff)
downloadzig-d4468affb751668e156230c32b29c84684825b4f.tar.gz
zig-d4468affb751668e156230c32b29c84684825b4f.zip
stage2 generics improvements: anytype and param type exprs
AstGen result locations now have a `coerced_ty` tag which is the same as `ty` except it assumes that Sema will do a coercion, so it does not redundantly add an `as` instruction into the ZIR code. This results in cleaner ZIR and about a 14% reduction of ZIR bytes. param and param_comptime ZIR instructions now have a block body for their type expressions. This allows Sema to skip evaluation of the block in the case that the parameter is comptime-provided. It also allows a new mechanism to function: when evaluating type expressions of generic functions, if it would depend on another parameter, it returns `error.GenericPoison` which bubbles up and then is caught by the param/param_comptime instruction and then handled. This allows parameters to be evaluated independently so that the type info for functions which have comptime or anytype parameters will still have types populated for parameters that do not depend on values of previous parameters (because evaluation of their param blocks will return successfully instead of `error.GenericPoison`). It also makes iteration over the block that contains function parameters slightly more efficient since it now only contains the param instructions. Finally, it fixes the case where a generic function type expression contains a function prototype. Formerly, this situation would cause shared state to clobber each other; now it is in a proper tree structure so that can't happen. This fix also required adding a field to Sema `comptime_args_fn_inst` to make sure that the `comptime_args` field passed into Sema is applied to the correct `func` instruction. Source location for `node_offset_asm_ret_ty` is fixed; it was pointing at the asm output name rather than the return type as intended. Generic function instantiation is fixed, notably with respect to parameter type expressions that depend on previous parameters, and with respect to types which must be always comptime-known. This involves passing all the comptime arguments at a callsite of a generic function, and allowing the generic function semantic analysis to coerce the values to the proper types (since it has access to the evaluated parameter type expressions) and then decide based on the type whether the parameter is runtime known or not. In the case of explicitly marked `comptime` parameters, there is a check at the semantic analysis of the `call` instruction. Semantic analysis of `call` instructions does type coercion on the arguments, which is needed both for generic functions and to make up for using `coerced_ty` result locations (mentioned above). Tasks left in this branch: * Implement the memoization table. * Add test coverage. * Improve error reporting and source locations for compile errors.
Diffstat (limited to 'src/Module.zig')
-rw-r--r--src/Module.zig52
1 files changed, 42 insertions, 10 deletions
diff --git a/src/Module.zig b/src/Module.zig
index 184ea617b1..2c3e745c11 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -1173,6 +1173,8 @@ pub const Scope = struct {
/// for the one that will be the same for all Block instances.
src_decl: *Decl,
instructions: ArrayListUnmanaged(Air.Inst.Index),
+ // `param` instructions are collected here to be used by the `func` instruction.
+ params: std.ArrayListUnmanaged(Param) = .{},
label: ?*Label = null,
inlining: ?*Inlining,
/// If runtime_index is not 0 then one of these is guaranteed to be non null.
@@ -1187,6 +1189,12 @@ pub const Scope = struct {
/// when null, it is determined by build mode, changed by @setRuntimeSafety
want_safety: ?bool = null,
+ const Param = struct {
+ /// `noreturn` means `anytype`.
+ ty: Type,
+ is_comptime: bool,
+ };
+
/// This `Block` maps a block ZIR instruction to the corresponding
/// AIR instruction for break instruction analysis.
pub const Label = struct {
@@ -1634,8 +1642,11 @@ pub const SrcLoc = struct {
.@"asm" => tree.asmFull(node),
else => unreachable,
};
+ const asm_output = full.outputs[0];
+ const node_datas = tree.nodes.items(.data);
+ const ret_ty_node = node_datas[asm_output].lhs;
const main_tokens = tree.nodes.items(.main_token);
- const tok_index = main_tokens[full.outputs[0]];
+ const tok_index = main_tokens[ret_ty_node];
const token_starts = tree.tokens.items(.start);
return token_starts[tok_index];
},
@@ -2099,7 +2110,20 @@ pub const LazySrcLoc = union(enum) {
};
pub const SemaError = error{ OutOfMemory, AnalysisFail };
-pub const CompileError = error{ OutOfMemory, AnalysisFail, NeededSourceLocation };
+pub const CompileError = error{
+ OutOfMemory,
+ /// When this is returned, the compile error for the failure has already been recorded.
+ AnalysisFail,
+ /// Returned when a compile error needed to be reported but a provided LazySrcLoc was set
+ /// to the `unneeded` tag. The source location was, in fact, needed. It is expected that
+ /// somewhere up the call stack, the operation will be retried after doing expensive work
+ /// to compute a source location.
+ NeededSourceLocation,
+ /// A Type or Value was needed to be used during semantic analysis, but it was not available
+ /// because the function is generic. This is only seen when analyzing the body of a param
+ /// instruction.
+ GenericPoison,
+};
pub fn deinit(mod: *Module) void {
const gpa = mod.gpa;
@@ -2796,14 +2820,16 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {
}
return error.AnalysisFail;
},
- else => {
+ error.NeededSourceLocation => unreachable,
+ error.GenericPoison => unreachable,
+ else => |e| {
decl.analysis = .sema_failure_retryable;
try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1);
mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
mod.gpa,
decl.srcLoc(),
"unable to analyze: {s}",
- .{@errorName(err)},
+ .{@errorName(e)},
));
return error.AnalysisFail;
},
@@ -2982,7 +3008,10 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
.inlining = null,
.is_comptime = true,
};
- defer block_scope.instructions.deinit(gpa);
+ defer {
+ block_scope.instructions.deinit(gpa);
+ block_scope.params.deinit(gpa);
+ }
const zir_block_index = decl.zirBlockIndex();
const inst_data = zir_datas[zir_block_index].pl_node;
@@ -3669,7 +3698,8 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
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;
+ var runtime_param_index: usize = 0;
+ var total_param_index: usize = 0;
for (fn_info.param_body) |inst| {
const name = switch (zir_tags[inst]) {
.param, .param_comptime => blk: {
@@ -3686,16 +3716,16 @@ 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];
+ const arg_tv = comptime_args[total_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;
+ total_param_index += 1;
continue;
}
}
- const param_type = fn_ty.fnParamType(param_index);
+ const param_type = fn_ty.fnParamType(runtime_param_index);
const ty_ref = try sema.addType(param_type);
const arg_index = @intCast(u32, sema.air_instructions.len);
inner_block.instructions.appendAssumeCapacity(arg_index);
@@ -3707,7 +3737,8 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
} },
});
sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));
- param_index += 1;
+ total_param_index += 1;
+ runtime_param_index += 1;
}
func.state = .in_progress;
@@ -3715,6 +3746,7 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
_ = sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
error.NeededSourceLocation => unreachable,
+ error.GenericPoison => unreachable,
else => |e| return e,
};