diff --git a/doc/langref.html.in b/doc/langref.html.in
index 627709ec84..b0003a714b 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -1734,6 +1734,43 @@ test "array initialization with function calls" {
{#code_end#}
{#see_also|for|Slices#}
+ {#header_open|Anonymous List Literals#}
+
Similar to {#link|Enum Literals#} and {#link|Anonymous Struct Literals#}
+ the type can be omitted from array literals:
+ {#code_begin|test|anon_list#}
+const std = @import("std");
+const assert = std.debug.assert;
+
+test "anonymous list literal syntax" {
+ var array: [4]u8 = .{11, 22, 33, 44};
+ assert(array[0] == 11);
+ assert(array[1] == 22);
+ assert(array[2] == 33);
+ assert(array[3] == 44);
+}
+ {#code_end#}
+
+ If there is no type in the result location then an anonymous list literal actually
+ turns into a {#link|struct#} with numbered field names:
+
+ {#code_begin|test|infer_list_literal#}
+const std = @import("std");
+const assert = std.debug.assert;
+
+test "fully anonymous list literal" {
+ dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi"});
+}
+
+fn dump(args: var) void {
+ assert(args.@"0" == 1234);
+ assert(args.@"1" == 12.34);
+ assert(args.@"2");
+ assert(args.@"3"[0] == 'h');
+ assert(args.@"3"[1] == 'i');
+}
+ {#code_end#}
+ {#header_close#}
+
{#header_open|Multidimensional Arrays#}
Mutlidimensional arrays can be created by nesting arrays:
@@ -2526,7 +2563,8 @@ test "overaligned pointer to packed struct" {
Don't worry, there will be a good solution for this use case in zig.
{#header_close#}
- {#header_open|struct Naming#}
+
+ {#header_open|Struct Naming#}
Since all structs are anonymous, Zig infers the type name based on a few rules.
- If the struct is in the initialization expression of a variable, it gets named after
@@ -2552,6 +2590,53 @@ fn List(comptime T: type) type {
}
{#code_end#}
{#header_close#}
+
+ {#header_open|Anonymous Struct Literals#}
+
+ Zig allows omitting the struct type of a literal. When the result is {#link|coerced|Type Coercion#},
+ the struct literal will directly instantiate the result location, with no copy:
+
+ {#code_begin|test|struct_result#}
+const std = @import("std");
+const assert = std.debug.assert;
+
+const Point = struct {x: i32, y: i32};
+
+test "anonymous struct literal" {
+ var pt: Point = .{
+ .x = 13,
+ .y = 67,
+ };
+ assert(pt.x == 13);
+ assert(pt.y == 67);
+}
+ {#code_end#}
+
+ The struct type can be inferred. Here the result location does not include a type, and
+ so Zig infers the type:
+
+ {#code_begin|test|struct_anon#}
+const std = @import("std");
+const assert = std.debug.assert;
+
+test "fully anonymous struct" {
+ dump(.{
+ .int = @as(u32, 1234),
+ .float = @as(f64, 12.34),
+ .b = true,
+ .s = "hi",
+ });
+}
+
+fn dump(args: var) void {
+ assert(args.int == 1234);
+ assert(args.float == 12.34);
+ assert(args.b);
+ assert(args.s[0] == 'h');
+ assert(args.s[1] == 'i');
+}
+ {#code_end#}
+ {#header_close#}
{#see_also|comptime|@fieldParentPtr#}
{#header_close#}
{#header_open|enum#}
@@ -2906,6 +2991,32 @@ test "@tagName" {
A {#syntax#}packed union{#endsyntax#} has well-defined in-memory layout and is eligible
to be in a {#link|packed struct#}.
{#header_close#}
+
+ {#header_open|Anonymous Union Literals#}
+
{#link|Anonymous Struct Literals#} syntax can be used to initialize unions without specifying
+ the type:
+ {#code_begin|test|anon_union#}
+const std = @import("std");
+const assert = std.debug.assert;
+
+const Number = union {
+ int: i32,
+ float: f64,
+};
+
+test "anonymous union literal syntax" {
+ var i: Number = .{.int = 42};
+ var f = makeNumber();
+ assert(i.int == 42);
+ assert(f.float == 12.34);
+}
+
+fn makeNumber() Number {
+ return .{.float = 12.34};
+}
+ {#code_end#}
+ {#header_close#}
+
{#header_close#}
{#header_open|blocks#}
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index 8315cd84cf..8052f0b31a 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -90,40 +90,11 @@ pub const Mode = enum {
ReleaseSmall,
};
-/// This data structure is used by the Zig language code generation and
-/// therefore must be kept in sync with the compiler implementation.
-pub const TypeId = enum {
- Type,
- Void,
- Bool,
- NoReturn,
- Int,
- Float,
- Pointer,
- Array,
- Struct,
- ComptimeFloat,
- ComptimeInt,
- Undefined,
- Null,
- Optional,
- ErrorUnion,
- ErrorSet,
- Enum,
- Union,
- Fn,
- BoundFn,
- ArgTuple,
- Opaque,
- Frame,
- AnyFrame,
- Vector,
- EnumLiteral,
-};
+pub const TypeId = @TagType(TypeInfo);
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
-pub const TypeInfo = union(TypeId) {
+pub const TypeInfo = union(enum) {
Type: void,
Void: void,
Bool: void,
diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig
index 9f32c6e54b..e705aea477 100644
--- a/lib/std/zig/ast.zig
+++ b/lib/std/zig/ast.zig
@@ -1648,10 +1648,15 @@ pub const Node = struct {
pub const SuffixOp = struct {
base: Node,
- lhs: *Node,
+ lhs: Lhs,
op: Op,
rtoken: TokenIndex,
+ pub const Lhs = union(enum) {
+ node: *Node,
+ dot: TokenIndex,
+ };
+
pub const Op = union(enum) {
Call: Call,
ArrayAccess: *Node,
@@ -1679,8 +1684,13 @@ pub const Node = struct {
pub fn iterate(self: *SuffixOp, index: usize) ?*Node {
var i = index;
- if (i < 1) return self.lhs;
- i -= 1;
+ switch (self.lhs) {
+ .node => |node| {
+ if (i == 0) return node;
+ i -= 1;
+ },
+ .dot => {},
+ }
switch (self.op) {
.Call => |*call_info| {
@@ -1721,7 +1731,10 @@ pub const Node = struct {
.Call => |*call_info| if (call_info.async_token) |async_token| return async_token,
else => {},
}
- return self.lhs.firstToken();
+ switch (self.lhs) {
+ .node => |node| return node.firstToken(),
+ .dot => |dot| return dot,
+ }
}
pub fn lastToken(self: *const SuffixOp) TokenIndex {
diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig
index 4c6c461c01..6d5d4b5f2d 100644
--- a/lib/std/zig/parse.zig
+++ b/lib/std/zig/parse.zig
@@ -1026,16 +1026,16 @@ fn parseWhileExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
/// CurlySuffixExpr <- TypeExpr InitList?
fn parseCurlySuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
const type_expr = (try parseTypeExpr(arena, it, tree)) orelse return null;
- const init_list = (try parseInitList(arena, it, tree)) orelse return type_expr;
- init_list.cast(Node.SuffixOp).?.lhs = type_expr;
- return init_list;
+ const suffix_op = (try parseInitList(arena, it, tree)) orelse return type_expr;
+ suffix_op.lhs.node = type_expr;
+ return &suffix_op.base;
}
/// InitList
/// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
/// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
/// / LBRACE RBRACE
-fn parseInitList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
+fn parseInitList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node.SuffixOp {
const lbrace = eatToken(it, .LBrace) orelse return null;
var init_list = Node.SuffixOp.Op.InitList.init(arena);
@@ -1064,11 +1064,11 @@ fn parseInitList(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
const node = try arena.create(Node.SuffixOp);
node.* = Node.SuffixOp{
.base = Node{ .id = .SuffixOp },
- .lhs = undefined, // set by caller
+ .lhs = .{.node = undefined}, // set by caller
.op = op,
.rtoken = try expectToken(it, tree, .RBrace),
};
- return &node.base;
+ return node;
}
/// TypeExpr <- PrefixTypeOp* ErrorUnionExpr
@@ -1117,7 +1117,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
while (try parseSuffixOp(arena, it, tree)) |node| {
switch (node.id) {
- .SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,
+ .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{.node = res},
.InfixOp => node.cast(Node.InfixOp).?.lhs = res,
else => unreachable,
}
@@ -1133,7 +1133,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
const node = try arena.create(Node.SuffixOp);
node.* = Node.SuffixOp{
.base = Node{ .id = .SuffixOp },
- .lhs = res,
+ .lhs = .{.node = res},
.op = Node.SuffixOp.Op{
.Call = Node.SuffixOp.Op.Call{
.params = params.list,
@@ -1150,7 +1150,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
while (true) {
if (try parseSuffixOp(arena, it, tree)) |node| {
switch (node.id) {
- .SuffixOp => node.cast(Node.SuffixOp).?.lhs = res,
+ .SuffixOp => node.cast(Node.SuffixOp).?.lhs = .{.node = res},
.InfixOp => node.cast(Node.InfixOp).?.lhs = res,
else => unreachable,
}
@@ -1161,7 +1161,7 @@ fn parseSuffixExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
const call = try arena.create(Node.SuffixOp);
call.* = Node.SuffixOp{
.base = Node{ .id = .SuffixOp },
- .lhs = res,
+ .lhs = .{.node = res},
.op = Node.SuffixOp.Op{
.Call = Node.SuffixOp.Op.Call{
.params = params.list,
@@ -1215,7 +1215,7 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N
return &node.base;
}
if (try parseContainerDecl(arena, it, tree)) |node| return node;
- if (try parseEnumLiteral(arena, it, tree)) |node| return node;
+ if (try parseAnonLiteral(arena, it, tree)) |node| return node;
if (try parseErrorSetDecl(arena, it, tree)) |node| return node;
if (try parseFloatLiteral(arena, it, tree)) |node| return node;
if (try parseFnProto(arena, it, tree)) |node| return node;
@@ -1494,16 +1494,28 @@ fn parseAsmExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
}
/// DOT IDENTIFIER
-fn parseEnumLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
+fn parseAnonLiteral(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
const dot = eatToken(it, .Period) orelse return null;
- const name = try expectToken(it, tree, .Identifier);
- const node = try arena.create(Node.EnumLiteral);
- node.* = Node.EnumLiteral{
- .base = Node{ .id = .EnumLiteral },
- .dot = dot,
- .name = name,
- };
- return &node.base;
+
+ // anon enum literal
+ if (eatToken(it, .Identifier)) |name| {
+ const node = try arena.create(Node.EnumLiteral);
+ node.* = Node.EnumLiteral{
+ .base = Node{ .id = .EnumLiteral },
+ .dot = dot,
+ .name = name,
+ };
+ return &node.base;
+ }
+
+ // anon container literal
+ if (try parseInitList(arena, it, tree)) |node| {
+ node.lhs = .{.dot = dot};
+ return &node.base;
+ }
+
+ putBackToken(it, dot);
+ return null;
}
/// AsmOutput <- COLON AsmOutputList AsmInput?
diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig
index e9b4cd6455..f61442adfc 100644
--- a/lib/std/zig/parser_test.zig
+++ b/lib/std/zig/parser_test.zig
@@ -1,3 +1,20 @@
+test "zig fmt: anon struct literal syntax" {
+ try testCanonical(
+ \\const x = .{
+ \\ .a = b,
+ \\ .c = d,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: anon list literal syntax" {
+ try testCanonical(
+ \\const x = .{ a, b, c };
+ \\
+ );
+}
+
test "zig fmt: async function" {
try testCanonical(
\\pub const Server = struct {
diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig
index c6912087cf..c824939296 100644
--- a/lib/std/zig/render.zig
+++ b/lib/std/zig/render.zig
@@ -538,9 +538,9 @@ fn renderExpression(
try renderToken(tree, stream, async_token, indent, start_col, Space.Space);
}
- try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
+ try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None);
- const lparen = tree.nextToken(suffix_op.lhs.lastToken());
+ const lparen = tree.nextToken(suffix_op.lhs.node.lastToken());
if (call_info.params.len == 0) {
try renderToken(tree, stream, lparen, indent, start_col, Space.None);
@@ -598,7 +598,7 @@ fn renderExpression(
const lbracket = tree.prevToken(index_expr.firstToken());
const rbracket = tree.nextToken(index_expr.lastToken());
- try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
+ try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None);
try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [
const starts_with_comment = tree.tokens.at(lbracket + 1).id == .LineComment;
@@ -616,18 +616,18 @@ fn renderExpression(
},
ast.Node.SuffixOp.Op.Deref => {
- try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
+ try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None);
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // .*
},
ast.Node.SuffixOp.Op.UnwrapOptional => {
- try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
+ try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None);
try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // .
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ?
},
@TagType(ast.Node.SuffixOp.Op).Slice => |range| {
- try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
+ try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs.node, Space.None);
const lbracket = tree.prevToken(range.start.firstToken());
const dotdot = tree.nextToken(range.start.lastToken());
@@ -647,10 +647,16 @@ fn renderExpression(
},
ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| {
- const lbrace = tree.nextToken(suffix_op.lhs.lastToken());
+ const lbrace = switch (suffix_op.lhs) {
+ .dot => |dot| tree.nextToken(dot),
+ .node => |node| tree.nextToken(node.lastToken()),
+ };
if (field_inits.len == 0) {
- try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
+ switch (suffix_op.lhs) {
+ .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
+ .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
+ }
try renderToken(tree, stream, lbrace, indent + indent_delta, start_col, Space.None);
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
}
@@ -691,7 +697,10 @@ fn renderExpression(
break :blk;
}
- try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
+ switch (suffix_op.lhs) {
+ .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
+ .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
+ }
try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
try renderExpression(allocator, stream, tree, indent, start_col, &field_init.base, Space.Space);
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
@@ -699,7 +708,10 @@ fn renderExpression(
if (!src_has_trailing_comma and src_same_line and expr_outputs_one_line) {
// render all on one line, no trailing comma
- try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
+ switch (suffix_op.lhs) {
+ .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
+ .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
+ }
try renderToken(tree, stream, lbrace, indent, start_col, Space.Space);
var it = field_inits.iterator(0);
@@ -719,7 +731,10 @@ fn renderExpression(
const new_indent = indent + indent_delta;
- try renderExpression(allocator, stream, tree, new_indent, start_col, suffix_op.lhs, Space.None);
+ switch (suffix_op.lhs) {
+ .dot => |dot| try renderToken(tree, stream, dot, new_indent, start_col, Space.None),
+ .node => |node| try renderExpression(allocator, stream, tree, new_indent, start_col, node, Space.None),
+ }
try renderToken(tree, stream, lbrace, new_indent, start_col, Space.Newline);
var it = field_inits.iterator(0);
@@ -743,23 +758,35 @@ fn renderExpression(
},
ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| {
- const lbrace = tree.nextToken(suffix_op.lhs.lastToken());
+ const lbrace = switch (suffix_op.lhs) {
+ .dot => |dot| tree.nextToken(dot),
+ .node => |node| tree.nextToken(node.lastToken()),
+ };
if (exprs.len == 0) {
- try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
+ switch (suffix_op.lhs) {
+ .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
+ .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
+ }
try renderToken(tree, stream, lbrace, indent, start_col, Space.None);
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
}
if (exprs.len == 1 and tree.tokens.at(exprs.at(0).*.lastToken() + 1).id == .RBrace) {
const expr = exprs.at(0).*;
- try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
+ switch (suffix_op.lhs) {
+ .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
+ .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
+ }
try renderToken(tree, stream, lbrace, indent, start_col, Space.None);
try renderExpression(allocator, stream, tree, indent, start_col, expr, Space.None);
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space);
}
- try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
+ switch (suffix_op.lhs) {
+ .dot => |dot| try renderToken(tree, stream, dot, indent, start_col, Space.None),
+ .node => |node| try renderExpression(allocator, stream, tree, indent, start_col, node, Space.None),
+ }
// scan to find row size
const maybe_row_size: ?usize = blk: {
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 8ba2bfc503..e38e857209 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1187,10 +1187,22 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b);
static const uint32_t VECTOR_INDEX_NONE = UINT32_MAX;
static const uint32_t VECTOR_INDEX_RUNTIME = UINT32_MAX - 1;
+struct InferredStructField {
+ ZigType *inferred_struct_type;
+ Buf *field_name;
+};
+
struct ZigTypePointer {
ZigType *child_type;
ZigType *slice_parent;
+ // Anonymous struct literal syntax uses this when the result location has
+ // no type in it. This field is null if this pointer does not refer to
+ // a field of a currently-being-inferred struct type.
+ // When this is non-null, the pointer is pointing to the base of the inferred
+ // struct.
+ InferredStructField *inferred_struct_field;
+
PtrLen ptr_len;
uint32_t explicit_alignment; // 0 means use ABI alignment
@@ -1237,6 +1249,7 @@ struct TypeStructField {
enum ResolveStatus {
ResolveStatusUnstarted,
ResolveStatusInvalid,
+ ResolveStatusBeingInferred,
ResolveStatusZeroBitsKnown,
ResolveStatusAlignmentKnown,
ResolveStatusSizeKnown,
@@ -1285,6 +1298,7 @@ struct ZigTypeStruct {
bool requires_comptime;
bool resolve_loop_flag_zero_bits;
bool resolve_loop_flag_other;
+ bool is_inferred;
};
struct ZigTypeOptional {
@@ -1741,6 +1755,7 @@ struct TypeId {
union {
struct {
ZigType *child_type;
+ InferredStructField *inferred_struct_field;
PtrLen ptr_len;
uint32_t alignment;
@@ -2812,7 +2827,7 @@ struct IrInstructionElemPtr {
IrInstruction *array_ptr;
IrInstruction *elem_index;
- IrInstruction *init_array_type;
+ AstNode *init_array_type_source_node;
PtrLen ptr_len;
bool safety_check_on;
};
@@ -2909,11 +2924,11 @@ struct IrInstructionResizeSlice {
struct IrInstructionContainerInitList {
IrInstruction base;
- IrInstruction *container_type;
IrInstruction *elem_type;
size_t item_count;
IrInstruction **elem_result_loc_list;
IrInstruction *result_loc;
+ AstNode *init_array_type_source_node;
};
struct IrInstructionContainerInitFieldsField {
@@ -2926,7 +2941,6 @@ struct IrInstructionContainerInitFieldsField {
struct IrInstructionContainerInitFields {
IrInstruction base;
- IrInstruction *container_type;
size_t field_count;
IrInstructionContainerInitFieldsField *fields;
IrInstruction *result_loc;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 66e1f8984b..316fa52ac5 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -140,7 +140,6 @@ void init_scope(CodeGen *g, Scope *dest, ScopeId id, AstNode *source_node, Scope
static ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type,
ZigType *import, Buf *bare_name)
{
- assert(node == nullptr || node->type == NodeTypeContainerDecl || node->type == NodeTypeFnCallExpr);
ScopeDecls *scope = allocate(1);
init_scope(g, &scope->base, ScopeIdDecls, node, parent);
scope->decl_table.init(4);
@@ -346,6 +345,8 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) {
switch (status) {
case ResolveStatusInvalid:
zig_unreachable();
+ case ResolveStatusBeingInferred:
+ zig_unreachable();
case ResolveStatusUnstarted:
case ResolveStatusZeroBitsKnown:
return true;
@@ -362,6 +363,8 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) {
switch (status) {
case ResolveStatusInvalid:
zig_unreachable();
+ case ResolveStatusBeingInferred:
+ zig_unreachable();
case ResolveStatusUnstarted:
return true;
case ResolveStatusZeroBitsKnown:
@@ -483,7 +486,7 @@ ZigType *get_fn_frame_type(CodeGen *g, ZigFn *fn) {
ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_const,
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment,
uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero,
- uint32_t vector_index)
+ uint32_t vector_index, InferredStructField *inferred_struct_field)
{
assert(ptr_len != PtrLenC || allow_zero);
assert(!type_is_invalid(child_type));
@@ -506,7 +509,7 @@ ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_con
TypeId type_id = {};
ZigType **parent_pointer = nullptr;
if (host_int_bytes != 0 || is_volatile || byte_alignment != 0 || ptr_len != PtrLenSingle ||
- allow_zero || vector_index != VECTOR_INDEX_NONE)
+ allow_zero || vector_index != VECTOR_INDEX_NONE || inferred_struct_field != nullptr)
{
type_id.id = ZigTypeIdPointer;
type_id.data.pointer.child_type = child_type;
@@ -518,6 +521,7 @@ ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_con
type_id.data.pointer.ptr_len = ptr_len;
type_id.data.pointer.allow_zero = allow_zero;
type_id.data.pointer.vector_index = vector_index;
+ type_id.data.pointer.inferred_struct_field = inferred_struct_field;
auto existing_entry = g->type_table.maybe_get(type_id);
if (existing_entry)
@@ -545,8 +549,15 @@ ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_con
}
buf_resize(&entry->name, 0);
if (host_int_bytes == 0 && byte_alignment == 0 && vector_index == VECTOR_INDEX_NONE) {
- buf_appendf(&entry->name, "%s%s%s%s%s",
- star_str, const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
+ if (inferred_struct_field == nullptr) {
+ buf_appendf(&entry->name, "%s%s%s%s%s",
+ star_str, const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
+ } else {
+ buf_appendf(&entry->name, "(%s%s%s%s field '%s' of %s)",
+ star_str, const_str, volatile_str, allow_zero_str,
+ buf_ptr(inferred_struct_field->field_name),
+ buf_ptr(&inferred_struct_field->inferred_struct_type->name));
+ }
} else if (host_int_bytes == 0 && vector_index == VECTOR_INDEX_NONE) {
buf_appendf(&entry->name, "%salign(%" PRIu32 ") %s%s%s%s", star_str, byte_alignment,
const_str, volatile_str, allow_zero_str, buf_ptr(&child_type->name));
@@ -603,6 +614,7 @@ ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type, bool is_con
entry->data.pointer.host_int_bytes = host_int_bytes;
entry->data.pointer.allow_zero = allow_zero;
entry->data.pointer.vector_index = vector_index;
+ entry->data.pointer.inferred_struct_field = inferred_struct_field;
if (parent_pointer) {
*parent_pointer = entry;
@@ -617,12 +629,12 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_cons
uint32_t bit_offset_in_host, uint32_t host_int_bytes, bool allow_zero)
{
return get_pointer_to_type_extra2(g, child_type, is_const, is_volatile, ptr_len,
- byte_alignment, bit_offset_in_host, host_int_bytes, allow_zero, VECTOR_INDEX_NONE);
+ byte_alignment, bit_offset_in_host, host_int_bytes, allow_zero, VECTOR_INDEX_NONE, nullptr);
}
ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const) {
return get_pointer_to_type_extra2(g, child_type, is_const, false, PtrLenSingle, 0, 0, 0, false,
- VECTOR_INDEX_NONE);
+ VECTOR_INDEX_NONE, nullptr);
}
ZigType *get_optional_type(CodeGen *g, ZigType *child_type) {
@@ -2079,7 +2091,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) {
}
assert(struct_type->data.structure.fields || struct_type->data.structure.src_field_count == 0);
- assert(decl_node->type == NodeTypeContainerDecl);
+ assert(decl_node->type == NodeTypeContainerDecl || decl_node->type == NodeTypeContainerInitExpr);
size_t field_count = struct_type->data.structure.src_field_count;
@@ -2667,7 +2679,6 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
return ErrorNone;
AstNode *decl_node = struct_type->data.structure.decl_node;
- assert(decl_node->type == NodeTypeContainerDecl);
if (struct_type->data.structure.resolve_loop_flag_zero_bits) {
if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) {
@@ -2678,29 +2689,46 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
}
return ErrorSemanticAnalyzeFail;
}
-
struct_type->data.structure.resolve_loop_flag_zero_bits = true;
- assert(!struct_type->data.structure.fields);
- size_t field_count = decl_node->data.container_decl.fields.length;
- struct_type->data.structure.src_field_count = (uint32_t)field_count;
- struct_type->data.structure.fields = allocate(field_count);
+ size_t field_count;
+ if (decl_node->type == NodeTypeContainerDecl) {
+ field_count = decl_node->data.container_decl.fields.length;
+ struct_type->data.structure.src_field_count = (uint32_t)field_count;
+
+ src_assert(struct_type->data.structure.fields == nullptr, decl_node);
+ struct_type->data.structure.fields = allocate(field_count);
+ } else if (decl_node->type == NodeTypeContainerInitExpr) {
+ src_assert(struct_type->data.structure.is_inferred, decl_node);
+ src_assert(struct_type->data.structure.fields != nullptr, decl_node);
+
+ field_count = struct_type->data.structure.src_field_count;
+ } else zig_unreachable();
+
struct_type->data.structure.fields_by_name.init(field_count);
Scope *scope = &struct_type->data.structure.decls_scope->base;
size_t gen_field_index = 0;
for (size_t i = 0; i < field_count; i += 1) {
- AstNode *field_node = decl_node->data.container_decl.fields.at(i);
TypeStructField *type_struct_field = &struct_type->data.structure.fields[i];
- type_struct_field->name = field_node->data.struct_field.name;
- type_struct_field->decl_node = field_node;
- if (field_node->data.struct_field.type == nullptr) {
- add_node_error(g, field_node, buf_sprintf("struct field missing type"));
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
+ AstNode *field_node;
+ if (decl_node->type == NodeTypeContainerDecl) {
+ field_node = decl_node->data.container_decl.fields.at(i);
+ type_struct_field->name = field_node->data.struct_field.name;
+ type_struct_field->decl_node = field_node;
+
+ if (field_node->data.struct_field.type == nullptr) {
+ add_node_error(g, field_node, buf_sprintf("struct field missing type"));
+ struct_type->data.structure.resolve_status = ResolveStatusInvalid;
+ return ErrorSemanticAnalyzeFail;
+ }
+ } else if (decl_node->type == NodeTypeContainerInitExpr) {
+ field_node = type_struct_field->decl_node;
+
+ src_assert(type_struct_field->type_entry != nullptr, field_node);
+ } else zig_unreachable();
auto field_entry = struct_type->data.structure.fields_by_name.put_unique(type_struct_field->name, type_struct_field);
if (field_entry != nullptr) {
@@ -2711,16 +2739,21 @@ static Error resolve_struct_zero_bits(CodeGen *g, ZigType *struct_type) {
return ErrorSemanticAnalyzeFail;
}
- ConstExprValue *field_type_val = analyze_const_value(g, scope,
- field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef);
- if (type_is_invalid(field_type_val->type)) {
- struct_type->data.structure.resolve_status = ResolveStatusInvalid;
- return ErrorSemanticAnalyzeFail;
- }
- assert(field_type_val->special != ConstValSpecialRuntime);
- type_struct_field->type_val = field_type_val;
- if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
- return ErrorSemanticAnalyzeFail;
+ ConstExprValue *field_type_val;
+ if (decl_node->type == NodeTypeContainerDecl) {
+ field_type_val = analyze_const_value(g, scope,
+ field_node->data.struct_field.type, g->builtin_types.entry_type, nullptr, LazyOkNoUndef);
+ if (type_is_invalid(field_type_val->type)) {
+ struct_type->data.structure.resolve_status = ResolveStatusInvalid;
+ return ErrorSemanticAnalyzeFail;
+ }
+ assert(field_type_val->special != ConstValSpecialRuntime);
+ type_struct_field->type_val = field_type_val;
+ if (struct_type->data.structure.resolve_status == ResolveStatusInvalid)
+ return ErrorSemanticAnalyzeFail;
+ } else if (decl_node->type == NodeTypeContainerInitExpr) {
+ field_type_val = type_struct_field->type_val;
+ } else zig_unreachable();
bool field_is_opaque_type;
if ((err = type_val_resolve_is_opaque_type(g, field_type_val, &field_is_opaque_type))) {
@@ -2804,7 +2837,7 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) {
}
struct_type->data.structure.resolve_loop_flag_other = true;
- assert(decl_node->type == NodeTypeContainerDecl);
+ assert(decl_node->type == NodeTypeContainerDecl || decl_node->type == NodeTypeContainerInitExpr);
size_t field_count = struct_type->data.structure.src_field_count;
bool packed = struct_type->data.structure.layout == ContainerLayoutPacked;
@@ -2814,7 +2847,8 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) {
if (field->gen_index == SIZE_MAX)
continue;
- AstNode *align_expr = field->decl_node->data.struct_field.align_expr;
+ AstNode *align_expr = (field->decl_node->type == NodeTypeStructField) ?
+ field->decl_node->data.struct_field.align_expr : nullptr;
if (align_expr != nullptr) {
if (!analyze_const_align(g, &struct_type->data.structure.decls_scope->base, align_expr,
&field->align))
@@ -5413,6 +5447,12 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) {
if (type_entry->one_possible_value != OnePossibleValueInvalid)
return type_entry->one_possible_value;
+ if (type_entry->id == ZigTypeIdStruct &&
+ type_entry->data.structure.resolve_status == ResolveStatusBeingInferred)
+ {
+ return OnePossibleValueNo;
+ }
+
Error err;
if ((err = type_resolve(g, type_entry, ResolveStatusZeroBitsKnown)))
return OnePossibleValueInvalid;
@@ -6132,6 +6172,8 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
continue;
if (instruction->ref_count == 0)
continue;
+ if ((err = type_resolve(g, instruction->value.type, ResolveStatusZeroBitsKnown)))
+ return ErrorSemanticAnalyzeFail;
if (!type_has_bits(instruction->value.type))
continue;
if (scope_needs_spill(instruction->scope)) {
@@ -6271,6 +6313,8 @@ Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) {
switch (status) {
case ResolveStatusUnstarted:
return ErrorNone;
+ case ResolveStatusBeingInferred:
+ zig_unreachable();
case ResolveStatusInvalid:
zig_unreachable();
case ResolveStatusZeroBitsKnown:
@@ -6995,7 +7039,16 @@ bool type_id_eql(TypeId a, TypeId b) {
a.data.pointer.alignment == b.data.pointer.alignment &&
a.data.pointer.bit_offset_in_host == b.data.pointer.bit_offset_in_host &&
a.data.pointer.vector_index == b.data.pointer.vector_index &&
- a.data.pointer.host_int_bytes == b.data.pointer.host_int_bytes;
+ a.data.pointer.host_int_bytes == b.data.pointer.host_int_bytes &&
+ (
+ a.data.pointer.inferred_struct_field == b.data.pointer.inferred_struct_field ||
+ (a.data.pointer.inferred_struct_field != nullptr &&
+ b.data.pointer.inferred_struct_field != nullptr &&
+ a.data.pointer.inferred_struct_field->inferred_struct_type ==
+ b.data.pointer.inferred_struct_field->inferred_struct_type &&
+ buf_eql_buf(a.data.pointer.inferred_struct_field->field_name,
+ b.data.pointer.inferred_struct_field->field_name))
+ );
case ZigTypeIdArray:
return a.data.array.child_type == b.data.array.child_type &&
a.data.array.size == b.data.array.size;
@@ -7808,7 +7861,6 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
ZigLLVMDIScope *di_scope;
unsigned line;
if (decl_node != nullptr) {
- assert(decl_node->type == NodeTypeContainerDecl);
Scope *scope = &struct_type->data.structure.decls_scope->base;
ZigType *import = get_scope_import(scope);
di_file = import->data.structure.root_struct->di_file;
@@ -8011,7 +8063,7 @@ static void resolve_llvm_types_struct(CodeGen *g, ZigType *struct_type, ResolveS
}
unsigned line;
if (decl_node != nullptr) {
- AstNode *field_node = decl_node->data.container_decl.fields.at(i);
+ AstNode *field_node = field->decl_node;
line = field_node->line + 1;
} else {
line = 0;
@@ -8307,12 +8359,12 @@ static void resolve_llvm_types_pointer(CodeGen *g, ZigType *type, ResolveStatus
if (type->data.pointer.vector_index == VECTOR_INDEX_NONE) {
peer_type = get_pointer_to_type_extra2(g, elem_type, false, false,
PtrLenSingle, 0, 0, type->data.pointer.host_int_bytes, false,
- VECTOR_INDEX_NONE);
+ VECTOR_INDEX_NONE, nullptr);
} else {
uint32_t host_vec_len = type->data.pointer.host_int_bytes;
ZigType *host_vec_type = get_vector_type(g, host_vec_len, elem_type);
peer_type = get_pointer_to_type_extra2(g, host_vec_type, false, false,
- PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE);
+ PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr);
}
type->llvm_type = get_llvm_type(g, peer_type);
type->llvm_di_type = get_llvm_di_type(g, peer_type);
@@ -9038,4 +9090,3 @@ Error analyze_import(CodeGen *g, ZigType *source_import, Buf *import_target_str,
*out_import = add_source_file(g, target_package, resolved_path, import_code, source_kind);
return ErrorNone;
}
-
diff --git a/src/analyze.hpp b/src/analyze.hpp
index a6af371e25..06c8847eda 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -24,7 +24,7 @@ ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type,
ZigType *get_pointer_to_type_extra2(CodeGen *g, ZigType *child_type,
bool is_const, bool is_volatile, PtrLen ptr_len,
uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count,
- bool allow_zero, uint32_t vector_index);
+ bool allow_zero, uint32_t vector_index, InferredStructField *inferred_struct_field);
uint64_t type_size(CodeGen *g, ZigType *type_entry);
uint64_t type_size_bits(CodeGen *g, ZigType *type_entry);
ZigType *get_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits);
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
index c016f629d1..34cbed245a 100644
--- a/src/ast_render.cpp
+++ b/src/ast_render.cpp
@@ -821,7 +821,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
break;
}
case NodeTypeContainerInitExpr:
- render_node_ungrouped(ar, node->data.container_init_expr.type);
+ if (node->data.container_init_expr.type != nullptr) {
+ render_node_ungrouped(ar, node->data.container_init_expr.type);
+ }
if (node->data.container_init_expr.kind == ContainerInitKindStruct) {
fprintf(ar->f, "{\n");
ar->indent += ar->indent_size;
diff --git a/src/ir.cpp b/src/ir.cpp
index 507b3e4459..9d15e341da 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -202,6 +202,10 @@ static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char
Scope *scope, AstNode *source_node, Buf *out_bare_name);
static ResultLocCast *ir_build_cast_result_loc(IrBuilder *irb, IrInstruction *dest_type,
ResultLoc *parent_result_loc);
+static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction *source_instr,
+ TypeStructField *field, IrInstruction *struct_ptr, ZigType *struct_type, bool initializing);
+static IrInstruction *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name,
+ IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type);
static ConstExprValue *const_ptr_pointee_unchecked(CodeGen *g, ConstExprValue *const_val) {
assert(get_src_ptr_type(const_val->type) != nullptr);
@@ -1350,18 +1354,17 @@ static IrInstruction *ir_build_return_ptr(IrAnalyze *ira, IrInstruction *source_
static IrInstruction *ir_build_elem_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *array_ptr, IrInstruction *elem_index, bool safety_check_on, PtrLen ptr_len,
- IrInstruction *init_array_type)
+ AstNode *init_array_type_source_node)
{
IrInstructionElemPtr *instruction = ir_build_instruction(irb, scope, source_node);
instruction->array_ptr = array_ptr;
instruction->elem_index = elem_index;
instruction->safety_check_on = safety_check_on;
instruction->ptr_len = ptr_len;
- instruction->init_array_type = init_array_type;
+ instruction->init_array_type_source_node = init_array_type_source_node;
ir_ref_instruction(array_ptr, irb->current_basic_block);
ir_ref_instruction(elem_index, irb->current_basic_block);
- if (init_array_type != nullptr) ir_ref_instruction(init_array_type, irb->current_basic_block);
return &instruction->base;
}
@@ -1575,17 +1578,16 @@ static IrInstruction *ir_build_un_op(IrBuilder *irb, Scope *scope, AstNode *sour
}
static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope, AstNode *source_node,
- IrInstruction *container_type, size_t item_count, IrInstruction **elem_result_loc_list,
- IrInstruction *result_loc)
+ size_t item_count, IrInstruction **elem_result_loc_list, IrInstruction *result_loc,
+ AstNode *init_array_type_source_node)
{
IrInstructionContainerInitList *container_init_list_instruction =
ir_build_instruction(irb, scope, source_node);
- container_init_list_instruction->container_type = container_type;
container_init_list_instruction->item_count = item_count;
container_init_list_instruction->elem_result_loc_list = elem_result_loc_list;
container_init_list_instruction->result_loc = result_loc;
+ container_init_list_instruction->init_array_type_source_node = init_array_type_source_node;
- ir_ref_instruction(container_type, irb->current_basic_block);
for (size_t i = 0; i < item_count; i += 1) {
ir_ref_instruction(elem_result_loc_list[i], irb->current_basic_block);
}
@@ -1595,17 +1597,14 @@ static IrInstruction *ir_build_container_init_list(IrBuilder *irb, Scope *scope,
}
static IrInstruction *ir_build_container_init_fields(IrBuilder *irb, Scope *scope, AstNode *source_node,
- IrInstruction *container_type, size_t field_count, IrInstructionContainerInitFieldsField *fields,
- IrInstruction *result_loc)
+ size_t field_count, IrInstructionContainerInitFieldsField *fields, IrInstruction *result_loc)
{
IrInstructionContainerInitFields *container_init_fields_instruction =
ir_build_instruction(irb, scope, source_node);
- container_init_fields_instruction->container_type = container_type;
container_init_fields_instruction->field_count = field_count;
container_init_fields_instruction->fields = fields;
container_init_fields_instruction->result_loc = result_loc;
- ir_ref_instruction(container_type, irb->current_basic_block);
for (size_t i = 0; i < field_count; i += 1) {
ir_ref_instruction(fields[i].result_loc, irb->current_basic_block);
}
@@ -3084,7 +3083,7 @@ static IrInstruction *ir_build_resolve_result(IrBuilder *irb, Scope *scope, AstN
instruction->result_loc = result_loc;
instruction->ty = ty;
- ir_ref_instruction(ty, irb->current_basic_block);
+ if (ty != nullptr) ir_ref_instruction(ty, irb->current_basic_block);
return &instruction->base;
}
@@ -6127,28 +6126,46 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A
AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr;
ContainerInitKind kind = container_init_expr->kind;
- IrInstruction *container_type = nullptr;
- IrInstruction *elem_type = nullptr;
- if (container_init_expr->type->type == NodeTypeInferredArrayType) {
- elem_type = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.child_type, scope);
- if (elem_type == irb->codegen->invalid_instruction)
- return elem_type;
- } else {
- container_type = ir_gen_node(irb, container_init_expr->type, scope);
- if (container_type == irb->codegen->invalid_instruction)
- return container_type;
- }
-
- switch (kind) {
- case ContainerInitKindStruct: {
- if (elem_type != nullptr) {
+ ResultLocCast *result_loc_cast = nullptr;
+ ResultLoc *child_result_loc;
+ AstNode *init_array_type_source_node;
+ if (container_init_expr->type != nullptr) {
+ IrInstruction *container_type;
+ if (container_init_expr->type->type == NodeTypeInferredArrayType) {
+ if (kind == ContainerInitKindStruct) {
add_node_error(irb->codegen, container_init_expr->type,
buf_sprintf("initializing array with struct syntax"));
return irb->codegen->invalid_instruction;
}
+ IrInstruction *elem_type = ir_gen_node(irb,
+ container_init_expr->type->data.inferred_array_type.child_type, scope);
+ if (elem_type == irb->codegen->invalid_instruction)
+ return elem_type;
+ size_t item_count = container_init_expr->entries.length;
+ IrInstruction *item_count_inst = ir_build_const_usize(irb, scope, node, item_count);
+ container_type = ir_build_array_type(irb, scope, node, item_count_inst, elem_type);
+ } else {
+ container_type = ir_gen_node(irb, container_init_expr->type, scope);
+ if (container_type == irb->codegen->invalid_instruction)
+ return container_type;
+ }
- IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, parent_result_loc,
- container_type);
+ result_loc_cast = ir_build_cast_result_loc(irb, container_type, parent_result_loc);
+ child_result_loc = &result_loc_cast->base;
+ init_array_type_source_node = container_type->source_node;
+ } else {
+ child_result_loc = parent_result_loc;
+ if (parent_result_loc->source_instruction != nullptr) {
+ init_array_type_source_node = parent_result_loc->source_instruction->source_node;
+ } else {
+ init_array_type_source_node = node;
+ }
+ }
+
+ switch (kind) {
+ case ContainerInitKindStruct: {
+ IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc,
+ nullptr);
size_t field_count = container_init_expr->entries.length;
IrInstructionContainerInitFieldsField *fields = allocate(field_count);
@@ -6176,29 +6193,27 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A
fields[i].source_node = entry_node;
fields[i].result_loc = field_ptr;
}
- IrInstruction *init_fields = ir_build_container_init_fields(irb, scope, node, container_type,
- field_count, fields, container_ptr);
+ IrInstruction *result = ir_build_container_init_fields(irb, scope, node, field_count,
+ fields, container_ptr);
- return ir_lval_wrap(irb, scope, init_fields, lval, parent_result_loc);
+ if (result_loc_cast != nullptr) {
+ result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast);
+ }
+ return ir_lval_wrap(irb, scope, result, lval, parent_result_loc);
}
case ContainerInitKindArray: {
size_t item_count = container_init_expr->entries.length;
- if (container_type == nullptr) {
- IrInstruction *item_count_inst = ir_build_const_usize(irb, scope, node, item_count);
- container_type = ir_build_array_type(irb, scope, node, item_count_inst, elem_type);
- }
-
- IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, parent_result_loc,
- container_type);
+ IrInstruction *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc,
+ nullptr);
IrInstruction **result_locs = allocate(item_count);
for (size_t i = 0; i < item_count; i += 1) {
AstNode *expr_node = container_init_expr->entries.at(i);
IrInstruction *elem_index = ir_build_const_usize(irb, scope, expr_node, i);
- IrInstruction *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr, elem_index,
- false, PtrLenSingle, container_type);
+ IrInstruction *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr,
+ elem_index, false, PtrLenSingle, init_array_type_source_node);
ResultLocInstruction *result_loc_inst = allocate(1);
result_loc_inst->base.id = ResultLocIdInstruction;
result_loc_inst->base.source_instruction = elem_ptr;
@@ -6213,9 +6228,12 @@ static IrInstruction *ir_gen_container_init_expr(IrBuilder *irb, Scope *scope, A
result_locs[i] = elem_ptr;
}
- IrInstruction *init_list = ir_build_container_init_list(irb, scope, node, container_type,
- item_count, result_locs, container_ptr);
- return ir_lval_wrap(irb, scope, init_list, lval, parent_result_loc);
+ IrInstruction *result = ir_build_container_init_list(irb, scope, node, item_count,
+ result_locs, container_ptr, init_array_type_source_node);
+ if (result_loc_cast != nullptr) {
+ result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast);
+ }
+ return ir_lval_wrap(irb, scope, result, lval, parent_result_loc);
}
}
zig_unreachable();
@@ -7935,14 +7953,14 @@ static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *o
static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char *kind_name,
Scope *scope, AstNode *source_node, Buf *out_bare_name)
{
- if (exec->name) {
+ if (exec != nullptr && exec->name) {
ZigType *import = get_scope_import(scope);
Buf *namespace_name = buf_alloc();
append_namespace_qualification(codegen, namespace_name, import);
buf_append_buf(namespace_name, exec->name);
buf_init_from_buf(out_bare_name, exec->name);
return namespace_name;
- } else if (exec->name_fn != nullptr) {
+ } else if (exec != nullptr && exec->name_fn != nullptr) {
Buf *name = buf_alloc();
buf_append_buf(name, &exec->name_fn->symbol_name);
buf_appendf(name, "(");
@@ -15541,11 +15559,7 @@ static bool ir_result_has_type(ResultLoc *result_loc) {
static IrInstruction *ir_resolve_no_result_loc(IrAnalyze *ira, IrInstruction *suspend_source_instr,
ResultLoc *result_loc, ZigType *value_type, bool force_runtime, bool non_null_comptime)
{
- Error err;
-
IrInstructionAllocaGen *alloca_gen = ir_build_alloca_gen(ira, suspend_source_instr, 0, "");
- if ((err = type_resolve(ira->codegen, value_type, ResolveStatusZeroBitsKnown)))
- return ira->codegen->invalid_instruction;
alloca_gen->base.value.type = get_pointer_to_type_extra(ira->codegen, value_type, false, false,
PtrLenSingle, 0, 0, 0, false);
set_up_result_loc_for_inferred_comptime(&alloca_gen->base);
@@ -15750,6 +15764,7 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe
return casted_value;
}
+ bool old_parent_result_loc_written = result_cast->parent->written;
IrInstruction *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, result_cast->parent,
dest_type, casted_value, force_runtime, non_null_comptime, true);
if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value.type) ||
@@ -15775,6 +15790,22 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe
parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle,
parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero);
+ {
+ // we also need to check that this cast is OK.
+ ConstCastOnly const_cast_result = types_match_const_cast_only(ira,
+ parent_result_loc->value.type, ptr_type,
+ result_cast->base.source_instruction->source_node, false);
+ if (const_cast_result.id == ConstCastResultIdInvalid)
+ return ira->codegen->invalid_instruction;
+ if (const_cast_result.id != ConstCastResultIdOk) {
+ // We will not be able to provide a result location for this value. Create
+ // a new result location.
+ result_cast->parent->written = old_parent_result_loc_written;
+ return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type,
+ force_runtime, non_null_comptime);
+ }
+ }
+
result_loc->written = true;
result_loc->resolved_loc = ir_analyze_ptr_cast(ira, suspend_source_instr, parent_result_loc,
ptr_type, result_cast->base.source_instruction, false);
@@ -15902,10 +15933,37 @@ static IrInstruction *ir_resolve_result(IrAnalyze *ira, IrInstruction *suspend_s
return result_loc;
}
-static IrInstruction *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrInstructionResolveResult *instruction) {
- ZigType *implicit_elem_type = ir_resolve_type(ira, instruction->ty->child);
- if (type_is_invalid(implicit_elem_type))
- return ira->codegen->invalid_instruction;
+static IrInstruction *ir_analyze_instruction_resolve_result(IrAnalyze *ira,
+ IrInstructionResolveResult *instruction)
+{
+ ZigType *implicit_elem_type;
+ if (instruction->ty == nullptr) {
+ if (instruction->result_loc->id == ResultLocIdCast) {
+ implicit_elem_type = ir_resolve_type(ira,
+ instruction->result_loc->source_instruction->child);
+ if (type_is_invalid(implicit_elem_type))
+ return ira->codegen->invalid_instruction;
+ } else if (instruction->result_loc->id == ResultLocIdReturn) {
+ implicit_elem_type = ira->explicit_return_type;
+ if (type_is_invalid(implicit_elem_type))
+ return ira->codegen->invalid_instruction;
+ } else {
+ Buf *bare_name = buf_alloc();
+ Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct),
+ instruction->base.scope, instruction->base.source_node, bare_name);
+
+ ZigType *inferred_struct_type = get_partial_container_type(ira->codegen,
+ instruction->base.scope, ContainerKindStruct, instruction->base.source_node,
+ buf_ptr(name), bare_name, ContainerLayoutAuto);
+ inferred_struct_type->data.structure.is_inferred = true;
+ inferred_struct_type->data.structure.resolve_status = ResolveStatusBeingInferred;
+ implicit_elem_type = inferred_struct_type;
+ }
+ } else {
+ implicit_elem_type = ir_resolve_type(ira, instruction->ty->child);
+ if (type_is_invalid(implicit_elem_type))
+ return ira->codegen->invalid_instruction;
+ }
IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
implicit_elem_type, nullptr, false, true, true);
if (result_loc != nullptr)
@@ -16267,13 +16325,78 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source
return ir_const_void(ira, source_instr);
}
- ZigType *child_type = ptr->value.type->data.pointer.child_type;
+ InferredStructField *isf = ptr->value.type->data.pointer.inferred_struct_field;
+ if (allow_write_through_const && isf != nullptr) {
+ // Now it's time to add the field to the struct type.
+ uint32_t old_field_count = isf->inferred_struct_type->data.structure.src_field_count;
+ uint32_t new_field_count = old_field_count + 1;
+ isf->inferred_struct_type->data.structure.src_field_count = new_field_count;
+ if (new_field_count > 16) {
+ // This thing with 16 is a hack to allow this functionality to work without
+ // modifying the ConstExprValue layout of structs. That reworking needs to be
+ // done, but this hack lets us do it separately, in the future.
+ zig_panic("TODO need to rework the layout of ZigTypeStruct. This realloc would have caused invalid pointer references");
+ }
+ if (isf->inferred_struct_type->data.structure.fields == nullptr) {
+ isf->inferred_struct_type->data.structure.fields = allocate(16);
+ }
+
+ // This reference can't live long, don't keep it around outside this block.
+ TypeStructField *field = &isf->inferred_struct_type->data.structure.fields[old_field_count];
+ field->name = isf->field_name;
+ field->type_entry = uncasted_value->value.type;
+ field->type_val = create_const_type(ira->codegen, field->type_entry);
+ field->src_index = old_field_count;
+ field->decl_node = uncasted_value->source_node;
+
+ ZigType *struct_ptr_type = get_pointer_to_type(ira->codegen, isf->inferred_struct_type, false);
+ IrInstruction *casted_ptr;
+ if (instr_is_comptime(ptr)) {
+ casted_ptr = ir_const(ira, source_instr, struct_ptr_type);
+ copy_const_val(&casted_ptr->value, &ptr->value, false);
+ casted_ptr->value.type = struct_ptr_type;
+ } else {
+ casted_ptr = ir_build_cast(&ira->new_irb, source_instr->scope,
+ source_instr->source_node, struct_ptr_type, ptr, CastOpNoop);
+ casted_ptr->value.type = struct_ptr_type;
+ }
+ if (instr_is_comptime(casted_ptr)) {
+ ConstExprValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad);
+ if (!ptr_val)
+ return ira->codegen->invalid_instruction;
+ if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
+ ConstExprValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val,
+ source_instr->source_node);
+ struct_val->special = ConstValSpecialStatic;
+ if (new_field_count > 16) {
+ // This thing with 16 is a hack to allow this functionality to work without
+ // modifying the ConstExprValue layout of structs. That reworking needs to be
+ // done, but this hack lets us do it separately, in the future.
+ zig_panic("TODO need to rework the layout of ConstExprValue for structs. This realloc would have caused invalid pointer references");
+ }
+ if (struct_val->data.x_struct.fields == nullptr) {
+ struct_val->data.x_struct.fields = create_const_vals(16);
+ }
+
+ ConstExprValue *field_val = &struct_val->data.x_struct.fields[old_field_count];
+ field_val->special = ConstValSpecialUndef;
+ field_val->type = field->type_entry;
+ field_val->parent.id = ConstParentIdStruct;
+ field_val->parent.data.p_struct.struct_val = struct_val;
+ field_val->parent.data.p_struct.field_index = old_field_count;
+ }
+ }
+
+ ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, casted_ptr,
+ isf->inferred_struct_type, true);
+ }
if (ptr->value.type->data.pointer.is_const && !allow_write_through_const) {
ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant"));
return ira->codegen->invalid_instruction;
}
+ ZigType *child_type = ptr->value.type->data.pointer.child_type;
IrInstruction *value = ir_implicit_cast(ira, uncasted_value, child_type);
if (value == ira->codegen->invalid_instruction)
return ira->codegen->invalid_instruction;
@@ -17769,6 +17892,19 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
} else if (array_type->id == ZigTypeIdVector) {
// This depends on whether the element index is comptime, so it is computed later.
return_type = nullptr;
+ } else if (elem_ptr_instruction->init_array_type_source_node != nullptr &&
+ array_type->id == ZigTypeIdStruct &&
+ array_type->data.structure.resolve_status == ResolveStatusBeingInferred)
+ {
+ ZigType *usize = ira->codegen->builtin_types.entry_usize;
+ IrInstruction *casted_elem_index = ir_implicit_cast(ira, elem_index, usize);
+ if (casted_elem_index == ira->codegen->invalid_instruction)
+ return ira->codegen->invalid_instruction;
+ ir_assert(instr_is_comptime(casted_elem_index), &elem_ptr_instruction->base);
+ Buf *field_name = buf_alloc();
+ bigint_append_buf(field_name, &casted_elem_index->value.data.x_bigint, 10);
+ return ir_analyze_inferred_field_ptr(ira, field_name, &elem_ptr_instruction->base,
+ array_ptr, array_type);
} else {
ir_add_error_node(ira, elem_ptr_instruction->base.source_node,
buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name)));
@@ -17799,7 +17935,8 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
return_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
elem_ptr_instruction->ptr_len,
- get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, (uint32_t)index);
+ get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, (uint32_t)index,
+ nullptr);
} else if (return_type->data.pointer.explicit_alignment != 0) {
// figure out the largest alignment possible
@@ -17837,7 +17974,9 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
if (array_ptr_val == nullptr)
return ira->codegen->invalid_instruction;
- if (array_ptr_val->special == ConstValSpecialUndef && elem_ptr_instruction->init_array_type != nullptr) {
+ if (array_ptr_val->special == ConstValSpecialUndef &&
+ elem_ptr_instruction->init_array_type_source_node != nullptr)
+ {
if (array_type->id == ZigTypeIdArray || array_type->id == ZigTypeIdVector) {
array_ptr_val->data.x_array.special = ConstArraySpecialNone;
array_ptr_val->data.x_array.data.s_none.elements = create_const_vals(array_type->data.array.len);
@@ -17851,11 +17990,13 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
elem_val->parent.data.p_array.elem_index = i;
}
} else if (is_slice(array_type)) {
- ZigType *actual_array_type = ir_resolve_type(ira, elem_ptr_instruction->init_array_type->child);
+ ir_assert(array_ptr->value.type->id == ZigTypeIdPointer, &elem_ptr_instruction->base);
+ ZigType *actual_array_type = array_ptr->value.type->data.pointer.child_type;
+
if (type_is_invalid(actual_array_type))
return ira->codegen->invalid_instruction;
if (actual_array_type->id != ZigTypeIdArray) {
- ir_add_error(ira, elem_ptr_instruction->init_array_type,
+ ir_add_error_node(ira, elem_ptr_instruction->init_array_type_source_node,
buf_sprintf("expected array type or [_], found slice"));
return ira->codegen->invalid_instruction;
}
@@ -17879,7 +18020,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
false);
array_ptr_val->data.x_struct.fields[slice_ptr_index].data.x_ptr.mut = ConstPtrMutInfer;
} else {
- ir_add_error(ira, elem_ptr_instruction->init_array_type,
+ ir_add_error_node(ira, elem_ptr_instruction->init_array_type_source_node,
buf_sprintf("expected array type or [_], found '%s'",
buf_ptr(&array_type->name)));
return ira->codegen->invalid_instruction;
@@ -18012,7 +18153,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
if (orig_array_ptr_val->data.x_ptr.mut == ConstPtrMutInfer) {
result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope,
elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index,
- false, elem_ptr_instruction->ptr_len, elem_ptr_instruction->init_array_type);
+ false, elem_ptr_instruction->ptr_len, nullptr);
result->value.type = return_type;
result->value.special = ConstValSpecialStatic;
} else {
@@ -18036,7 +18177,8 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
return_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
elem_ptr_instruction->ptr_len,
- get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, VECTOR_INDEX_RUNTIME);
+ get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, VECTOR_INDEX_RUNTIME,
+ nullptr);
} else {
// runtime known element index
switch (type_requires_comptime(ira->codegen, return_type)) {
@@ -18073,7 +18215,7 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct
IrInstruction *result = ir_build_elem_ptr(&ira->new_irb, elem_ptr_instruction->base.scope,
elem_ptr_instruction->base.source_node, array_ptr, casted_elem_index, safety_check_on,
- elem_ptr_instruction->ptr_len, elem_ptr_instruction->init_array_type);
+ elem_ptr_instruction->ptr_len, nullptr);
result->value.type = return_type;
return result;
}
@@ -18152,31 +18294,34 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction
case OnePossibleValueNo:
break;
}
- ResolveStatus needed_resolve_status =
- (struct_type->data.structure.layout == ContainerLayoutAuto) ?
- ResolveStatusZeroBitsKnown : ResolveStatusSizeKnown;
- if ((err = type_resolve(ira->codegen, struct_type, needed_resolve_status)))
- return ira->codegen->invalid_instruction;
- assert(struct_ptr->value.type->id == ZigTypeIdPointer);
- uint32_t ptr_bit_offset = struct_ptr->value.type->data.pointer.bit_offset_in_host;
- uint32_t ptr_host_int_bytes = struct_ptr->value.type->data.pointer.host_int_bytes;
- uint32_t host_int_bytes_for_result_type = (ptr_host_int_bytes == 0) ?
- get_host_int_bytes(ira->codegen, struct_type, field) : ptr_host_int_bytes;
bool is_const = struct_ptr->value.type->data.pointer.is_const;
bool is_volatile = struct_ptr->value.type->data.pointer.is_volatile;
- ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type,
- is_const, is_volatile, PtrLenSingle, field->align,
- (uint32_t)(ptr_bit_offset + field->bit_offset_in_host),
- (uint32_t)host_int_bytes_for_result_type, false);
+ ZigType *ptr_type;
+ if (struct_type->data.structure.is_inferred) {
+ ptr_type = get_pointer_to_type_extra(ira->codegen, field_type,
+ is_const, is_volatile, PtrLenSingle, 0, 0, 0, false);
+ } else {
+ ResolveStatus needed_resolve_status =
+ (struct_type->data.structure.layout == ContainerLayoutAuto) ?
+ ResolveStatusZeroBitsKnown : ResolveStatusSizeKnown;
+ if ((err = type_resolve(ira->codegen, struct_type, needed_resolve_status)))
+ return ira->codegen->invalid_instruction;
+ assert(struct_ptr->value.type->id == ZigTypeIdPointer);
+ uint32_t ptr_bit_offset = struct_ptr->value.type->data.pointer.bit_offset_in_host;
+ uint32_t ptr_host_int_bytes = struct_ptr->value.type->data.pointer.host_int_bytes;
+ uint32_t host_int_bytes_for_result_type = (ptr_host_int_bytes == 0) ?
+ get_host_int_bytes(ira->codegen, struct_type, field) : ptr_host_int_bytes;
+ ptr_type = get_pointer_to_type_extra(ira->codegen, field_type,
+ is_const, is_volatile, PtrLenSingle, field->align,
+ (uint32_t)(ptr_bit_offset + field->bit_offset_in_host),
+ (uint32_t)host_int_bytes_for_result_type, false);
+ }
if (instr_is_comptime(struct_ptr)) {
ConstExprValue *ptr_val = ir_resolve_const(ira, struct_ptr, UndefBad);
if (!ptr_val)
return ira->codegen->invalid_instruction;
if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
- if ((err = type_resolve(ira->codegen, struct_type, ResolveStatusSizeKnown)))
- return ira->codegen->invalid_instruction;
-
ConstExprValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node);
if (struct_val == nullptr)
return ira->codegen->invalid_instruction;
@@ -18188,7 +18333,8 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction
for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) {
ConstExprValue *field_val = &struct_val->data.x_struct.fields[i];
field_val->special = ConstValSpecialUndef;
- field_val->type = struct_type->data.structure.fields[i].type_entry;
+ field_val->type = resolve_struct_field_type(ira->codegen,
+ &struct_type->data.structure.fields[i]);
field_val->parent.id = ConstParentIdStruct;
field_val->parent.data.p_struct.struct_val = struct_val;
field_val->parent.data.p_struct.field_index = i;
@@ -18217,12 +18363,53 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction
return result;
}
+static IrInstruction *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name,
+ IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type)
+{
+ // The type of the field is not available until a store using this pointer happens.
+ // So, here we create a special pointer type which has the inferred struct type and
+ // field name encoded in the type. Later, when there is a store via this pointer,
+ // the field type will then be available, and the field will be added to the inferred
+ // struct.
+
+ ZigType *container_ptr_type = container_ptr->value.type;
+ ir_assert(container_ptr_type->id == ZigTypeIdPointer, source_instr);
+
+ InferredStructField *inferred_struct_field = allocate(1, "InferredStructField");
+ inferred_struct_field->inferred_struct_type = container_type;
+ inferred_struct_field->field_name = field_name;
+
+ ZigType *elem_type = ira->codegen->builtin_types.entry_c_void;
+ ZigType *field_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type,
+ container_ptr_type->data.pointer.is_const, container_ptr_type->data.pointer.is_volatile,
+ PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, inferred_struct_field);
+
+ if (instr_is_comptime(container_ptr)) {
+ IrInstruction *result = ir_const(ira, source_instr, field_ptr_type);
+ copy_const_val(&result->value, &container_ptr->value, false);
+ result->value.type = field_ptr_type;
+ return result;
+ }
+
+ IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope,
+ source_instr->source_node, field_ptr_type, container_ptr, CastOpNoop);
+ result->value.type = field_ptr_type;
+ return result;
+}
+
static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,
IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type, bool initializing)
{
Error err;
ZigType *bare_type = container_ref_type(container_type);
+
+ if (initializing && bare_type->id == ZigTypeIdStruct &&
+ bare_type->data.structure.resolve_status == ResolveStatusBeingInferred)
+ {
+ return ir_analyze_inferred_field_ptr(ira, field_name, source_instr, container_ptr, bare_type);
+ }
+
if ((err = type_resolve(ira->codegen, bare_type, ResolveStatusZeroBitsKnown)))
return ira->codegen->invalid_instruction;
@@ -19997,6 +20184,11 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
return ira->codegen->invalid_instruction;
}
+ if (container_type->data.structure.resolve_status == ResolveStatusBeingInferred) {
+ // We're now done inferring the type.
+ container_type->data.structure.resolve_status = ResolveStatusUnstarted;
+ }
+
if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown)))
return ira->codegen->invalid_instruction;
@@ -20066,8 +20258,12 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
TypeStructField *field = &container_type->data.structure.fields[i];
if (field->init_val == nullptr) {
// it's not memoized. time to go analyze it
- assert(field->decl_node->type == NodeTypeStructField);
- AstNode *init_node = field->decl_node->data.struct_field.value;
+ AstNode *init_node;
+ if (field->decl_node->type == NodeTypeStructField) {
+ init_node = field->decl_node->data.struct_field.value;
+ } else {
+ init_node = nullptr;
+ }
if (init_node == nullptr) {
ir_add_error_node(ira, instruction->source_node,
buf_sprintf("missing field: '%s'", buf_ptr(container_type->data.structure.fields[i].name)));
@@ -20124,14 +20320,18 @@ static IrInstruction *ir_analyze_container_init_fields(IrAnalyze *ira, IrInstruc
static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
IrInstructionContainerInitList *instruction)
{
- ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child);
- if (type_is_invalid(container_type))
- return ira->codegen->invalid_instruction;
+ ir_assert(instruction->result_loc != nullptr, &instruction->base);
+ IrInstruction *result_loc = instruction->result_loc->child;
+ if (type_is_invalid(result_loc->value.type))
+ return result_loc;
+ ir_assert(result_loc->value.type->id == ZigTypeIdPointer, &instruction->base);
+
+ ZigType *container_type = result_loc->value.type->data.pointer.child_type;
size_t elem_count = instruction->item_count;
if (is_slice(container_type)) {
- ir_add_error(ira, instruction->container_type,
+ ir_add_error_node(ira, instruction->init_array_type_source_node,
buf_sprintf("expected array type or [_], found slice"));
return ira->codegen->invalid_instruction;
}
@@ -20153,29 +20353,28 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
return ir_analyze_container_init_fields(ira, &instruction->base, container_type, 0, nullptr, result_loc);
}
- if (container_type->id != ZigTypeIdArray) {
+ if (container_type->id == ZigTypeIdArray) {
+ ZigType *child_type = container_type->data.array.child_type;
+ if (container_type->data.array.len != elem_count) {
+ ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count);
+
+ ir_add_error(ira, &instruction->base,
+ buf_sprintf("expected %s literal, found %s literal",
+ buf_ptr(&container_type->name), buf_ptr(&literal_type->name)));
+ return ira->codegen->invalid_instruction;
+ }
+ } else if (container_type->id == ZigTypeIdStruct &&
+ container_type->data.structure.resolve_status == ResolveStatusBeingInferred)
+ {
+ // We're now done inferring the type.
+ container_type->data.structure.resolve_status = ResolveStatusUnstarted;
+ } else {
ir_add_error_node(ira, instruction->base.source_node,
buf_sprintf("type '%s' does not support array initialization",
buf_ptr(&container_type->name)));
return ira->codegen->invalid_instruction;
}
- ir_assert(instruction->result_loc != nullptr, &instruction->base);
- IrInstruction *result_loc = instruction->result_loc->child;
- if (type_is_invalid(result_loc->value.type))
- return result_loc;
- ir_assert(result_loc->value.type->id == ZigTypeIdPointer, &instruction->base);
-
- ZigType *child_type = container_type->data.array.child_type;
- if (container_type->data.array.len != elem_count) {
- ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count);
-
- ir_add_error(ira, &instruction->base,
- buf_sprintf("expected %s literal, found %s literal",
- buf_ptr(&container_type->name), buf_ptr(&literal_type->name)));
- return ira->codegen->invalid_instruction;
- }
-
switch (type_has_one_possible_value(ira->codegen, container_type)) {
case OnePossibleValueInvalid:
return ira->codegen->invalid_instruction;
@@ -20262,16 +20461,14 @@ static IrInstruction *ir_analyze_instruction_container_init_list(IrAnalyze *ira,
static IrInstruction *ir_analyze_instruction_container_init_fields(IrAnalyze *ira,
IrInstructionContainerInitFields *instruction)
{
- IrInstruction *container_type_value = instruction->container_type->child;
- ZigType *container_type = ir_resolve_type(ira, container_type_value);
- if (type_is_invalid(container_type))
- return ira->codegen->invalid_instruction;
-
ir_assert(instruction->result_loc != nullptr, &instruction->base);
IrInstruction *result_loc = instruction->result_loc->child;
if (type_is_invalid(result_loc->value.type))
return result_loc;
+ ir_assert(result_loc->value.type->id == ZigTypeIdPointer, &instruction->base);
+ ZigType *container_type = result_loc->value.type->data.pointer.child_type;
+
return ir_analyze_container_init_fields(ira, &instruction->base, container_type,
instruction->field_count, instruction->fields, result_loc);
}
@@ -24607,6 +24804,10 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
ZigType *src_type = ptr->value.type;
assert(!type_is_invalid(src_type));
+ if (src_type == dest_type) {
+ return ptr;
+ }
+
// We have a check for zero bits later so we use get_src_ptr_type to
// validate src_type and dest_type.
@@ -24656,6 +24857,9 @@ static IrInstruction *ir_analyze_ptr_cast(IrAnalyze *ira, IrInstruction *source_
IrInstruction *result;
if (ptr->value.data.x_ptr.mut == ConstPtrMutInfer) {
result = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on);
+
+ if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown)))
+ return ira->codegen->invalid_instruction;
} else {
result = ir_const(ira, source_instr, dest_type);
}
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index 89cfab22c2..da7ae38e5f 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -731,7 +731,6 @@ static void ir_print_phi(IrPrint *irp, IrInstructionPhi *phi_instruction) {
}
static void ir_print_container_init_list(IrPrint *irp, IrInstructionContainerInitList *instruction) {
- ir_print_other_instruction(irp, instruction->container_type);
fprintf(irp->f, "{");
if (instruction->item_count > 50) {
fprintf(irp->f, "...(%" ZIG_PRI_usize " items)...", instruction->item_count);
@@ -743,11 +742,11 @@ static void ir_print_container_init_list(IrPrint *irp, IrInstructionContainerIni
ir_print_other_instruction(irp, result_loc);
}
}
- fprintf(irp->f, "}");
+ fprintf(irp->f, "}result=");
+ ir_print_other_instruction(irp, instruction->result_loc);
}
static void ir_print_container_init_fields(IrPrint *irp, IrInstructionContainerInitFields *instruction) {
- ir_print_other_instruction(irp, instruction->container_type);
fprintf(irp->f, "{");
for (size_t i = 0; i < instruction->field_count; i += 1) {
IrInstructionContainerInitFieldsField *field = &instruction->fields[i];
@@ -755,7 +754,8 @@ static void ir_print_container_init_fields(IrPrint *irp, IrInstructionContainerI
fprintf(irp->f, "%s.%s = ", comma, buf_ptr(field->name));
ir_print_other_instruction(irp, field->result_loc);
}
- fprintf(irp->f, "} // container init");
+ fprintf(irp->f, "}result=");
+ ir_print_other_instruction(irp, instruction->result_loc);
}
static void ir_print_unreachable(IrPrint *irp, IrInstructionUnreachable *instruction) {
diff --git a/src/parser.cpp b/src/parser.cpp
index a1ece3ed10..484e145cfa 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -81,7 +81,7 @@ static AstNode *ast_parse_for_type_expr(ParseContext *pc);
static AstNode *ast_parse_while_type_expr(ParseContext *pc);
static AstNode *ast_parse_switch_expr(ParseContext *pc);
static AstNode *ast_parse_asm_expr(ParseContext *pc);
-static AstNode *ast_parse_enum_lit(ParseContext *pc);
+static AstNode *ast_parse_anon_lit(ParseContext *pc);
static AstNode *ast_parse_asm_output(ParseContext *pc);
static AsmOutput *ast_parse_asm_output_item(ParseContext *pc);
static AstNode *ast_parse_asm_input(ParseContext *pc);
@@ -1600,9 +1600,9 @@ static AstNode *ast_parse_primary_type_expr(ParseContext *pc) {
if (container_decl != nullptr)
return container_decl;
- AstNode *enum_lit = ast_parse_enum_lit(pc);
- if (enum_lit != nullptr)
- return enum_lit;
+ AstNode *anon_lit = ast_parse_anon_lit(pc);
+ if (anon_lit != nullptr)
+ return anon_lit;
AstNode *error_set_decl = ast_parse_error_set_decl(pc);
if (error_set_decl != nullptr)
@@ -1876,16 +1876,22 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc) {
return res;
}
-static AstNode *ast_parse_enum_lit(ParseContext *pc) {
+static AstNode *ast_parse_anon_lit(ParseContext *pc) {
Token *period = eat_token_if(pc, TokenIdDot);
if (period == nullptr)
return nullptr;
- Token *identifier = expect_token(pc, TokenIdSymbol);
- AstNode *res = ast_create_node(pc, NodeTypeEnumLiteral, period);
- res->data.enum_literal.period = period;
- res->data.enum_literal.identifier = identifier;
- return res;
+ // anon enum literal
+ Token *identifier = eat_token_if(pc, TokenIdSymbol);
+ if (identifier != nullptr) {
+ AstNode *res = ast_create_node(pc, NodeTypeEnumLiteral, period);
+ res->data.enum_literal.period = period;
+ res->data.enum_literal.identifier = identifier;
+ return res;
+ }
+
+ // anon container literal
+ return ast_parse_init_list(pc);
}
// AsmOutput <- COLON AsmOutputList AsmInput?
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 0917c3dbb4..1adf3c3f8e 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -2,6 +2,23 @@ const tests = @import("tests.zig");
const builtin = @import("builtin");
pub fn addCases(cases: *tests.CompileErrorContext) void {
+ cases.add(
+ "missing const in slice with nested array type",
+ \\const Geo3DTex2D = struct { vertices: [][2]f32 };
+ \\pub fn getGeo3DTex2D() Geo3DTex2D {
+ \\ return Geo3DTex2D{
+ \\ .vertices = [_][2]f32{
+ \\ [_]f32{ -0.5, -0.5},
+ \\ },
+ \\ };
+ \\}
+ \\export fn entry() void {
+ \\ var geo_data = getGeo3DTex2D();
+ \\}
+ ,
+ "tmp.zig:4:30: error: expected type '[][2]f32', found '[1][2]f32'",
+ );
+
cases.add(
"slicing of global undefined pointer",
\\var buf: *[1]u8 = undefined;
@@ -216,9 +233,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ const obj = AstObject{ .lhsExpr = lhsExpr };
\\}
,
- "tmp.zig:4:19: error: union 'AstObject' depends on itself",
- "tmp.zig:2:5: note: while checking this field",
+ "tmp.zig:1:17: error: struct 'LhsExpr' depends on itself",
"tmp.zig:5:5: note: while checking this field",
+ "tmp.zig:2:5: note: while checking this field",
);
cases.add(
diff --git a/test/stage1/behavior/array.zig b/test/stage1/behavior/array.zig
index f9ca1efdb9..0558a47fa4 100644
--- a/test/stage1/behavior/array.zig
+++ b/test/stage1/behavior/array.zig
@@ -298,3 +298,17 @@ test "implicit cast zero sized array ptr to slice" {
const c: []const u8 = &b;
expect(c.len == 0);
}
+
+test "anonymous list literal syntax" {
+ const S = struct {
+ fn doTheTest() void {
+ var array: [4]u8 = .{1, 2, 3, 4};
+ expect(array[0] == 1);
+ expect(array[1] == 2);
+ expect(array[2] == 3);
+ expect(array[3] == 4);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/stage1/behavior/async_fn.zig b/test/stage1/behavior/async_fn.zig
index 99efa0e7be..f44ca541eb 100644
--- a/test/stage1/behavior/async_fn.zig
+++ b/test/stage1/behavior/async_fn.zig
@@ -1214,7 +1214,7 @@ test "spill target expr in a for loop" {
}
const Foo = struct {
- slice: []i32,
+ slice: []const i32,
};
fn atest(foo: *Foo) i32 {
@@ -1245,7 +1245,7 @@ test "spill target expr in a for loop, with a var decl in the loop body" {
}
const Foo = struct {
- slice: []i32,
+ slice: []const i32,
};
fn atest(foo: *Foo) i32 {
diff --git a/test/stage1/behavior/struct.zig b/test/stage1/behavior/struct.zig
index 76ecad6b43..a42c261e3b 100644
--- a/test/stage1/behavior/struct.zig
+++ b/test/stage1/behavior/struct.zig
@@ -709,3 +709,62 @@ test "packed struct field passed to generic function" {
var loaded = S.genericReadPackedField(&p.b);
expect(loaded == 29);
}
+
+test "anonymous struct literal syntax" {
+ const S = struct {
+ const Point = struct {
+ x: i32,
+ y: i32,
+ };
+
+ fn doTheTest() void {
+ var p: Point = .{
+ .x = 1,
+ .y = 2,
+ };
+ expect(p.x == 1);
+ expect(p.y == 2);
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "fully anonymous struct" {
+ const S = struct {
+ fn doTheTest() void {
+ dump(.{
+ .int = @as(u32, 1234),
+ .float = @as(f64, 12.34),
+ .b = true,
+ .s = "hi",
+ });
+ }
+ fn dump(args: var) void {
+ expect(args.int == 1234);
+ expect(args.float == 12.34);
+ expect(args.b);
+ expect(args.s[0] == 'h');
+ expect(args.s[1] == 'i');
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
+
+test "fully anonymous list literal" {
+ const S = struct {
+ fn doTheTest() void {
+ dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi"});
+ }
+ fn dump(args: var) void {
+ expect(args.@"0" == 1234);
+ expect(args.@"1" == 12.34);
+ expect(args.@"2");
+ expect(args.@"3"[0] == 'h');
+ expect(args.@"3"[1] == 'i');
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}
diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig
index 497aa7e574..caad1d474f 100644
--- a/test/stage1/behavior/union.zig
+++ b/test/stage1/behavior/union.zig
@@ -549,3 +549,25 @@ test "initialize global array of union" {
expect(glbl_array[0].U0 == 1);
expect(glbl_array[1].U1 == 2);
}
+
+test "anonymous union literal syntax" {
+ const S = struct {
+ const Number = union {
+ int: i32,
+ float: f64,
+ };
+
+ fn doTheTest() void {
+ var i: Number = .{.int = 42};
+ var f = makeNumber();
+ expect(i.int == 42);
+ expect(f.float == 12.34);
+ }
+
+ fn makeNumber() Number {
+ return .{.float = 12.34};
+ }
+ };
+ S.doTheTest();
+ comptime S.doTheTest();
+}