Skip to content

Commit

Permalink
fix: Anonymous object with fields in different order were not equal
Browse files Browse the repository at this point in the history
We now sort fields by their name so that indexes don't change regardless of their order in the code
  • Loading branch information
giann committed Sep 20, 2024
1 parent c287fc7 commit 83e2c01
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/Parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4103,6 +4103,8 @@ fn anonymousObjectInit(self: *Self, _: bool) Error!Ast.Node.Index {
}
}

try object_type.resolved_type.?.Object.sortFieldIndexes(self.gc.allocator);

try self.consume(.RightBrace, "Expected `}` after object initialization.");

return try self.ast.appendNode(
Expand Down Expand Up @@ -6648,6 +6650,8 @@ fn objectDeclaration(self: *Self) Error!Ast.Node.Index {
}
}

try object_type.resolved_type.?.Object.sortFieldIndexes(self.gc.allocator);

try self.consume(.RightBrace, "Expected `}` after object body.");

const scope_end = try self.endScope();
Expand Down
48 changes: 48 additions & 0 deletions src/obj.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1768,6 +1768,54 @@ pub const ObjObject = struct {
};
}

fn compareStrings(_: void, lhs: []const u8, rhs: []const u8) bool {
return std.mem.order(u8, lhs, rhs).compare(std.math.CompareOperator.lt);
}

// Anonymous object can have the same fields but in a different order
// This function will sort them by name and fix their index accordingly
pub fn sortFieldIndexes(self: *ObjectDef, allocator: Allocator) !void {
// Don't do anything on regular objects
if (!self.anonymous) {
return;
}

const fields = try allocator.alloc(
[]const u8,
self.fields.keys().len,
);
defer allocator.free(fields);
std.mem.copyForwards(
[]const u8,
fields,
self.fields.keys(),
);

std.mem.sort(
[]const u8,
fields,
{},
compareStrings,
);

for (fields, 0..) |field_name, index| {
const original_field = self.fields.get(field_name).?;
try self.fields.put(
field_name,
.{
.name = original_field.name,
.type_def = original_field.type_def,
.constant = original_field.constant,
.method = original_field.method,
.static = original_field.static,
.location = original_field.location,
.has_default = original_field.has_default,
.index = index,
},
);
}
}

pub fn deinit(self: *ObjectDef) void {
self.fields.deinit();
self.placeholders.deinit();
Expand Down
21 changes: 21 additions & 0 deletions tests/042-anonymous-objects.buzz
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,24 @@ test "Anonymous object with generics" {

std\assert(payload.data == 42, message: "Could use anonymous object with generic");
}

fun callMe(o: obj{ x: int, y: int }) > void {
std\assert(o.x == 12 and o.y == 13);
}

test "anonymous objects with different fields order" {
callMe(.{ y = 13, x = 12 });
}

// placeholders in anonymous object fields works
object A {
board: [int],

fun free() > void {
foreach (x, y in this.board) {
var board = [<obj{ x: int }>];

board.append(.{ x = x });
}
}
}

0 comments on commit 83e2c01

Please sign in to comment.