frontend: make fn calls byval; fix false positive isNonErr

This commit does two things which seem unrelated at first, but,
together, solve a miscompilation, and potentially slightly speed up
compiler perf, at the expense of making #2765 trickier to implement in
the future.

Sema: avoid returning a false positive for whether an inferred error set
is comptime-known to be empty.

AstGen: mark function calls as not being interested in a result
location. This prevents the test case "ret_ptr doesn't cause own
inferred error set to be resolved" from being regressed. If we want to
accept and implement #2765 in the future, it will require solving this
problem a different way, but the principle of YAGNI tells us to go ahead
with this change.

Old ZIR looks like this:

  %97 = ret_ptr()
  %101 = store_node(%97, %100)
  %102 = load(%97)
  %103 = ret_is_non_err(%102)

New ZIR looks like this:

  %97 = ret_type()
  %101 = as_node(%97, %100)
  %102 = ret_is_non_err(%101)

closes #15669
This commit is contained in:
Andrew Kelley 2023-07-26 22:51:16 -07:00
parent 9a3adeea6e
commit e66190025f
3 changed files with 40 additions and 28 deletions

View file

@ -9536,16 +9536,19 @@ fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_
.@"for", // This variant always has an else expression. .@"for", // This variant always has an else expression.
.@"switch", .@"switch",
.switch_comma, .switch_comma,
.call_one,
.call_one_comma,
.async_call_one, .async_call_one,
.async_call_one_comma, .async_call_one_comma,
.call,
.call_comma,
.async_call, .async_call,
.async_call_comma, .async_call_comma,
=> return true, => return true,
// https://github.com/ziglang/zig/issues/2765 would change this.
.call_one,
.call_one_comma,
.call,
.call_comma,
=> return false,
.block_two, .block_two,
.block_two_semicolon, .block_two_semicolon,
.block, .block,

View file

@ -30732,18 +30732,10 @@ fn analyzeIsNonErrComptimeOnly(
else => return .none, else => return .none,
}, },
} }
for (ies.inferred_error_sets.keys()) |other_ies_index| { // We do not have a comptime answer because this inferred error
if (set_ty == other_ies_index) continue; // set is not resolved, and an instruction later in this function
const other_resolved = // body may or may not cause an error to be added to this set.
try sema.resolveInferredErrorSet(block, src, other_ies_index); return .none;
if (other_resolved == .anyerror_type) {
ies.resolved = .anyerror_type;
return .none;
}
if (ip.indexToKey(other_resolved).error_set_type.names.len != 0)
return .none;
}
return .bool_true;
}, },
else => switch (ip.indexToKey(set_ty)) { else => switch (ip.indexToKey(set_ty)) {
.error_set_type => |error_set_type| { .error_set_type => |error_set_type| {
@ -30771,18 +30763,10 @@ fn analyzeIsNonErrComptimeOnly(
else => return .none, else => return .none,
}, },
} }
for (ies.inferred_error_sets.keys()) |other_ies_index| { // We do not have a comptime answer because this inferred error
if (set_ty == other_ies_index) continue; // set is not resolved, and an instruction later in this function
const other_resolved = // body may or may not cause an error to be added to this set.
try sema.resolveInferredErrorSet(block, src, other_ies_index); return .none;
if (other_resolved == .anyerror_type) {
ies.resolved = .anyerror_type;
return .none;
}
if (ip.indexToKey(other_resolved).error_set_type.names.len != 0)
return .none;
}
return .bool_true;
} }
} }
const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty); const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty);

View file

@ -938,3 +938,28 @@ test "returning an error union containing a type with no runtime bits" {
var zero_byte: ZeroByteType = undefined; var zero_byte: ZeroByteType = undefined;
(&zero_byte).* = try ZeroByteType.init(); (&zero_byte).* = try ZeroByteType.init();
} }
test "try used in recursive function with inferred error set" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const Value = union(enum) {
values: []const @This(),
b,
fn x(value: @This()) !void {
switch (value.values[0]) {
.values => return try x(value.values[0]),
.b => return error.a,
}
}
};
const a = Value{
.values = &[1]Value{
.{
.values = &[1]Value{.{ .b = {} }},
},
},
};
try expectError(error.a, Value.x(a));
}