Skip to content

Commit 09a5758

Browse files
committed
compiler: preserve result type information through address-of operator
This commit introduces the new `ref_coerced_ty` result type into AstGen. This represents a expression which we want to treat as an lvalue, and the pointer will be coerced to a given type. This change gives known result types to many expressions, in particular struct and array initializations. This allows certain casts to work which previously required explicitly specifying types via `@as`. It also eliminates our dependence on anonymous struct types for expressions of the form `&.{ ... }` - this paves the way for ziglang#16865, and also results in less Sema magic happening for such initializations, also leading to potentially better runtime code. As part of these changes, this commit also implements ziglang#17194 by disallowing RLS on explicitly-typed struct and array initializations. Apologies for linking these changes - it seemed rather pointless to try and separate them, since they both make big changes to struct and array initializations in AstGen. The rationale for this change can be found in the proposal - in essence, performing RLS whilst maintaining the semantics of the intermediary type is a very difficult problem to solve. This allowed the problematic `coerce_result_ptr` ZIR instruction to be completely eliminated, which in turn also simplified the logic for inferred allocations in Sema - thanks to this, we almost break even on line count! In doing this, the ZIR instructions surrounding these initializations have been restructured - some have been added and removed, and others renamed for clarity (and their semantics changed slightly). In order to optimize ZIR tag count, the `struct_init_anon_ref` and `array_init_anon_ref` instructions have been removed in favour of using `ref` on a standard anonymous value initialization, since these instructions are now virtually never used. Lastly, it's worth noting that this commit introduces a slightly strange source of generic poison types: in the expression `@as(*anyopaque, &x)`, the sub-expression `x` has a generic poison result type, despite no generic code being involved. This turns out to be a logical choice, because we don't know the result type for `x`, and the generic poison type represents precisely this case, providing the semantics we need. Resolves: ziglang#16512 Resolves: ziglang#17194
1 parent 01906a3 commit 09a5758

35 files changed

+1251
-1025
lines changed

lib/std/debug.zig

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,12 @@ pub const StackIterator = struct {
514514

515515
return StackIterator{
516516
.first_address = first_address,
517-
.fp = fp orelse @frameAddress(),
517+
// TODO: this is a workaround for #16876
518+
//.fp = fp orelse @frameAddress(),
519+
.fp = fp orelse blk: {
520+
const fa = @frameAddress();
521+
break :blk fa;
522+
},
518523
};
519524
}
520525

src/AstGen.zig

Lines changed: 318 additions & 305 deletions
Large diffs are not rendered by default.

