fix(0117): pointer-to-array indexing auto-derefs
A '*[N]T' receiver in an index expression reached LLVM emission with an unresolved element type and tripped the panic sentinel — no read or write spelling worked. ptrToArrayElem on Lowering recognises the shape; the index READ path GEPs the pointee array through the pointer value and loads the element; the write / compound-assign / lvalue / addr-of-element paths and the expression typer resolve the element type through the same helper (their GEP machinery already handled a pointer base). Kept out of getElementType so slice paths don't half-accept a raw pointer base. Regression: examples/0176 (read, write, compound, element ptr + deref).
This commit is contained in:
21
examples/0176-types-pointer-to-array-index.sx
Normal file
21
examples/0176-types-pointer-to-array-index.sx
Normal file
@@ -0,0 +1,21 @@
|
||||
// Indexing through a pointer-to-array auto-derefs: `p : *[N]T` makes
|
||||
// `p[i]` GEP the pointee array and load the element, and `p[i] = v` /
|
||||
// `p[i] += v` store through it — mirroring the struct-pointer auto-deref
|
||||
// on field access. Writes through the pointer are visible in the
|
||||
// original array and vice versa.
|
||||
//
|
||||
// Regression (issue 0117): this shape used to reach LLVM emission with
|
||||
// an unresolved element type and panic.
|
||||
|
||||
#import "modules/std.sx";
|
||||
|
||||
main :: () {
|
||||
k : [4]s64 = .[11, 22, 33, 44];
|
||||
p := @k;
|
||||
print("read={}\n", p[2]);
|
||||
p[1] = 99;
|
||||
p[3] += 1;
|
||||
print("write={} {} {}\n", k[1], p[1], k[3]);
|
||||
e := @p[2];
|
||||
print("elem-ptr={}\n", e.*);
|
||||
}
|
||||
1
examples/expected/0176-types-pointer-to-array-index.exit
Normal file
1
examples/expected/0176-types-pointer-to-array-index.exit
Normal file
@@ -0,0 +1 @@
|
||||
0
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
read=33
|
||||
write=99 99 45
|
||||
elem-ptr=33
|
||||
@@ -1,5 +1,15 @@
|
||||
# 0117 — indexing through a pointer-to-array panics at LLVM emission
|
||||
|
||||
> **RESOLVED** (2026-06-11). `ptrToArrayElem` on Lowering recognises the
|
||||
> `*[N]T` receiver; the index READ path GEPs the pointee array through the
|
||||
> pointer value and loads the element; the WRITE / compound-assign /
|
||||
> lvalue / addr-of-element paths and the expression typer resolve the
|
||||
> element type through the same helper (their GEP machinery already
|
||||
> handled a pointer base). Postfix deref (`p.*`) was always the deref
|
||||
> spelling — the issue's `(*p)[2]` note was a wrong-syntax red herring.
|
||||
> Regression test: examples/0176-types-pointer-to-array-index.sx
|
||||
> (read, write, compound, element pointer + deref).
|
||||
|
||||
**Symptom.** Indexing through a `*[N]T` pointer is neither lowered nor
|
||||
diagnosed: the index expression reaches the LLVM emitter with the
|
||||
`.unresolved` type sentinel and trips the panic tripwire.
|
||||
|
||||
@@ -364,6 +364,7 @@ pub const ExprTyper = struct {
|
||||
return self.l.inferExprType(arg_node);
|
||||
}
|
||||
const obj_ty = self.l.inferExprType(ie.object);
|
||||
if (self.l.ptrToArrayElem(obj_ty)) |elem| return elem;
|
||||
return self.l.getElementType(obj_ty);
|
||||
},
|
||||
.slice_expr => |se| {
|
||||
|
||||
@@ -1106,6 +1106,20 @@ pub const Lowering = struct {
|
||||
};
|
||||
}
|
||||
|
||||
/// The element type when `ty` is a POINTER TO AN ARRAY (`*[N]T` → T),
|
||||
/// else null. Indexing auto-derefs this shape (GEP the pointee array
|
||||
/// through the pointer value); kept OUT of `getElementType` so the
|
||||
/// slice/subslice paths don't half-accept a raw pointer base.
|
||||
pub fn ptrToArrayElem(self: *Lowering, ty: TypeId) ?TypeId {
|
||||
if (ty.isBuiltin()) return null;
|
||||
const info = self.module.types.get(ty);
|
||||
if (info != .pointer) return null;
|
||||
const pointee = info.pointer.pointee;
|
||||
if (pointee.isBuiltin()) return null;
|
||||
const pi = self.module.types.get(pointee);
|
||||
return if (pi == .array) pi.array.element else null;
|
||||
}
|
||||
|
||||
pub fn isFloat(ty: TypeId) bool {
|
||||
return ty == .f32 or ty == .f64;
|
||||
}
|
||||
|
||||
@@ -1263,6 +1263,12 @@ pub fn lowerIndexExpr(self: *Lowering, ie: *const ast.IndexExpr) Ref {
|
||||
const idx = self.lowerExpr(ie.index);
|
||||
// Infer element type from the object's slice/array type
|
||||
const obj_ty = self.inferExprType(ie.object);
|
||||
// `*[N]T` receiver auto-derefs (issue 0117): `obj` IS the pointer
|
||||
// value — GEP the pointee array and load the element.
|
||||
if (self.ptrToArrayElem(obj_ty)) |elem| {
|
||||
const gep = self.builder.emit(.{ .index_gep = .{ .lhs = obj, .rhs = idx } }, self.module.types.ptrTo(elem));
|
||||
return self.builder.load(gep, elem);
|
||||
}
|
||||
const elem_ty = self.getElementType(obj_ty);
|
||||
return self.builder.emit(.{ .index_get = .{ .lhs = obj, .rhs = idx } }, elem_ty);
|
||||
}
|
||||
@@ -1843,7 +1849,7 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
const ie = &uop.operand.data.index_expr;
|
||||
const idx = self.lowerExpr(ie.index);
|
||||
const obj_ty = self.inferExprType(ie.object);
|
||||
const elem_ty = self.getElementType(obj_ty);
|
||||
const elem_ty = self.ptrToArrayElem(obj_ty) orelse self.getElementType(obj_ty);
|
||||
const ptr_ty = self.module.types.ptrTo(elem_ty);
|
||||
// For array targets, use the storage pointer (alloca for a
|
||||
// local, global_addr for a module global) so the resulting
|
||||
|
||||
@@ -564,7 +564,8 @@ pub fn lowerAssignment(self: *Lowering, asgn: *const ast.Assignment) void {
|
||||
}
|
||||
} else if (asgn.target.data == .index_expr) {
|
||||
// For array[i] = val, set target_type to the element type
|
||||
const elem_ty = self.getElementType(self.inferExprType(asgn.target.data.index_expr.object));
|
||||
const tgt_obj_ty = self.inferExprType(asgn.target.data.index_expr.object);
|
||||
const elem_ty = self.ptrToArrayElem(tgt_obj_ty) orelse self.getElementType(tgt_obj_ty);
|
||||
if (elem_ty != .void) self.target_type = elem_ty;
|
||||
} else if (asgn.target.data == .field_access) {
|
||||
// For obj.field = val, set target_type to the field's type so RHS
|
||||
@@ -722,7 +723,7 @@ pub fn lowerAssignment(self: *Lowering, asgn: *const ast.Assignment) void {
|
||||
.index_expr => |ie| {
|
||||
const idx = self.lowerExpr(ie.index);
|
||||
const obj_ty = self.inferExprType(ie.object);
|
||||
const elem_ty = self.getElementType(obj_ty);
|
||||
const elem_ty = self.ptrToArrayElem(obj_ty) orelse self.getElementType(obj_ty);
|
||||
const ptr_ty = self.module.types.ptrTo(elem_ty);
|
||||
// For fixed-size array assignment targets, use the alloca pointer directly
|
||||
// so that the store modifies the original variable (not a loaded copy).
|
||||
@@ -939,7 +940,7 @@ pub fn lowerExprAsPtr(self: *Lowering, node: *const Node) Ref {
|
||||
.index_expr => |ie| {
|
||||
const idx = self.lowerExpr(ie.index);
|
||||
const obj_ty = self.inferExprType(ie.object);
|
||||
const elem_ty = self.getElementType(obj_ty);
|
||||
const elem_ty = self.ptrToArrayElem(obj_ty) orelse self.getElementType(obj_ty);
|
||||
const ptr_ty = self.module.types.ptrTo(elem_ty);
|
||||
// For fixed-size arrays, use the alloca so GEP addresses the original memory
|
||||
const is_array = !obj_ty.isBuiltin() and self.module.types.get(obj_ty) == .array;
|
||||
@@ -1153,7 +1154,7 @@ pub fn lowerMultiAssign(self: *Lowering, ma: *const ast.MultiAssign) void {
|
||||
.index_expr => |ie| {
|
||||
const idx = self.lowerExpr(ie.index);
|
||||
const obj_ty = self.inferExprType(ie.object);
|
||||
const elem_ty = self.getElementType(obj_ty);
|
||||
const elem_ty = self.ptrToArrayElem(obj_ty) orelse self.getElementType(obj_ty);
|
||||
const ptr_ty = self.module.types.ptrTo(elem_ty);
|
||||
const val_ty = self.builder.getRefType(val);
|
||||
const store_val = if (val_ty != elem_ty and val_ty != .void and elem_ty != .void)
|
||||
|
||||
Reference in New Issue
Block a user