-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Open
Description
Description
Following code
proc main() =
var trimMap = [@[1], @[2]]
var z = 0
for j in 0 ..< 2:
var i = trimMap.high
while i >= 0:
#let len {.volatile} = trimMap[i].len # this way it "works"
let len = trimMap[i].len
if len > 0:
z = trimMap[i].pop()
echo "set"
break
dec i
echo "popped ", i
doAssert z != 0
echo "z", z
main()
prints:
set
popped 1
set
popped 1
fatal.nim(53) sysFatal
Error: unhandled exception: z.nim(15, 5) `z != 0` [AssertionDefect]
on both devel and 2.0.14, when compiled like:
nim c --mm:arc -d:danger -d:lto --cc:clang z.nim
with clang version 18.1.8. (-d:release instead of -d:danger has the same effect.)
The crash disappears if I do any of:
- set len to volatile
- --passc:-fno-strict-aliasing
- disable LTO
- --mm:refc
It also disappears if I switch to GCC, but this is probably by accident.
The original program that I reduced this from crashes under GCC as well.
len is supposed to be set in shrink:
/* z.nim.c */
struct tySequence__qwqHTkRvwhrRyENtudHQ7g {
NI len;
tySequence__qwqHTkRvwhrRyENtudHQ7g_Content* p;
};
struct tySequence__qwqHTkRvwhrRyENtudHQ7g_Content {
NI cap;
NI data[SEQ_DECL_SIZE];
};
static N_INLINE(NI, pop__z_u27)(tySequence__qwqHTkRvwhrRyENtudHQ7g* s_p0) {
NI result;
NI L;
NI T1_;
T1_ = (*s_p0).len;
L = ((NI) (T1_ - ((NI) 1)));
result = ((*s_p0)).p->data[L];
((*s_p0)).p->data[L] = 0;
shrink__z_u42(s_p0, (L));
return result;
}
/* system.nim.c */
typedef struct tyObject_NimSeqPayload__g7Ny9bTlNzn6dsxoJOHv6LQ tyObject_NimSeqPayload__g7Ny9bTlNzn6dsxoJOHv6LQ;
/* ^ incomplete struct; never gets defined anywhere */
struct tyObject_NimSeqV2__PEQQ33xihBDluJkSxo81LQ {
NI len;
tyObject_NimSeqPayload__g7Ny9bTlNzn6dsxoJOHv6LQ* p;
};
N_LIB_PRIVATE N_NIMCALL(void, shrink__z_u42)(tySequence__qwqHTkRvwhrRyENtudHQ7g* x_p0, NI newLen_p1) {
(*((tyObject_NimSeqV2__PEQQ33xihBDluJkSxo81LQ*) (x_p0))).len = newLen_p1;
}
Perhaps the cast violates strict aliasing?
Nim Version
Nim Compiler Version 2.3.1 [Linux: amd64]
Compiled at 2025-01-01
Copyright (c) 2006-2025 by Andreas Rumpf
git hash: 3dda60a8ce32cb7d5e3e99111399a1550c145176
active boot switches: -d:release
Current Output
No response
Expected Output
set
popped 1
set
popped 0
z1
Known Workarounds
No response
Additional Information
ETA: this seems to fix it
diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim
index 572e77408..3817b6bc5 100644
--- a/lib/system/seqs_v2.nim
+++ b/lib/system/seqs_v2.nim
@@ -133,7 +133,7 @@ proc shrink*[T](x: var seq[T]; newLen: Natural) {.tags: [], raises: [].} =
reset x[i]
# XXX This is wrong for const seqs that were moved into 'x'!
{.noSideEffect.}:
- cast[ptr NimSeqV2[T]](addr x).len = newLen
+ cast[ptr int](addr x)[] = newLen
proc grow*[T](x: var seq[T]; newLen: Natural; value: T) {.nodestroy.} =
let oldLen = x.len
C allows aliasing with the first member, so it makes sense.
But if it's a strict aliasing problem, then it's not an adequate fix, because the cast is also performed in other functions.