src/AstRlAnnotate.zig

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -669,17 +669,21 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
669669
=> {
670670
var buf: [2]Ast.Node.Index = undefined;
671671
const full = tree.fullArrayInit(&buf, node).?;
672-
const have_type = if (full.ast.type_expr != 0) have_type: {
672+
673+
if (full.ast.type_expr != 0) {
674+
// Explicitly typed init does not participate in RLS
673675
_ = try astrl.expr(full.ast.type_expr, block, ResultInfo.none);
674-
break :have_type true;
675-
} else ri.have_type;
676-
if (have_type) {
677-
const elem_ri: ResultInfo = .{
678-
.have_type = true,
679-
.have_ptr = ri.have_ptr,
680-
};
681676
for (full.ast.elements) |elem_init| {
682-
_ = try astrl.expr(elem_init, block, elem_ri);
677+
_ = try astrl.expr(elem_init, block, ResultInfo.type_only);
678+
}
679+
return false;
680+
}
681+
682+
if (ri.have_type) {
683+
// Always forward type information
684+
// If we have a result pointer, we use and forward it
685+
for (full.ast.elements) |elem_init| {
686+
_ = try astrl.expr(elem_init, block, ri);
683687
}
684688
return ri.have_ptr;
685689
} else {
@@ -702,17 +706,21 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
702706
=> {
703707
var buf: [2]Ast.Node.Index = undefined;
704708
const full = tree.fullStructInit(&buf, node).?;
705-
const have_type = if (full.ast.type_expr != 0) have_type: {
709+
710+
if (full.ast.type_expr != 0) {
711+
// Explicitly typed init does not participate in RLS
706712
_ = try astrl.expr(full.ast.type_expr, block, ResultInfo.none);
707-
break :have_type true;
708-
} else ri.have_type;
709-
if (have_type) {
710-
const elem_ri: ResultInfo = .{
711-
.have_type = true,
712-
.have_ptr = ri.have_ptr,
713-
};
714713
for (full.ast.fields) |field_init| {
715-
_ = try astrl.expr(field_init, block, elem_ri);
714+
_ = try astrl.expr(field_init, block, ResultInfo.type_only);
715+
}
716+
return false;
717+
}
718+
719+
if (ri.have_type) {
720+
// Always forward type information
721+
// If we have a result pointer, we use and forward it
722+
for (full.ast.fields) |field_init| {
723+
_ = try astrl.expr(field_init, block, ri);
716724
}
717725
return ri.have_ptr;
718726
} else {

src/Autodoc.zig

Lines changed: 3 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2391,34 +2391,6 @@ fn walkInstruction(
23912391
.expr = .{ .@"&" = expr_index },
23922392
};
23932393
},
2394-
.array_init_anon_ref => {
2395-
const pl_node = data[inst_index].pl_node;
2396-
const extra = file.zir.extraData(Zir.Inst.MultiOp, pl_node.payload_index);
2397-
const operands = file.zir.refSlice(extra.end, extra.data.operands_len);
2398-
const array_data = try self.arena.alloc(usize, operands.len);
2399-
2400-
for (operands, 0..) |op, idx| {
2401-
const wr = try self.walkRef(
2402-
file,
2403-
parent_scope,
2404-
parent_src,
2405-
op,
2406-
false,
2407-
call_ctx,
2408-
);
2409-
const expr_index = self.exprs.items.len;
2410-
try self.exprs.append(self.arena, wr.expr);
2411-
array_data[idx] = expr_index;
2412-
}
2413-
2414-
const expr_index = self.exprs.items.len;
2415-
try self.exprs.append(self.arena, .{ .array = array_data });
2416-
2417-
return DocData.WalkResult{
2418-
.typeRef = null,
2419-
.expr = .{ .@"&" = expr_index },
2420-
};
2421-
},
24222394
.float => {
24232395
const float = data[inst_index].float;
24242396
return DocData.WalkResult{
@@ -2709,9 +2681,7 @@ fn walkInstruction(
27092681
.expr = .{ .declRef = decl_status },
27102682
};
27112683
},
2712-
.field_val, .field_ptr, .field_type => {
2713-
// TODO: field type uses Zir.Inst.FieldType, it just happens to have the
2714-
// same layout as Zir.Inst.Field :^)
2684+
.field_val, .field_ptr => {
27152685
const pl_node = data[inst_index].pl_node;
27162686
const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index);
27172687

@@ -2730,8 +2700,7 @@ fn walkInstruction(
27302700
};
27312701

27322702
if (tags[lhs] != .field_val and
2733-
tags[lhs] != .field_ptr and
2734-
tags[lhs] != .field_type) break :blk lhs_extra.data.lhs;
2703+
tags[lhs] != .field_ptr) break :blk lhs_extra.data.lhs;
27352704

27362705
lhs_extra = file.zir.extraData(
27372706
Zir.Inst.Field,
@@ -2870,7 +2839,7 @@ fn walkInstruction(
28702839

28712840
const field_name = blk: {
28722841
const field_inst_index = init_extra.data.field_type;
2873-
if (tags[field_inst_index] != .field_type) unreachable;
2842+
if (tags[field_inst_index] != .struct_init_field_type) unreachable;
28742843
const field_pl_node = data[field_inst_index].pl_node;
28752844
const field_extra = file.zir.extraData(
28762845
Zir.Inst.FieldType,

0 commit comments

Comments
 (0)