From 83e2c01483451668e0d2e14d0dd908ac87025e8f Mon Sep 17 00:00:00 2001 From: Benoit Giannangeli Date: Fri, 20 Sep 2024 12:17:03 +0200 Subject: [PATCH] fix: Anonymous object with fields in different order were not equal We now sort fields by their name so that indexes don't change regardless of their order in the code --- src/Parser.zig | 4 +++ src/obj.zig | 48 ++++++++++++++++++++++++++++++++ tests/042-anonymous-objects.buzz | 21 ++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/src/Parser.zig b/src/Parser.zig index 315153a6..a474b40e 100644 --- a/src/Parser.zig +++ b/src/Parser.zig @@ -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( @@ -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(); diff --git a/src/obj.zig b/src/obj.zig index e255a4d3..ead8636e 100644 --- a/src/obj.zig +++ b/src/obj.zig @@ -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(); diff --git a/tests/042-anonymous-objects.buzz b/tests/042-anonymous-objects.buzz index 68be5524..ceb17c68 100644 --- a/tests/042-anonymous-objects.buzz +++ b/tests/042-anonymous-objects.buzz @@ -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 = []; + + board.append(.{ x = x }); + } + } +} \ No newline at end of file