authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-20 15:36:40-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-01-20 16:17:16-07:00
log4fccc95b0152aefeb40912768ec045b57a2fdc2b
tree711ec4766c0cd236b239eb448398d2162c770068
parente86ff712a666ab5be54fa763cc12a5f245718117

Sema: fix requiresComptime infinite recursion

When asking a struct or union whether the type requires comptime, it may need to ask itself recursively, for example because of a field which is a pointer to itself. This commit adds a field to each to keep track of when computing the "requires comptime" value and returns `false` if the check is already ongoing.

2 files changed, 37 insertions(+), 11 deletions(-)

src/Module.zig+4
...@@ -821,6 +821,8 @@ pub const ErrorSet = struct {...@@ -821,6 +821,8 @@ pub const ErrorSet = struct {
821 }821 }
822};822};
823823
824pub const RequiresComptime = enum { no, yes, unknown, wip };
825
824/// Represents the data that a struct declaration provides.826/// Represents the data that a struct declaration provides.
825pub const Struct = struct {827pub const Struct = struct {
826 /// The Decl that corresponds to the struct itself.828 /// The Decl that corresponds to the struct itself.
...@@ -849,6 +851,7 @@ pub const Struct = struct {...@@ -849,6 +851,7 @@ pub const Struct = struct {
849 /// If true, definitely nonzero size at runtime. If false, resolving the fields851 /// If true, definitely nonzero size at runtime. If false, resolving the fields
850 /// is necessary to determine whether it has bits at runtime.852 /// is necessary to determine whether it has bits at runtime.
851 known_has_bits: bool,853 known_has_bits: bool,
854 requires_comptime: RequiresComptime = .unknown,
852855
853 pub const Fields = std.StringArrayHashMapUnmanaged(Field);856 pub const Fields = std.StringArrayHashMapUnmanaged(Field);
854857
...@@ -1038,6 +1041,7 @@ pub const Union = struct {...@@ -1038,6 +1041,7 @@ pub const Union = struct {
1038 // which `have_layout` does not ensure.1041 // which `have_layout` does not ensure.
1039 fully_resolved,1042 fully_resolved,
1040 },1043 },
1044 requires_comptime: RequiresComptime = .unknown,
10411045
1042 pub const Field = struct {1046 pub const Field = struct {
1043 /// undefined until `status` is `have_field_types` or `have_layout`.1047 /// undefined until `status` is `have_field_types` or `have_layout`.
src/type.zig+33-11
...@@ -1544,22 +1544,40 @@ pub const Type = extern union {...@@ -1544,22 +1544,40 @@ pub const Type = extern union {
15441544
1545 .@"struct" => {1545 .@"struct" => {
1546 const struct_obj = ty.castTag(.@"struct").?.data;1546 const struct_obj = ty.castTag(.@"struct").?.data;
1547 for (struct_obj.fields.values()) |field| {1547 switch (struct_obj.requires_comptime) {
1548 if (requiresComptime(field.ty)) {1548 .no, .wip => return false,
1549 return true;1549 .yes => return true,
1550 }1550 .unknown => {
1551 struct_obj.requires_comptime = .wip;
1552 for (struct_obj.fields.values()) |field| {
1553 if (requiresComptime(field.ty)) {
1554 struct_obj.requires_comptime = .yes;
1555 return true;
1556 }
1557 }
1558 struct_obj.requires_comptime = .no;
1559 return false;
1560 },
1551 }1561 }
1552 return false;
1553 },1562 },
15541563
1555 .@"union", .union_tagged => {1564 .@"union", .union_tagged => {
1556 const union_obj = ty.cast(Payload.Union).?.data;1565 const union_obj = ty.cast(Payload.Union).?.data;
1557 for (union_obj.fields.values()) |field| {1566 switch (union_obj.requires_comptime) {
1558 if (requiresComptime(field.ty)) {1567 .no, .wip => return false,
1559 return true;1568 .yes => return true,
1560 }1569 .unknown => {
1570 union_obj.requires_comptime = .wip;
1571 for (union_obj.fields.values()) |field| {
1572 if (requiresComptime(field.ty)) {
1573 union_obj.requires_comptime = .yes;
1574 return true;
1575 }
1576 }
1577 union_obj.requires_comptime = .no;
1578 return false;
1579 },
1561 }1580 }
1562 return false;
1563 },1581 },
15641582
1565 .error_union => return requiresComptime(errorUnionPayload(ty)),1583 .error_union => return requiresComptime(errorUnionPayload(ty)),
...@@ -3661,7 +3679,7 @@ pub const Type = extern union {...@@ -3661,7 +3679,7 @@ pub const Type = extern union {
3661 .Slice, .Many, .C => true,3679 .Slice, .Many, .C => true,
3662 .One => ty.elemType().zigTypeTag() == .Array,3680 .One => ty.elemType().zigTypeTag() == .Array,
3663 },3681 },
3664 .Struct => ty.tag() == .tuple,3682 .Struct => ty.isTuple(),
3665 else => false,3683 else => false,
3666 };3684 };
3667 }3685 }
...@@ -4501,6 +4519,10 @@ pub const Type = extern union {...@@ -4501,6 +4519,10 @@ pub const Type = extern union {
4501 }4519 }
4502 };4520 };
45034521
4522 pub fn isTuple(ty: Type) bool {
4523 return ty.tag() == .tuple;
4524 }
4525
4504 /// The sub-types are named after what fields they contain.4526 /// The sub-types are named after what fields they contain.
4505 pub const Payload = struct {4527 pub const Payload = struct {
4506 tag: Tag,4528 tag: Tag,