Fix field access left assign
All checks were successful
CI / ci (ReleaseFast, ubuntu-latest) (push) Successful in 2m10s
CI / ci (ReleaseSmall, ubuntu-latest) (push) Successful in 2m9s
CI / ci (ReleaseSafe, ubuntu-latest) (push) Successful in 2m17s
CI / ci (Debug, ubuntu-latest) (push) Successful in 3m21s

This commit is contained in:
David Gonzalez Martin 2025-04-18 12:52:59 -06:00
parent 91a23d6976
commit d421189c45
4 changed files with 224 additions and 115 deletions

View File

@ -674,10 +674,11 @@ const Unary = struct {
@"+",
@"&",
@"!",
@"~",
pub fn is_boolean(id: Id) bool {
return switch (id) {
.@"+", .@"-", .@"&" => false,
.@"+", .@"-", .@"&", .@"~" => false,
.@"!" => true,
};
}
@ -1800,6 +1801,13 @@ pub const Module = struct {
};
count += 1;
r[@intFromEnum(Token.Id.@"~")] = .{
.before = rule_before_unary,
.after = null,
.precedence = .none,
};
count += 1;
const bitwise_operators = [_]Token.Id{
.@"|",
.@"^",
@ -2208,6 +2216,11 @@ pub const Module = struct {
},
};
},
'~' => blk: {
module.offset += 1;
break :blk .@"~";
},
else => @trap(),
};
@ -2665,6 +2678,7 @@ pub const Module = struct {
.field = identifier,
},
},
.kind = value_builder.kind,
};
return value;
}
@ -3040,6 +3054,7 @@ pub const Module = struct {
.@"+" => .@"+",
.@"&" => .@"&",
.@"!" => .@"!",
.@"~" => .@"~",
else => @trap(),
};
@ -5144,6 +5159,62 @@ pub const Module = struct {
module.emit_value(function, value, type_kind);
}
pub fn analyze_field_access_type(module: *Module, function: ?*Global, value: *Value) *Type {
switch (value.bb) {
.field_access => |field_access| {
module.analyze_value_type(function, field_access.aggregate, .{});
const field_name = field_access.field;
switch (field_access.aggregate.kind) {
.left => {
if (field_access.aggregate.type.?.bb != .pointer) {
module.report_error();
}
const pty = field_access.aggregate.type.?.bb.pointer.type;
const ty = switch (pty.bb) {
.pointer => |pointer| pointer.type,
else => pty,
};
switch (ty.bb) {
.structure => |structure| {
const field_type = for (structure.fields) |*field| {
if (lib.string.equal(field_name, field.name)) {
break field.type;
}
} else {
module.report_error();
};
return switch (value.kind) {
.left => module.get_pointer_type(.{ .type = field_type }),
.right => field_type,
};
},
.bits => |bits| {
const field_type = for (bits.fields) |*field| {
if (lib.string.equal(field_name, field.name)) {
break field.type;
}
} else {
module.report_error();
};
return switch (value.kind) {
.left => module.report_error(),
.right => field_type,
};
},
.pointer => module.report_error(),
else => @trap(),
}
},
.right => module.report_error(),
}
},
else => unreachable,
}
}
pub fn analyze_value_type(module: *Module, function: ?*Global, value: *Value, analysis: ValueAnalysis) void {
assert(value.type == null);
assert(value.llvm == null);
@ -5175,8 +5246,7 @@ pub const Module = struct {
},
else => @trap(),
},
.unary => |unary| {
switch (unary.id) {
.unary => |unary| switch (unary.id) {
.@"+" => @trap(),
.@"-" => {
module.analyze_value_type(function, unary.value, analysis);
@ -5184,11 +5254,15 @@ pub const Module = struct {
module.report_error();
}
assert(expected_type == unary.value.type);
if (expected_type != unary.value.type) {
module.report_error();
}
},
.@"&" => {
module.analyze_value_type(function, unary.value, analysis);
assert(expected_type == unary.value.type);
if (expected_type != unary.value.type) {
module.report_error();
}
},
.@"!" => {
@trap();
@ -5201,7 +5275,12 @@ pub const Module = struct {
// else => @trap(),
// }
},
}
.@"~" => {
module.analyze_value_type(function, unary.value, analysis);
if (expected_type != unary.value.type) {
module.report_error();
}
},
},
.binary => |binary| {
const is_boolean = binary.id.is_boolean();
@ -5270,6 +5349,19 @@ pub const Module = struct {
module.report_error();
}
},
.integer_max => |int_type| {
if (int_type.bb != .integer) {
module.report_error();
}
if (expected_type.bb != .integer) {
module.report_error();
}
if (int_type != expected_type) {
@trap();
}
},
.int_from_pointer => |pointer_value| {
module.analyze_value_type(function, pointer_value, .{});
assert(pointer_value.type != null);
@ -5417,8 +5509,7 @@ pub const Module = struct {
// module.report_error();
// };
},
.aggregate_initialization => |*aggregate_initialization| {
switch (expected_type.bb) {
.aggregate_initialization => |*aggregate_initialization| switch (expected_type.bb) {
.bits => |bits| {
var is_ordered = true;
var is_constant = true;
@ -5456,50 +5547,11 @@ pub const Module = struct {
aggregate_initialization.is_constant = is_constant and is_ordered;
},
else => @trap(),
}
},
.field_access => |field_access| {
module.analyze_value_type(function, field_access.aggregate, .{});
const field_name = field_access.field;
switch (field_access.aggregate.kind) {
.left => switch (field_access.aggregate.type.?.bb) {
.pointer => |pointer| switch (pointer.type.bb) {
.structure => |structure| {
const field_type = for (structure.fields) |*field| {
if (lib.string.equal(field_name, field.name)) {
break field.type;
}
} else {
module.report_error();
};
switch (value.kind) {
.left => @trap(),
.right => if (field_type != expected_type) {
module.report_error();
},
}
},
.bits => |bits| {
const field_type = for (bits.fields) |*field| {
if (lib.string.equal(field_name, field.name)) {
break field.type;
}
} else {
module.report_error();
};
switch (value.kind) {
.left => module.report_error(),
.right => if (field_type != expected_type) {
module.report_error();
},
}
},
else => @trap(),
},
else => module.report_error(),
},
.right => module.report_error(),
.field_access => {
const field_type = module.analyze_field_access_type(function, value);
if (field_type != expected_type) {
module.report_error();
}
},
.zero => {},
@ -5569,7 +5621,7 @@ pub const Module = struct {
});
} else {
module.analyze_value_type(function, binary.left, .{});
module.analyze_value_type(function, binary.right, .{});
module.analyze_value_type(function, binary.right, .{ .type = if (binary.left.type.?.bb != .pointer) binary.left.type.? else null });
}
const is_boolean = binary.id.is_boolean();
@ -5949,46 +6001,7 @@ pub const Module = struct {
},
}
},
.field_access => |field_access| blk: {
module.analyze_value_type(function, field_access.aggregate, .{});
const field_name = field_access.field;
switch (field_access.aggregate.kind) {
.left => switch (field_access.aggregate.type.?.bb) {
.pointer => |pointer| switch (pointer.type.bb) {
.structure => |structure| {
const field_type = for (structure.fields) |*field| {
if (lib.string.equal(field_name, field.name)) {
break field.type;
}
} else {
module.report_error();
};
break :blk switch (value.kind) {
.left => @trap(),
.right => field_type,
};
},
.bits => |bits| {
const field_type = for (bits.fields) |*field| {
if (lib.string.equal(field_name, field.name)) {
break field.type;
}
} else {
module.report_error();
};
break :blk switch (value.kind) {
.left => module.report_error(),
.right => field_type,
};
},
else => @trap(),
},
else => module.report_error(),
},
.right => module.report_error(),
}
},
.field_access => module.analyze_field_access_type(function, value),
.string_literal => module.get_slice_type(.{ .type = module.integer_type(8, false) }),
.slice_expression => |slice_expression| blk: {
module.analyze_value_type(function, slice_expression.array_like, .{});
@ -6050,29 +6063,43 @@ pub const Module = struct {
.left => @trap(),
.right => {
assert(structure.is_slice);
const slice_pointer_type = value_type.bb.structure.fields[0].type;
const slice_element_type = slice_pointer_type.bb.pointer.type;
const is_start_zero = slice_expression.start.bb == .constant_integer and slice_expression.start.bb.constant_integer.value == 0;
if (slice_expression.end) |end| {
_ = end;
@trap();
} else {
if (is_start_zero) {
// TODO: consider if we should emit an error here or it be a NOP
module.report_error();
} else {
const old_slice_pointer = module.llvm.builder.create_extract_value(slice_expression.array_like.llvm.?, 0);
const old_slice_length = module.llvm.builder.create_extract_value(slice_expression.array_like.llvm.?, 1);
const slice_pointer = switch (is_start_zero) {
true => old_slice_pointer,
false => b: {
module.emit_value(function, slice_expression.start, .memory);
const old_slice_pointer = module.llvm.builder.create_extract_value(slice_expression.array_like.llvm.?, 0);
const old_slice_length = module.llvm.builder.create_extract_value(slice_expression.array_like.llvm.?, 1);
const slice_pointer = module.llvm.builder.create_gep(.{
break :b module.llvm.builder.create_gep(.{
.type = slice_element_type.llvm.memory.?,
.aggregate = old_slice_pointer,
.indices = &.{slice_expression.start.llvm.?},
});
const slice_length = module.llvm.builder.create_sub(old_slice_length, slice_expression.start.llvm.?);
return .{ slice_pointer, slice_length };
},
};
const slice_length = if (slice_expression.end) |end| b: {
module.emit_value(function, end, .memory);
const slice_length = switch (is_start_zero) {
true => end.llvm.?,
false => module.llvm.builder.create_sub(end.llvm.?, slice_expression.start.llvm.?),
};
break :b slice_length;
} else b: {
if (is_start_zero) {
module.report_error();
}
}
const slice_length = module.llvm.builder.create_sub(old_slice_length, slice_expression.start.llvm.?);
break :b slice_length;
};
return .{ slice_pointer, slice_length };
},
},
else => @trap(),
@ -6115,6 +6142,10 @@ pub const Module = struct {
else => @trap(),
},
},
.@"~" => b: {
module.emit_value(function, unary.value, type_kind);
break :b module.llvm.builder.create_not(unary.value.llvm.?);
},
else => @trap(),
},
.binary => |binary| blk: {
@ -6201,8 +6232,8 @@ pub const Module = struct {
true => module.llvm.builder.create_srem(left, right),
false => module.llvm.builder.create_urem(left, right),
},
.@"&" => module.llvm.builder.create_and(left, right),
.@"|" => module.llvm.builder.create_or(left, right),
.@"&", .@"and" => module.llvm.builder.create_and(left, right),
.@"|", .@"or" => module.llvm.builder.create_or(left, right),
.@"^" => module.llvm.builder.create_xor(left, right),
.@"<<" => module.llvm.builder.create_shl(left, right),
.@">>" => switch (integer.signed) {
@ -6533,8 +6564,20 @@ pub const Module = struct {
module.emit_value(function, field_access.aggregate, .memory);
const field_name = field_access.field;
switch (field_access.aggregate.kind) {
.left => switch (field_access.aggregate.type.?.bb) {
.pointer => |pointer| switch (pointer.type.bb) {
.left => {
const base_type = field_access.aggregate.type.?;
assert(base_type.bb == .pointer);
const pointer_type = switch (base_type.bb.pointer.type.bb) {
.pointer => base_type.bb.pointer.type,
else => base_type,
};
const ty = pointer_type.bb.pointer.type;
const v = switch (pointer_type == base_type) {
false => module.create_load(.{ .type = pointer_type, .value = field_access.aggregate.llvm.? }),
true => field_access.aggregate.llvm.?,
};
switch (ty.bb) {
.structure => |structure| {
const field_index: u32 = for (structure.fields, 0..) |field, field_index| {
if (lib.string.equal(field_name, field.name)) {
@ -6542,8 +6585,7 @@ pub const Module = struct {
}
} else module.report_error();
pointer.type.resolve(module);
const gep = module.llvm.builder.create_struct_gep(pointer.type.llvm.memory.?.to_struct(), field_access.aggregate.llvm.?, field_index);
const gep = module.llvm.builder.create_struct_gep(ty.llvm.memory.?.to_struct(), v, field_index);
break :blk switch (value.kind) {
.left => gep,
.right => module.create_load(.{
@ -6561,14 +6603,13 @@ pub const Module = struct {
const field = bits.fields[field_index];
field.type.resolve(module);
const load = module.create_load(.{ .type = pointer.type, .alignment = pointer.alignment, .value = field_access.aggregate.llvm.? });
const load = module.create_load(.{ .type = ty, .alignment = pointer_type.bb.pointer.alignment, .value = v });
const shift = module.llvm.builder.create_lshr(load, bits.backing_type.llvm.abi.?.to_integer().get_constant(field.bit_offset, 0).to_value());
const trunc = module.llvm.builder.create_truncate(shift, field.type.llvm.abi.?);
break :blk trunc;
},
else => @trap(),
},
else => @trap(),
}
},
.right => switch (field_access.aggregate.type.?.bb) {
else => @trap(),
@ -7677,6 +7718,8 @@ const Token = union(Id) {
@">>",
// Logical NOT operator
@"!",
// Bitwise NOT operator
@"~",
// Pointer dereference
@".&",
// Parenthesis
@ -7743,6 +7786,8 @@ const Token = union(Id) {
@">>",
// Logical NOT operator
@"!",
// Bitwise NOT operator
@"~",
// Pointer dereference
@".&",
// Parenthesis

View File

@ -1,6 +1,22 @@
[extern] memcmp = fn [cc(c)] (a: &u8, b: &u8, byte_count: u64) s32;
[extern] exit = fn [cc(c)] (exit_code: s32) noreturn;
assert = fn (ok: u1) void
{
if (!ok)
{
unreachable;
}
}
align_forward = fn (value: u64, alignment: u64) u64
{
assert(alignment != 0);
>mask = alignment - 1;
>result = (value + mask) & ~mask;
return result;
}
string_no_match = #integer_max(u64);
c_string_length = fn (c_string: &u8) u64
@ -212,6 +228,24 @@ arena_initialize_default = fn (initial_size: u64) &Arena
});
}
arena_allocate_bytes = fn (arena: &Arena, size: u64, alignment: u64) &u8
{
>aligned_offset = align_forward(arena.position, alignment);
>aligned_size_after = aligned_offset + size;
if (aligned_size_after > arena.os_position)
{
#trap();
}
>arena_byte_pointer: &u8 = #pointer_cast(arena);
>result = arena_byte_pointer + aligned_offset;
arena.position = aligned_size_after;
assert(arena.position <= arena.os_position);
return result;
}
GlobalState = struct
{
arena: &Arena,
@ -281,6 +315,11 @@ compile_file = fn (arena: &Arena, compile: CompileFile) void
{
separator_index = 0;
}
>base_start = separator_index + #extend(separator_index != 0 or relative_file_path[separator_index] == '/');
>base_name = relative_file_path[base_start..extension_start];
>is_compiler = string_equal(relative_file_path, "src/compiler.bbb");
}
[export] main = fn [cc(c)] (argument_count: u32, argv: &&u8) s32

View File

@ -315,4 +315,5 @@ const names = &[_][]const u8{
"else_if",
"else_if_complicated",
"shortcircuiting_if",
"field_access_left_assign",
};

View File

@ -0,0 +1,24 @@
S = struct
{
a: u32,
b: u32,
}
require = fn (ok: u1) void
{
if (!ok) #trap();
}
[export] main = fn [cc(c)] () s32
{
>s: S = {
.a = 2,
.b = 3,
};
s.a = s.b + 1;
s.b = s.a + 2;
require(s.a == 4);
require(s.b == 6);
return 0;
}