authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-12-17 04:39:09+01:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-12-21 01:41:50+01:00
log9d6c45f6979543607a7064be7155afa409be956a
treec92d89093e998cdca6fbe3c546e290ea00248366
parenta2958a4ede0af4b4559eeb142c0400ae640db63e

stage2: inferred error set coercion


3 files changed, 183 insertions(+), 42 deletions(-)

src/Module.zig+9-6
......@@ -1239,14 +1239,17 @@ pub const Fn = struct {
12391239 /// When the inferred error set is fully resolved, this map contains all the errors that the function might return.
12401240 errors: std.StringHashMapUnmanaged(void) = .{},
12411241
1242 /// Other functions with inferred error sets which the inferred error set of this
1243 /// function should include.
1244 functions: std.AutoHashMapUnmanaged(*Fn, void) = .{},
1242 /// Other inferred error sets which this inferred error set should include.
1243 inferred_error_sets: std.AutoHashMapUnmanaged(*InferredErrorSet, void) = .{},
12451244
12461245 /// Whether the function returned anyerror. This is true if either of the dependent functions
12471246 /// returns anyerror.
12481247 is_anyerror: bool = false,
12491248
1249 /// Whether this error set is already fully resolved. If true, resolving can skip resolving any dependents
1250 /// of this inferred error set.
1251 is_resolved: bool = false,
1252
12501253 pub fn addErrorSet(self: *InferredErrorSet, gpa: Allocator, err_set_ty: Type) !void {
12511254 switch (err_set_ty.tag()) {
12521255 .error_set => {
......@@ -1260,8 +1263,8 @@ pub const Fn = struct {
12601263 try self.errors.put(gpa, name, {});
12611264 },
12621265 .error_set_inferred => {
1263 const dependent_func = err_set_ty.castTag(.error_set_inferred).?.data.func;
1264 try self.functions.put(gpa, dependent_func, {});
1266 const set = err_set_ty.castTag(.error_set_inferred).?.data;
1267 try self.inferred_error_sets.put(gpa, set, {});
12651268 },
12661269 .error_set_merged => {
12671270 const names = err_set_ty.castTag(.error_set_merged).?.data.keys();
......@@ -1285,7 +1288,7 @@ pub const Fn = struct {
12851288 while (it) |node| {
12861289 const next = node.next;
12871290 node.data.errors.deinit(gpa);
1288 node.data.functions.deinit(gpa);
1291 node.data.inferred_error_sets.deinit(gpa);
12891292 gpa.destroy(node);
12901293 it = next;
12911294 }
src/Sema.zig+144-35
......@@ -5207,9 +5207,6 @@ fn funcCommon(
52075207 .rbrace_line = src_locs.rbrace_line,
52085208 .lbrace_column = @truncate(u16, src_locs.columns),
52095209 .rbrace_column = @truncate(u16, src_locs.columns >> 16),
5210 .inferred_error_sets = .{
5211 .first = maybe_inferred_error_set_node,
5212 },
52135210 };
52145211 if (maybe_inferred_error_set_node) |node| {
52155212 new_func.inferred_error_sets.prepend(node);
......@@ -12193,7 +12190,7 @@ fn coerce(
1219312190 const arena = sema.arena;
1219412191 const target = sema.mod.getTarget();
1219512192
12196 const in_memory_result = coerceInMemoryAllowed(dest_ty, inst_ty, false, target);
12193 const in_memory_result = try sema.coerceInMemoryAllowed(dest_ty, inst_ty, false, target);
1219712194 if (in_memory_result == .ok) {
1219812195 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
1219912196 // Keep the comptime Value representation; take the new type.
......@@ -12252,7 +12249,7 @@ fn coerce(
1225212249 if (inst_ty.isConstPtr() and dest_is_mut) break :single_item;
1225312250 if (inst_ty.isVolatilePtr() and !dest_info.@"volatile") break :single_item;
1225412251 if (inst_ty.ptrAddressSpace() != dest_info.@"addrspace") break :single_item;
12255 switch (coerceInMemoryAllowed(array_elem_ty, ptr_elem_ty, dest_is_mut, target)) {
12252 switch (try sema.coerceInMemoryAllowed(array_elem_ty, ptr_elem_ty, dest_is_mut, target)) {
1225612253 .ok => {},
1225712254 .no_match => break :single_item,
1225812255 }
......@@ -12271,7 +12268,7 @@ fn coerce(
1227112268 if (inst_ty.ptrAddressSpace() != dest_info.@"addrspace") break :src_array_ptr;
1227212269
1227312270 const dst_elem_type = dest_info.pointee_type;
12274 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) {
12271 switch (try sema.coerceInMemoryAllowed(dst_elem_type, array_elem_type, dest_is_mut, target)) {
1227512272 .ok => {},
1227612273 .no_match => break :src_array_ptr,
1227712274 }
......@@ -12310,7 +12307,7 @@ fn coerce(
1231012307 const src_elem_ty = inst_ty.childType();
1231112308 const dest_is_mut = dest_info.mutable;
1231212309 const dst_elem_type = dest_info.pointee_type;
12313 switch (coerceInMemoryAllowed(dst_elem_type, src_elem_ty, dest_is_mut, target)) {
12310 switch (try sema.coerceInMemoryAllowed(dst_elem_type, src_elem_ty, dest_is_mut, target)) {
1231412311 .ok => {},
1231512312 .no_match => break :src_c_ptr,
1231612313 }
......@@ -12453,7 +12450,13 @@ const InMemoryCoercionResult = enum {
1245312450/// * sentinel-terminated pointers can coerce into `[*]`
1245412451/// TODO improve this function to report recursive compile errors like it does in stage1.
1245512452/// look at the function types_match_const_cast_only
12456fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target: std.Target) InMemoryCoercionResult {
12453fn coerceInMemoryAllowed(
12454 sema: *Sema,
12455 dest_ty: Type,
12456 src_ty: Type,
12457 dest_is_mut: bool,
12458 target: std.Target
12459) CompileError!InMemoryCoercionResult {
1245712460 if (dest_ty.eql(src_ty))
1245812461 return .ok;
1245912462
......@@ -12462,32 +12465,35 @@ fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target:
1246212465 var src_buf: Type.Payload.ElemType = undefined;
1246312466 if (dest_ty.ptrOrOptionalPtrTy(&dest_buf)) |dest_ptr_ty| {
1246412467 if (src_ty.ptrOrOptionalPtrTy(&src_buf)) |src_ptr_ty| {
12465 return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target);
12468 return try sema.coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target);
1246612469 }
1246712470 }
1246812471
1246912472 // Slices
1247012473 if (dest_ty.isSlice() and src_ty.isSlice()) {
12471 return coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target);
12474 return try sema.coerceInMemoryAllowedPtrs(dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target);
1247212475 }
1247312476
12477 const dest_tag = dest_ty.zigTypeTag();
12478 const src_tag = src_ty.zigTypeTag();
12479
1247412480 // Functions
12475 if (dest_ty.zigTypeTag() == .Fn and src_ty.zigTypeTag() == .Fn) {
12476 return coerceInMemoryAllowedFns(dest_ty, src_ty, target);
12481 if (dest_tag == .Fn and src_tag == .Fn) {
12482 return try sema.coerceInMemoryAllowedFns(dest_ty, src_ty, target);
1247712483 }
1247812484
1247912485 // Error Unions
12480 if (dest_ty.zigTypeTag() == .ErrorUnion and src_ty.zigTypeTag() == .ErrorUnion) {
12481 const child = coerceInMemoryAllowed(dest_ty.errorUnionPayload(), src_ty.errorUnionPayload(), dest_is_mut, target);
12486 if (dest_tag == .ErrorUnion and src_tag == .ErrorUnion) {
12487 const child = try sema.coerceInMemoryAllowed(dest_ty.errorUnionPayload(), src_ty.errorUnionPayload(), dest_is_mut, target);
1248212488 if (child == .no_match) {
1248312489 return child;
1248412490 }
12485 return coerceInMemoryAllowed(dest_ty.errorUnionSet(), src_ty.errorUnionSet(), dest_is_mut, target);
12491 return try sema.coerceInMemoryAllowed(dest_ty.errorUnionSet(), src_ty.errorUnionSet(), dest_is_mut, target);
1248612492 }
1248712493
1248812494 // Error Sets
12489 if (dest_ty.zigTypeTag() == .ErrorSet and src_ty.zigTypeTag() == .ErrorSet) {
12490 return coerceInMemoryAllowedErrorSets(dest_ty, src_ty);
12495 if (dest_tag == .ErrorSet and src_tag == .ErrorSet) {
12496 return try sema.coerceInMemoryAllowedErrorSets(dest_ty, src_ty);
1249112497 }
1249212498
1249312499 // TODO: arrays
......@@ -12498,14 +12504,16 @@ fn coerceInMemoryAllowed(dest_ty: Type, src_ty: Type, dest_is_mut: bool, target:
1249812504}
1249912505
1250012506fn coerceInMemoryAllowedErrorSets(
12507 sema: *Sema,
1250112508 dest_ty: Type,
1250212509 src_ty: Type,
12503) InMemoryCoercionResult {
12504 // Coercion to `anyerror`. Note that this check can return false positives
12510) !InMemoryCoercionResult {
12511 // Coercion to `anyerror`. Note that this check can return false negatives
1250512512 // in case the error sets did not get resolved.
1250612513 if (dest_ty.isAnyError()) {
1250712514 return .ok;
1250812515 }
12516
1250912517 // If both are inferred error sets of functions, and
1251012518 // the dest includes the source function, the coercion is OK.
1251112519 // This check is important because it works without forcing a full resolution
......@@ -12515,21 +12523,85 @@ fn coerceInMemoryAllowedErrorSets(
1251512523 const src_func = src_payload.data.func;
1251612524 const dst_func = dst_payload.data.func;
1251712525
12518 if (src_func == dst_func or dst_payload.data.functions.contains(src_func)) {
12526 if (src_func == dst_func or dst_payload.data.inferred_error_sets.contains(src_payload.data)) {
1251912527 return .ok;
1252012528 }
12529 return .no_match;
1252112530 }
1252212531 }
1252312532
12524 // TODO full error set resolution and compare sets by names.
12533 if (dest_ty.castTag(.error_set_inferred)) |payload| {
12534 try sema.resolveInferredErrorSet(payload.data);
12535 // isAnyError might have changed from a false negative to a true positive after resolution.
12536 if (dest_ty.isAnyError()) {
12537 return .ok;
12538 }
12539 }
12540
12541 switch (src_ty.tag()) {
12542 .error_set_inferred => {
12543 const src_data = src_ty.castTag(.error_set_inferred).?.data;
12544
12545 try sema.resolveInferredErrorSet(src_data);
12546 // src anyerror status might have changed after the resolution.
12547 if (src_ty.isAnyError()) {
12548 // dest_ty.isAnyError() == true is already checked for at this point.
12549 return .no_match;
12550 }
12551
12552 var it = src_data.errors.keyIterator();
12553 while (it.next()) |name_ptr| {
12554 if (!dest_ty.errorSetHasField(name_ptr.*)) {
12555 return .no_match;
12556 }
12557 }
12558
12559 return .ok;
12560 },
12561 .error_set_single => {
12562 const name = src_ty.castTag(.error_set_single).?.data;
12563 if (dest_ty.errorSetHasField(name)) {
12564 return .ok;
12565 }
12566 },
12567 .error_set_merged => {
12568 const names = src_ty.castTag(.error_set_merged).?.data.keys();
12569 for (names) |name| {
12570 if (!dest_ty.errorSetHasField(name)) {
12571 return .no_match;
12572 }
12573 }
12574
12575 return .ok;
12576 },
12577 .error_set => {
12578 const names = src_ty.castTag(.error_set).?.data.names.keys();
12579 for (names) |name| {
12580 if (!dest_ty.errorSetHasField(name)) {
12581 return .no_match;
12582 }
12583 }
12584
12585 return .ok;
12586 },
12587 .anyerror => switch (dest_ty.tag()) {
12588 .error_set_inferred => return .no_match, // Caught by dest.isAnyError() above.
12589 .error_set_single, .error_set_merged, .error_set => {},
12590 .anyerror => unreachable, // Filtered out above.
12591 else => unreachable,
12592 },
12593 else => unreachable,
12594 }
12595
1252512596 return .no_match;
1252612597}
1252712598
1252812599fn coerceInMemoryAllowedFns(
12600 sema: *Sema,
1252912601 dest_ty: Type,
1253012602 src_ty: Type,
1253112603 target: std.Target,
12532) InMemoryCoercionResult {
12604) !InMemoryCoercionResult {
1253312605 const dest_info = dest_ty.fnInfo();
1253412606 const src_info = src_ty.fnInfo();
1253512607
......@@ -12542,7 +12614,7 @@ fn coerceInMemoryAllowedFns(
1254212614 }
1254312615
1254412616 if (!src_info.return_type.isNoReturn()) {
12545 const rt = coerceInMemoryAllowed(dest_info.return_type, src_info.return_type, false, target);
12617 const rt = try sema.coerceInMemoryAllowed(dest_info.return_type, src_info.return_type, false, target);
1254612618 if (rt == .no_match) {
1254712619 return rt;
1254812620 }
......@@ -12562,7 +12634,7 @@ fn coerceInMemoryAllowedFns(
1256212634 // TODO: nolias
1256312635
1256412636 // Note: Cast direction is reversed here.
12565 const param = coerceInMemoryAllowed(src_param_ty, dest_param_ty, false, target);
12637 const param = try sema.coerceInMemoryAllowed(src_param_ty, dest_param_ty, false, target);
1256612638 if (param == .no_match) {
1256712639 return param;
1256812640 }
......@@ -12576,17 +12648,18 @@ fn coerceInMemoryAllowedFns(
1257612648}
1257712649
1257812650fn coerceInMemoryAllowedPtrs(
12651 sema: *Sema,
1257912652 dest_ty: Type,
1258012653 src_ty: Type,
1258112654 dest_ptr_ty: Type,
1258212655 src_ptr_ty: Type,
1258312656 dest_is_mut: bool,
1258412657 target: std.Target,
12585) InMemoryCoercionResult {
12658) !InMemoryCoercionResult {
1258612659 const dest_info = dest_ptr_ty.ptrInfo().data;
1258712660 const src_info = src_ptr_ty.ptrInfo().data;
1258812661
12589 const child = coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target);
12662 const child = try sema.coerceInMemoryAllowed(dest_info.pointee_type, src_info.pointee_type, dest_info.mutable, target);
1259012663 if (child == .no_match) {
1259112664 return child;
1259212665 }
......@@ -13307,7 +13380,7 @@ fn coerceVectorInMemory(
1330713380 const target = sema.mod.getTarget();
1330813381 const dest_elem_ty = dest_ty.childType();
1330913382 const inst_elem_ty = inst_ty.childType();
13310 const in_memory_result = coerceInMemoryAllowed(dest_elem_ty, inst_elem_ty, false, target);
13383 const in_memory_result = try sema.coerceInMemoryAllowed(dest_elem_ty, inst_elem_ty, false, target);
1331113384 if (in_memory_result != .ok) {
1331213385 // TODO recursive error notes for coerceInMemoryAllowed failure
1331313386 return sema.fail(block, inst_src, "expected {}, found {}", .{ dest_ty, inst_ty });
......@@ -13910,11 +13983,11 @@ fn wrapErrorUnion(
1391013983 }
1391113984 },
1391213985 .error_set_inferred => ok: {
13986 const expected_name = val.castTag(.@"error").?.data.name;
1391313987 const data = dest_err_set_ty.castTag(.error_set_inferred).?.data;
13988 try sema.resolveInferredErrorSet(data);
1391413989 if (data.is_anyerror) break :ok;
13915 const expected_name = val.castTag(.@"error").?.data.name;
1391613990 if (data.errors.contains(expected_name)) break :ok;
13917 // TODO error set resolution here before emitting a compile error
1391813991 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
1391913992 },
1392013993 else => unreachable,
......@@ -14059,12 +14132,12 @@ fn resolvePeerTypes(
1405914132 .Optional => {
1406014133 var opt_child_buf: Type.Payload.ElemType = undefined;
1406114134 const opt_child_ty = candidate_ty.optionalChild(&opt_child_buf);
14062 if (coerceInMemoryAllowed(opt_child_ty, chosen_ty, false, target) == .ok) {
14135 if ((try sema.coerceInMemoryAllowed(opt_child_ty, chosen_ty, false, target)) == .ok) {
1406314136 chosen = candidate;
1406414137 chosen_i = candidate_i + 1;
1406514138 continue;
1406614139 }
14067 if (coerceInMemoryAllowed(chosen_ty, opt_child_ty, false, target) == .ok) {
14140 if ((try sema.coerceInMemoryAllowed(chosen_ty, opt_child_ty, false, target)) == .ok) {
1406814141 any_are_null = true;
1406914142 continue;
1407014143 }
......@@ -14087,10 +14160,10 @@ fn resolvePeerTypes(
1408714160 .Optional => {
1408814161 var opt_child_buf: Type.Payload.ElemType = undefined;
1408914162 const opt_child_ty = chosen_ty.optionalChild(&opt_child_buf);
14090 if (coerceInMemoryAllowed(opt_child_ty, candidate_ty, false, target) == .ok) {
14163 if ((try sema.coerceInMemoryAllowed(opt_child_ty, candidate_ty, false, target)) == .ok) {
1409114164 continue;
1409214165 }
14093 if (coerceInMemoryAllowed(candidate_ty, opt_child_ty, false, target) == .ok) {
14166 if ((try sema.coerceInMemoryAllowed(candidate_ty, opt_child_ty, false, target)) == .ok) {
1409414167 any_are_null = true;
1409514168 chosen = candidate;
1409614169 chosen_i = candidate_i + 1;
......@@ -14256,6 +14329,42 @@ fn resolveBuiltinTypeFields(
1425614329 return sema.resolveTypeFields(block, src, resolved_ty);
1425714330}
1425814331
14332fn resolveInferredErrorSet(sema: *Sema, inferred_error_set: *Module.Fn.InferredErrorSet) CompileError!void {
14333 // Ensuring that a particular decl is analyzed does not neccesarily mean that
14334 // it's error set is inferred, so traverse all of them to get the complete
14335 // picture.
14336 // Note: We want to skip re-resolving the current function, as recursion
14337 // doesn't change the error set. We can just check for state == .in_progress for this.
14338 // TODO: Is that correct?
14339
14340 if (inferred_error_set.is_resolved) {
14341 return;
14342 }
14343
14344 var it = inferred_error_set.inferred_error_sets.keyIterator();
14345 while (it.next()) |other_error_set_ptr| {
14346 const func = other_error_set_ptr.*.func;
14347 const decl = func.*.owner_decl;
14348
14349 if (func.*.state == .in_progress) {
14350 // Recursion, doesn't alter current error set, keep going.
14351 continue;
14352 }
14353
14354 try sema.ensureDeclAnalyzed(decl); // To ensure that all dependencies are properly added to the set.
14355 try sema.resolveInferredErrorSet(other_error_set_ptr.*);
14356
14357 var error_it = other_error_set_ptr.*.errors.keyIterator();
14358 while (error_it.next()) |entry| {
14359 try inferred_error_set.errors.put(sema.gpa, entry.*, {});
14360 }
14361 if (other_error_set_ptr.*.is_anyerror)
14362 inferred_error_set.is_anyerror = true;
14363 }
14364
14365 inferred_error_set.is_resolved = true;
14366}
14367
1425914368fn semaStructFields(
1426014369 mod: *Module,
1426114370 struct_obj: *Module.Struct,
......@@ -15218,8 +15327,8 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr
1521815327 // We have a Value that lines up in virtual memory exactly with what we want to load.
1521915328 // If the Type is in-memory coercable to `load_ty`, it may be returned without modifications.
1522015329 const coerce_in_mem_ok =
15221 coerceInMemoryAllowed(load_ty, parent.ty, false, target) == .ok or
15222 coerceInMemoryAllowed(parent.ty, load_ty, false, target) == .ok;
15330 (try sema.coerceInMemoryAllowed(load_ty, parent.ty, false, target)) == .ok or
15331 (try sema.coerceInMemoryAllowed(parent.ty, load_ty, false, target)) == .ok;
1522315332 if (coerce_in_mem_ok) {
1522415333 if (parent.is_mutable) {
1522515334 // The decl whose value we are obtaining here may be overwritten with
src/type.zig+30-1
......@@ -1203,7 +1203,7 @@ pub const Type = extern union {
12031203 return writer.writeAll(std.mem.sliceTo(error_set.owner_decl.name, 0));
12041204 },
12051205 .error_set_inferred => {
1206 const func = ty.castTag(.error_set_inferred).?.data;
1206 const func = ty.castTag(.error_set_inferred).?.data.func;
12071207 return writer.print("(inferred error set of {s})", .{func.owner_decl.name});
12081208 },
12091209 .error_set_merged => {
......@@ -2874,6 +2874,35 @@ pub const Type = extern union {
28742874 };
28752875 }
28762876
2877 /// Returns whether ty, which must be an error set, includes an error `name`.
2878 /// Might return a false negative if `ty` is an inferred error set and not fully
2879 /// resolved yet.
2880 pub fn errorSetHasField(ty: Type, name: []const u8) bool {
2881 if (ty.isAnyError()) {
2882 return true;
2883 }
2884
2885 switch (ty.tag()) {
2886 .error_set_single => {
2887 const data = ty.castTag(.error_set_single).?.data;
2888 return std.mem.eql(u8, data, name);
2889 },
2890 .error_set_inferred => {
2891 const data = ty.castTag(.error_set_inferred).?.data;
2892 return data.errors.contains(name);
2893 },
2894 .error_set_merged => {
2895 const data = ty.castTag(.error_set_merged).?.data;
2896 return data.contains(name);
2897 },
2898 .error_set => {
2899 const data = ty.castTag(.error_set).?.data;
2900 return data.names.contains(name);
2901 },
2902 else => unreachable,
2903 }
2904 }
2905
28772906 /// Asserts the type is an array or vector.
28782907 pub fn arrayLen(ty: Type) u64 {
28792908 return switch (ty.tag()) {