From 0ca705f070a02db07cb4a92622846a135e7c7328 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Mon, 23 Sep 2024 15:19:51 -0700 Subject: [PATCH] Parser: add frontend support for global var relocations --- src/aro/Attribute.zig | 1 + src/aro/Diagnostics/messages.def | 4 ++ src/aro/Parser.zig | 99 +++++++++++++++++++++++++------- src/aro/Tree.zig | 2 +- src/aro/Value.zig | 93 ++++++++++++++++++++++++++++-- src/backend/Interner.zig | 42 ++++++++++++++ test/cases/const decl folding.c | 10 ++-- test/cases/relocations.c | 44 ++++++++++++++ 8 files changed, 264 insertions(+), 31 deletions(-) create mode 100644 test/cases/relocations.c diff --git a/src/aro/Attribute.zig b/src/aro/Attribute.zig index 2bdf5be2..01fac90c 100644 --- a/src/aro/Attribute.zig +++ b/src/aro/Attribute.zig @@ -340,6 +340,7 @@ fn diagnoseField( .array_ty, .vector_ty, .record_ty, + .global_var_offset, => unreachable, }); } diff --git a/src/aro/Diagnostics/messages.def b/src/aro/Diagnostics/messages.def index 5e25c4c8..949ea767 100644 --- a/src/aro/Diagnostics/messages.def +++ b/src/aro/Diagnostics/messages.def @@ -2530,3 +2530,7 @@ auto_type_self_initialized .msg = "variable '{s}' declared with deduced type '__auto_type' cannot appear in its own initializer" .extra = .str .kind = .@"error" + +non_constant_initializer + .msg = "initializer element is not a compile-time constant" + .kind = .@"error" diff --git a/src/aro/Parser.zig b/src/aro/Parser.zig index 28b658e8..3e50eed0 100644 --- a/src/aro/Parser.zig +++ b/src/aro/Parser.zig @@ -486,9 +486,9 @@ pub fn valueChangedStr(p: *Parser, res: *Result, old_value: Value, int_ty: Type) try w.writeAll(" changes "); if (res.val.isZero(p.comp)) try w.writeAll("non-zero "); try w.writeAll("value from "); - try old_value.print(res.ty, p.comp, w); + try old_value.print(res.ty, p.comp, p.comp.string_interner.getSlowTypeMapper(), w); try w.writeAll(" to "); - try res.val.print(int_ty, p.comp, w); + try res.val.print(int_ty, p.comp, p.comp.string_interner.getSlowTypeMapper(), w); return try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]); } @@ -1284,16 +1284,11 @@ fn staticAssert(p: *Parser) Error!bool { try p.errStr(.pre_c23_compat, static_assert, "'_Static_assert' with no message"); } - // Array will never be zero; a value of zero for a pointer is a null pointer constant - if ((res.ty.isArray() or res.ty.isPtr()) and !res.val.isZero(p.comp)) { - const err_start = p.comp.diagnostics.list.items.len; - try p.errTok(.const_decl_folded, res_token); - if (res.ty.isPtr() and err_start != p.comp.diagnostics.list.items.len) { - // Don't show the note if the .const_decl_folded diagnostic was not added - try p.errTok(.constant_expression_conversion_not_allowed, res_token); - } - } + const is_int_expr = res.ty.isInt(); try res.boolCast(p, .{ .specifier = .bool }, res_token); + if (!is_int_expr) { + res.val = .{}; + } if (res.val.opt_ref == .none) { if (res.ty.specifier != .invalid) { try p.errTok(.static_assert_not_constant, res_token); @@ -5183,7 +5178,7 @@ pub const Result = struct { const strings_top = p.strings.items.len; defer p.strings.items.len = strings_top; - try res.val.print(res.ty, p.comp, p.strings.writer()); + try res.val.print(res.ty, p.comp, p.comp.string_interner.getSlowTypeMapper(), p.strings.writer()); return try p.comp.diagnostics.arena.allocator().dupe(u8, p.strings.items[strings_top..]); } @@ -6589,8 +6584,8 @@ fn eqExpr(p: *Parser) Error!Result { if (try lhs.adjustTypes(ne.?, &rhs, p, .equality)) { const op: std.math.CompareOperator = if (tag == .equal_expr) .eq else .neq; - const res = lhs.val.compare(op, rhs.val, p.comp); - lhs.val = Value.fromBool(res); + const res = lhs.val.compareExtra(op, rhs.val, p.comp); + lhs.val = if (res) |val| Value.fromBool(val) else .{}; } else { lhs.val.boolCast(p.comp); } @@ -6620,8 +6615,8 @@ fn compExpr(p: *Parser) Error!Result { .greater_than_equal_expr => .gte, else => unreachable, }; - const res = lhs.val.compare(op, rhs.val, p.comp); - lhs.val = Value.fromBool(res); + const res = lhs.val.compareExtra(op, rhs.val, p.comp); + lhs.val = if (res) |val| Value.fromBool(val) else .{}; } else { lhs.val.boolCast(p.comp); } @@ -6677,7 +6672,7 @@ fn addExpr(p: *Parser) Error!Result { if (try lhs.val.add(lhs.val, rhs.val, lhs.ty, p.comp) and lhs.ty.signedness(p.comp) != .unsigned) try p.errOverflow(plus.?, lhs); } else { - if (try lhs.val.sub(lhs.val, rhs.val, lhs.ty, p.comp) and + if (try lhs.val.sub(lhs.val, rhs.val, lhs.ty, rhs.ty, p.comp) and lhs.ty.signedness(p.comp) != .unsigned) try p.errOverflow(minus.?, lhs); } } @@ -7034,6 +7029,54 @@ fn offsetofMemberDesignator(p: *Parser, base_ty: Type, want_bits: bool) Error!Re return Result{ .ty = base_ty, .val = val, .node = lhs.node }; } +const Reloc = struct { + global: StringId, + offset: i64, +}; + +fn computeOffsetExtra(p: *Parser, node: NodeIndex, offset_so_far: i64) !Reloc { + const tys = p.nodes.items(.ty); + const tags = p.nodes.items(.tag); + const data = p.nodes.items(.data); + const tag = tags[@intFromEnum(node)]; + + switch (tag) { + .implicit_cast => { + const cast_data = data[@intFromEnum(node)].cast; + return switch (cast_data.kind) { + .array_to_pointer => p.computeOffsetExtra(cast_data.operand, offset_so_far), + else => unreachable, + }; + }, + .paren_expr => return p.computeOffsetExtra(data[@intFromEnum(node)].un, offset_so_far), + .decl_ref_expr => { + const var_name = try p.comp.internString(p.tokSlice(data[@intFromEnum(node)].decl_ref)); + return .{ .global = var_name, .offset = offset_so_far }; + }, + .array_access_expr => { + const bin_data = data[@intFromEnum(node)].bin; + const ty = tys[@intFromEnum(node)]; + + const index_val = p.value_map.get(bin_data.rhs) orelse return error.InvalidReloc; + const as_int = index_val.toInt(i64, p.comp).?; + const size = ty.sizeof(p.comp).?; + const this_offset = @as(i64, @intCast(size)) * as_int; + return p.computeOffsetExtra(bin_data.lhs, this_offset + offset_so_far); + }, + .member_access_expr => { + const member = data[@intFromEnum(node)].member; + const record = tys[@intFromEnum(member.lhs)].getRecord().?; + const field_offset: i64 = @intCast(@divExact(record.fields[member.index].layout.offset_bits, 8)); + return p.computeOffsetExtra(member.lhs, offset_so_far + field_offset); + }, + else => return error.InvalidReloc, + } +} + +fn computeOffset(p: *Parser, node: NodeIndex) !Reloc { + return p.computeOffsetExtra(node, 0); +} + /// unExpr /// : (compoundLiteral | primaryExpr) suffixExpr* /// | '&&' IDENTIFIER @@ -7074,9 +7117,11 @@ fn unExpr(p: *Parser) Error!Result { try p.err(.invalid_preproc_operator); return error.ParsingFailed; } + const ampersand_tok = p.tok_i; p.tok_i += 1; var operand = try p.castExpr(); try operand.expect(p); + var addr_val: Value = .{}; const tree = p.tmpTree(); if (p.getNode(operand.node, .member_access_expr) orelse @@ -7084,12 +7129,23 @@ fn unExpr(p: *Parser) Error!Result { { if (tree.isBitfield(member_node)) try p.errTok(.addr_of_bitfield, tok); } - if (!tree.isLval(operand.node) and !operand.ty.is(.invalid)) { + const operand_ty_valid = !operand.ty.is(.invalid); + if (!tree.isLval(operand.node) and operand_ty_valid) { try p.errTok(.addr_of_rvalue, tok); + } else if (operand_ty_valid and p.func.ty == null) { + // address of global + const reloc: Reloc = p.computeOffset(operand.node) catch |e| switch (e) { + error.InvalidReloc => blk: { + try p.errTok(.non_constant_initializer, ampersand_tok); + break :blk .{ .global = .empty, .offset = 0 }; + }, + else => |er| return er, + }; + addr_val = try Value.reloc(reloc.global, reloc.offset, p.comp); } if (operand.ty.qual.register) try p.errTok(.addr_of_register, tok); - if (!operand.ty.is(.invalid)) { + if (operand_ty_valid) { const elem_ty = try p.arena.create(Type); elem_ty.* = operand.ty; operand.ty = Type{ @@ -7099,6 +7155,7 @@ fn unExpr(p: *Parser) Error!Result { } try operand.saveValue(p); try operand.un(p, .addr_of_expr, tok); + operand.val = addr_val; return operand; }, .asterisk => { @@ -7144,7 +7201,7 @@ fn unExpr(p: *Parser) Error!Result { try operand.usualUnaryConversion(p, tok); if (operand.val.isArithmetic(p.comp)) { - _ = try operand.val.sub(Value.zero, operand.val, operand.ty, p.comp); + _ = try operand.val.negate(operand.val, operand.ty, p.comp); } else { operand.val = .{}; } @@ -7198,7 +7255,7 @@ fn unExpr(p: *Parser) Error!Result { try operand.usualUnaryConversion(p, tok); if (operand.val.is(.int, p.comp) or operand.val.is(.int, p.comp)) { - if (try operand.val.sub(operand.val, Value.one, operand.ty, p.comp)) + if (try operand.val.decrement(operand.val, operand.ty, p.comp)) try p.errOverflow(tok, operand); } else { operand.val = .{}; diff --git a/src/aro/Tree.zig b/src/aro/Tree.zig index 21640829..a94710a7 100644 --- a/src/aro/Tree.zig +++ b/src/aro/Tree.zig @@ -900,7 +900,7 @@ fn dumpNode( if (tree.value_map.get(node)) |val| { try config.setColor(w, LITERAL); try w.writeAll(" (value: "); - try val.print(ty, tree.comp, w); + try val.print(ty, tree.comp, mapper, w); try w.writeByte(')'); } if (tag == .implicit_return and data.return_zero) { diff --git a/src/aro/Value.zig b/src/aro/Value.zig index 61b8f0f8..3b18068a 100644 --- a/src/aro/Value.zig +++ b/src/aro/Value.zig @@ -4,6 +4,8 @@ const BigIntConst = std.math.big.int.Const; const BigIntMutable = std.math.big.int.Mutable; const backend = @import("backend"); const Interner = backend.Interner; +const StringInterner = backend.StringInterner; +const StringId = StringInterner.StringId; const BigIntSpace = Interner.Tag.Int.BigIntSpace; const Compilation = @import("Compilation.zig"); const Type = @import("Type.zig"); @@ -32,6 +34,10 @@ pub fn int(i: anytype, comp: *Compilation) !Value { } } +pub fn reloc(name: StringId, offset: i64, comp: *Compilation) !Value { + return intern(comp, .{ .global_var_offset = .{ .name = name, .offset = offset } }); +} + pub fn ref(v: Value) Interner.Ref { std.debug.assert(v.opt_ref != .none); return @enumFromInt(@intFromEnum(v.opt_ref)); @@ -407,6 +413,7 @@ pub fn isZero(v: Value, comp: *const Compilation) bool { inline else => |data| return data[0] == 0.0 and data[1] == 0.0, }, .bytes => return false, + .global_var_offset => return false, else => unreachable, } } @@ -503,6 +510,21 @@ fn complexAddSub(lhs: Value, rhs: Value, comptime T: type, op: ComplexOp, comp: pub fn add(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !bool { const bits: usize = @intCast(ty.bitSizeof(comp).?); + if (ty.isPtr()) blk: { + const lhs_key = comp.interner.get(lhs.ref()); + const rhs_key = comp.interner.get(rhs.ref()); + const rel, const delta = if (lhs_key == .global_var_offset) + .{ lhs_key.global_var_offset, rhs.toInt(i64, comp).? } + else if (rhs_key == .global_var_offset) + .{ rhs_key.global_var_offset, lhs.toInt(i64, comp).? } + else + break :blk; + + const elem_size: i64 = @intCast(ty.elemType().sizeof(comp) orelse 1); + const total_offset = rel.offset + elem_size * delta; + res.* = try reloc(rel.name, total_offset, comp); + return false; + } if (ty.isFloat()) { if (ty.isComplex()) { res.* = switch (bits) { @@ -544,8 +566,28 @@ pub fn add(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b } } -pub fn sub(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !bool { +pub fn negate(res: *Value, val: Value, ty: Type, comp: *Compilation) !bool { + return res.sub(zero, val, ty, Type.int, comp); +} + +pub fn decrement(res: *Value, val: Value, ty: Type, comp: *Compilation) !bool { + return res.sub(val, one, ty, Type.int, comp); +} + +pub fn sub(res: *Value, lhs: Value, rhs: Value, ty: Type, rhs_ty: Type, comp: *Compilation) !bool { const bits: usize = @intCast(ty.bitSizeof(comp).?); + if (ty.isPtr()) { + const maybe_rel = comp.interner.get(lhs.ref()); + if (maybe_rel == .global_var_offset) { + const rel = maybe_rel.global_var_offset; + const delta = rhs.toInt(i64, comp).?; + + const elem_size: i64 = @intCast(ty.elemType().sizeof(comp) orelse 1); + const total_offset = rel.offset - elem_size * delta; + res.* = try reloc(rel.name, total_offset, comp); + return false; + } + } if (ty.isFloat()) { if (ty.isComplex()) { res.* = switch (bits) { @@ -569,6 +611,22 @@ pub fn sub(res: *Value, lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !b res.* = try intern(comp, .{ .float = f }); return false; } else { + if (rhs_ty.isPtr()) { + const maybe_lhs_reloc = comp.interner.get(lhs.ref()); + const maybe_rhs_reloc = comp.interner.get(rhs.ref()); + if (maybe_lhs_reloc == .global_var_offset and maybe_rhs_reloc == .global_var_offset) { + const lhs_reloc = maybe_lhs_reloc.global_var_offset; + const rhs_reloc = maybe_rhs_reloc.global_var_offset; + if (lhs_reloc.name != rhs_reloc.name) { + res.* = .{}; + return false; + } + const difference, const overflowed = @subWithOverflow(lhs_reloc.offset, rhs_reloc.offset); + const rhs_size: i64 = @intCast(rhs_ty.elemType().sizeof(comp) orelse 1); + res.* = try int(@divTrunc(difference, rhs_size), comp); + return overflowed != 0; + } + } var lhs_space: BigIntSpace = undefined; var rhs_space: BigIntSpace = undefined; const lhs_bigint = lhs.toBigInt(&lhs_space, comp); @@ -723,7 +781,7 @@ pub fn rem(lhs: Value, rhs: Value, ty: Type, comp: *Compilation) !Value { var tmp: Value = undefined; _ = try tmp.div(lhs, rhs, ty, comp); _ = try tmp.mul(tmp, rhs, ty, comp); - _ = try tmp.sub(lhs, tmp, ty, comp); + _ = try tmp.sub(lhs, tmp, ty, Type.int, comp); return tmp; } } @@ -899,7 +957,7 @@ pub fn complexConj(val: Value, ty: Type, comp: *Compilation) !Value { return intern(comp, .{ .complex = cf }); } -pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *const Compilation) bool { +pub fn compareExtra(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *const Compilation) ?bool { if (op == .eq) { return lhs.opt_ref == rhs.opt_ref; } else if (lhs.opt_ref == rhs.opt_ref) { @@ -919,6 +977,19 @@ pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *cons const imag_equal = std.math.compare(lhs.imag(f128, comp), .eq, rhs.imag(f128, comp)); return !real_equal or !imag_equal; } + if (lhs_key == .global_var_offset and rhs_key == .global_var_offset) { + const lhs_reloc = lhs_key.global_var_offset; + const rhs_reloc = rhs_key.global_var_offset; + switch (op) { + .eq => if (lhs_reloc.name != rhs_reloc.name) return false, + .neq => if (lhs_reloc.name != rhs_reloc.name) return true, + else => if (lhs_reloc.name != rhs_reloc.name) return null, + } + + return std.math.compare(lhs_reloc.offset, op, rhs_reloc.offset); + } else if (lhs_key == .global_var_offset or rhs_key == .global_var_offset) { + return null; + } var lhs_bigint_space: BigIntSpace = undefined; var rhs_bigint_space: BigIntSpace = undefined; @@ -927,6 +998,10 @@ pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *cons return lhs_bigint.order(rhs_bigint).compare(op); } +pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value, comp: *const Compilation) bool { + return lhs.compareExtra(op, rhs, comp).?; +} + fn twosCompIntLimit(limit: std.math.big.int.TwosCompIntLimit, ty: Type, comp: *Compilation) !Value { const signedness = ty.signedness(comp); if (limit == .min and signedness == .unsigned) return Value.zero; @@ -963,7 +1038,7 @@ pub fn maxInt(ty: Type, comp: *Compilation) !Value { return twosCompIntLimit(.max, ty, comp); } -pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w).Error!void { +pub fn print(v: Value, ty: Type, comp: *const Compilation, mapper: StringInterner.TypeMapper, w: anytype) @TypeOf(w).Error!void { if (ty.is(.bool)) { return w.writeAll(if (v.isZero(comp)) "false" else "true"); } @@ -983,6 +1058,16 @@ pub fn print(v: Value, ty: Type, comp: *const Compilation, w: anytype) @TypeOf(w .cf32 => |components| return w.print("{d} + {d}i", .{ @round(@as(f64, @floatCast(components[0])) * 1000000) / 1000000, @round(@as(f64, @floatCast(components[1])) * 1000000) / 1000000 }), inline else => |components| return w.print("{d} + {d}i", .{ @as(f64, @floatCast(components[0])), @as(f64, @floatCast(components[1])) }), }, + .global_var_offset => |rel| { + const name = mapper.lookup(rel.name); + try w.print("&{s}", .{name}); + if (rel.offset == 0) return; + if (rel.offset == std.math.minInt(i64)) { + return w.print(" - {d}", .{std.math.maxInt(i64) + 1}); + } + const sign: u8 = if (rel.offset < 0) '-' else '+'; + return w.print(" {c} {d}", .{ sign, @abs(rel.offset) }); + }, else => unreachable, // not a value } } diff --git a/src/backend/Interner.zig b/src/backend/Interner.zig index 631ec8ee..531f8191 100644 --- a/src/backend/Interner.zig +++ b/src/backend/Interner.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const StringId = @import("StringInterner.zig").StringId; const Allocator = std.mem.Allocator; const assert = std.debug.assert; const BigIntConst = std.math.big.int.Const; @@ -65,6 +66,7 @@ pub const Key = union(enum) { float: Float, complex: Complex, bytes: []const u8, + global_var_offset: GlobalVarOffset, pub const Float = union(enum) { f16: f16, @@ -80,6 +82,11 @@ pub const Key = union(enum) { cf80: [2]f80, cf128: [2]f128, }; + pub const GlobalVarOffset = struct { + name: StringId, + /// Offset in bytes + offset: i64, + }; pub fn hash(key: Key) u32 { var hasher = Hash.init(0); @@ -303,6 +310,8 @@ pub const Tag = enum(u8) { bytes, /// `data` is `Record` record_ty, + /// `data` is GlobalVarOffset + global_var_offset, pub const Array = struct { len0: u32, @@ -537,6 +546,29 @@ pub const Tag = enum(u8) { // trailing // [elements_len]Ref }; + + pub const GlobalVarOffset = struct { + name: u32, + piece0: u32, + piece1: u32, + + pub fn get(self: GlobalVarOffset) Key.GlobalVarOffset { + const offset = @as(u64, self.piece0) | (@as(u64, self.piece1) << 32); + return .{ + .name = @enumFromInt(self.name), + .offset = @bitCast(offset), + }; + } + + fn pack(val: Key.GlobalVarOffset) GlobalVarOffset { + const bits: u64 = @bitCast(val.offset); + return .{ + .name = @intFromEnum(val.name), + .piece0 = @truncate(bits), + .piece1 = @truncate(bits >> 32), + }; + } + }; }; pub const PackedU64 = packed struct(u64) { @@ -716,6 +748,12 @@ pub fn put(i: *Interner, gpa: Allocator, key: Key) !Ref { }); i.extra.appendSliceAssumeCapacity(@ptrCast(elems)); }, + .global_var_offset => |reloc| { + i.items.appendAssumeCapacity(.{ + .tag = .global_var_offset, + .data = try i.addExtra(gpa, Tag.GlobalVarOffset.pack(reloc)), + }); + }, .ptr_ty, .noreturn_ty, .void_ty, @@ -848,6 +886,10 @@ pub fn get(i: *const Interner, ref: Ref) Key { .record_ty = @ptrCast(i.extra.items[extra.end..][0..extra.data.elements_len]), }; }, + .global_var_offset => { + const components = i.extraData(Tag.GlobalVarOffset, data); + return .{ .global_var_offset = components.get() }; + }, }; } diff --git a/test/cases/const decl folding.c b/test/cases/const decl folding.c index d32d1288..694b71ad 100644 --- a/test/cases/const decl folding.c +++ b/test/cases/const decl folding.c @@ -67,14 +67,14 @@ _Static_assert(4.2, ""); "const decl folding.c:27:14: note: previous case defined here" \ "const decl folding.c:34:27: error: '__builtin_choose_expr' requires a constant expression" \ "const decl folding.c:38:15: warning: variable length array folded to constant array as an extension [-Wgnu-folding-constant]" \ - "const decl folding.c:43:16: warning: expression is not an integer constant expression; folding it to a constant is a GNU extension [-Wgnu-folding-constant]" \ "const decl folding.c:43:16: warning: implicit conversion turns string literal into bool: 'char [1]' to '_Bool' [-Wstring-conversion]" \ + "const decl folding.c:43:16: error: static_assert expression is not an integral constant expression" \ "const decl folding.c:44:1: error: static assertion failed \"\"" \ - "const decl folding.c:46:1: error: static assertion failed \"\"" \ - "const decl folding.c:47:16: warning: expression is not an integer constant expression; folding it to a constant is a GNU extension [-Wgnu-folding-constant]" \ - "const decl folding.c:47:16: note: this conversion is not allowed in a constant expression" \ - "const decl folding.c:50:16: warning: expression is not an integer constant expression; folding it to a constant is a GNU extension [-Wgnu-folding-constant]" \ + "const decl folding.c:46:16: error: static_assert expression is not an integral constant expression" \ + "const decl folding.c:47:16: error: static_assert expression is not an integral constant expression" \ "const decl folding.c:50:16: warning: address of array 'arr' will always evaluate to 'true' [-Wpointer-bool-conversion]" \ + "const decl folding.c:50:16: error: static_assert expression is not an integral constant expression" \ "const decl folding.c:51:1: error: static assertion failed \"\"" \ "const decl folding.c:53:16: warning: implicit conversion from 'double' to '_Bool' changes value from 4.2 to true [-Wfloat-conversion]" \ + "const decl folding.c:53:16: error: static_assert expression is not an integral constant expression" \ diff --git a/test/cases/relocations.c b/test/cases/relocations.c new file mode 100644 index 00000000..b726b8b3 --- /dev/null +++ b/test/cases/relocations.c @@ -0,0 +1,44 @@ +//aro-args --target=x86_64-linux -std=c23 + +int arr[20] = {0}; +_Static_assert(&arr[10] - &arr[0] == 10); +_Static_assert(&arr[1] + 3 == &arr[4]); +_Static_assert(&arr[4] - 4 == &arr[0]); + +struct Child { + int x[10]; + char c; +}; + +struct Parent { + int x; + struct Child children[20]; +}; + +struct Parent parents[10]; +_Static_assert(&parents[5].children[10].x[5] - &parents[0].children[0].x[0] == 1220); + +int x; +int y; +_Static_assert(&x != &y); +_Static_assert(&x + 1 == &y + 1); + +_Static_assert(&x + 1 != &x + 2); +_Static_assert(&x == &x); +_Static_assert(&x >= &x); +_Static_assert(&x > &y); +_Static_assert(&x); + +struct __attribute__((packed)) Packed { + int x; + char c; + int y; +}; + +struct Packed packed; +_Static_assert(&packed.x - &packed.y == -1); + +#define EXPECTED_ERRORS "relocations.c:24:1: error: static assertion failed" \ + "relocations.c:29:16: error: static_assert expression is not an integral constant expression" \ + "relocations.c:30:16: error: static_assert expression is not an integral constant expression" \ +