From 559efec8a20eb773fa5a7d1f9ed76b7c68ea2dc9 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Thu, 8 Oct 2026 15:44:19 +0200 Subject: [PATCH] kernel: check mutability where a tnum implied it Two places test for an exact tnum and rely on it implying that the object is mutable: the interpreter and compiled code assign directly to a list with tnum T_PLIST, and MigrateObjects in HPC-GAP sorts the components of a record with tnum T_PREC. Test the mutability as well, so that this keeps working once mutability is no longer part of the tnum (see #6025). Assisted-by: Claude Code (Opus 5.5) --- src/compiled.h | 6 ++++-- src/hpc/threadapi.c | 2 +- src/vars.c | 2 +- tst/testinstall/listindex.tst | 13 ++++++++++++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/compiled.h b/src/compiled.h index ef5c8cb214..2f157ac56f 100644 --- a/src/compiled.h +++ b/src/compiled.h @@ -124,7 +124,8 @@ static inline void CHECK_FUNC(Obj obj) else ASSB_LIST(list, p, rhs); #define C_ASS_LIST_FPL(list,p,rhs) \ - if ( IS_POS_INTOBJ(p) && TNUM_OBJ(list) == T_PLIST ) { \ + if ( IS_POS_INTOBJ(p) && TNUM_OBJ(list) == T_PLIST && \ + IS_MUTABLE_OBJ(list) ) { \ if ( LEN_PLIST(list) < INT_INTOBJ(p) ) { \ GROW_PLIST( list, (UInt)INT_INTOBJ(p) ); \ SET_LEN_PLIST( list, INT_INTOBJ(p) ); \ @@ -136,7 +137,8 @@ static inline void CHECK_FUNC(Obj obj) } #define C_ASS_LIST_FPL_INTOBJ(list,p,rhs) \ - if ( IS_POS_INTOBJ(p) && TNUM_OBJ(list) == T_PLIST) { \ + if ( IS_POS_INTOBJ(p) && TNUM_OBJ(list) == T_PLIST && \ + IS_MUTABLE_OBJ(list) ) { \ if ( LEN_PLIST(list) < INT_INTOBJ(p) ) { \ GROW_PLIST( list, (UInt)INT_INTOBJ(p) ); \ SET_LEN_PLIST( list, INT_INTOBJ(p) ); \ diff --git a/src/hpc/threadapi.c b/src/hpc/threadapi.c index 4abb6f6a5e..a5010699eb 100644 --- a/src/hpc/threadapi.c +++ b/src/hpc/threadapi.c @@ -2023,7 +2023,7 @@ MigrateObjects(int count, Obj * objects, Region * target, int retype) // they need to be sorted, as sorting upon access may prove impossible. for (i = 0; i < count; i++) { Obj obj = objects[i]; - if (TNUM_OBJ(obj) == T_PREC) { + if (TNUM_OBJ(obj) == T_PREC && IS_MUTABLE_OBJ(obj)) { SortPRecRNam(obj); } SET_REGION(obj, target); diff --git a/src/vars.c b/src/vars.c index 335492c62e..7de1530d18 100644 --- a/src/vars.c +++ b/src/vars.c @@ -494,7 +494,7 @@ static ExecStatus ExecAssList(Expr stat) p = INT_INTOBJ(pos); // special case for plain list - if ( TNUM_OBJ(list) == T_PLIST ) { + if ( TNUM_OBJ(list) == T_PLIST && IS_MUTABLE_OBJ(list) ) { if ( LEN_PLIST(list) < p ) { GROW_PLIST( list, p ); SET_LEN_PLIST( list, p ); diff --git a/tst/testinstall/listindex.tst b/tst/testinstall/listindex.tst index 0c4571b94a..d28ccf057a 100644 --- a/tst/testinstall/listindex.tst +++ b/tst/testinstall/listindex.tst @@ -5,7 +5,7 @@ ## ## ## -#@local foo,l,o,r,res,s,t,x +#@local f,foo,l,o,r,res,s,t,x gap> START_TEST("listindex.tst"); # custom list implementation, pretending to have infinite length @@ -408,5 +408,16 @@ function ( x ) return 1; end +# assignment inside a function to an immutable list that the kernel has not +# classified beyond "plain list" +gap> l := [ 1, "a", 3 ];; l[5] := 1;; Unbind(l[5]);; MakeImmutable(l);; +gap> f := function(l, i) l[i] := 0; end;; +gap> f(l, 1); +Error, List Assignment: must be a mutable list +gap> f(l, 5); +Error, List Assignment: must be a mutable list +gap> l; +[ 1, "a", 3 ] + # that's all, folks gap> STOP_TEST("listindex.tst");