From abdb049d05296b408b217b7c78abe84ed47945d4 Mon Sep 17 00:00:00 2001 From: Sebastian Jeong Date: Mon, 17 Aug 2026 12:52:38 +0900 Subject: [PATCH] =?UTF-8?q?lowering:=20.n=20=EC=9D=80=20=EC=8A=AC=EB=9D=BC?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=EC=97=90=EA=B2=8C=EB=A7=8C=20=EA=B8=B8?= =?UTF-8?q?=EC=9D=B4=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 구조체도 필드를 n 이라 부를 수 있다. lowering 은 이름만 보고 슬라이스 길이 자리(포인터 다음 4바이트)를 읽어서, 그 자리에 있던 그럴듯한 숫자를 돌려주고 있었다. 체커는 제대로 필드로 풀고 있었으니 같은 함수 안에서 쓰기와 읽기가 어긋났다. Box{ room: ^[]mut u8, n: usize, m: usize } n 777 m 999 (전에는 n 12 -- room 의 길이) 배열/슬라이스/str 일 때만 길이로 읽는다. fieldn.fe 가 이것과, 옆의 슬라이스가 여전히 길이로 답하는 것을 함께 고정한다. 222/222, 30/30. --- fec/src/lowerexp.c | 12 +++++++--- fec/tests/exec/fieldn.fe | 49 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 fec/tests/exec/fieldn.fe diff --git a/fec/src/lowerexp.c b/fec/src/lowerexp.c index 40de08c..2b97662 100644 --- a/fec/src/lowerexp.c +++ b/fec/src/lowerexp.c @@ -151,9 +151,15 @@ Slot lower_expr_core(Lower *L, FeNode *n) return slot_place(fe_ir_at_temp(p, 0), it, ir_size(t)); } /* `.n` is how many elements there are, which an array knows at - compile time and a slice carries beside its pointer. */ - if (n->b && n->b->text && !strcmp(n->b->text, "n")) { - FeType *bt = n->a ? n->a->sem_type : 0; + compile time and a slice carries beside its pointer. Only for those: + a struct is free to have a field called `n`, and reading it as a + length would quietly hand back the wrong four bytes. */ + if (n->b && n->b->text && !strcmp(n->b->text, "n") && + n->a && n->a->sem_type && + (n->a->sem_type->kind == FE_TYPE_ARRAY || + n->a->sem_type->kind == FE_TYPE_SLICE || + n->a->sem_type->kind == FE_TYPE_STR)) { + FeType *bt = n->a->sem_type; Slot base; if (bt && bt->kind == FE_TYPE_ARRAY) return slot_value(fe_ir_const(L->m, L->b, FE_IR_I32, diff --git a/fec/tests/exec/fieldn.fe b/fec/tests/exec/fieldn.fe new file mode 100644 index 0000000..cfd148f --- /dev/null +++ b/fec/tests/exec/fieldn.fe @@ -0,0 +1,49 @@ +// EXIT:0 +// OUTPUT:n 777 m 999 +// OUTPUT:len 12 first 7 +// OUTPUT:count 3 +// OUTPUT:balanced +unit fieldn; + +import std.io; +import std.sys; +import std.mem; + +// `.n` on a slice is its length. On a struct it is whatever field is called +// `n` -- and a struct is allowed to call a field that. Reading one as the +// other hands back the four bytes beside the pointer, which is a plausible +// number and so goes unnoticed. + +struct Box { + room: ^[]mut u8, + n: usize, + m: usize, +} + +struct Counter { + n: usize, + + fn bump(self: &mut Self) -> void { self.n = self.n + 1; return; } +} + +fn run() -> !void { + let r: ^[]mut u8 = try mem.alloc_slice(u8, 12); + r.^[0] = 7; + let b: Box = Box{ room: r, n: 777, m: 999 }; + @print("n {} m {}\n", b.n, b.m); + // The slice beside it still answers with its length. + @print("len {} first {}\n", b.room.^.n, b.room.^[0]); + var c: Counter = Counter{ n: 0 }; + c.bump(); + c.bump(); + c.bump(); + @print("count {}\n", c.n); + return; +} + +fn main() -> i32 { + run() catch |e| { @print("failed\n"); return 1; }; + if sys.allocs() == sys.frees() { @print("balanced\n"); } + else { @print("leaked\n"); } + return 0; +}