diff --git a/lib/attr.gd b/lib/attr.gd index 155d6a643f..2b347bc6e5 100644 --- a/lib/attr.gd +++ b/lib/attr.gd @@ -36,10 +36,14 @@ ## ## ## -## This info class (together with is used -## for messages about attribute storing being disabled (at level 2) or -## enabled (level 3). It may be used in the future for other messages -## concerning changes to attribute behaviour. +## This info class (together with ) is used +## for messages about attributes. Messages are shown under the following circumstances: +## +## is used (level 2). +## is used (level 3). +## When trying to assign to non-mutable attribute which already is set to a different value (level 3). +## When the test filter for an attribute (i.e., HasFOO) is set, but no value is assigned (level 3). +## ## ## ## <#/GAPDoc> diff --git a/lib/attr.gi b/lib/attr.gi index b7cb9edcc5..79f04e386c 100644 --- a/lib/attr.gi +++ b/lib/attr.gi @@ -37,3 +37,13 @@ InstallGlobalFunction(DisableAttributeValueStoring, function( attr ) Info(InfoAttributes + InfoWarning, 2, "Disabling value storing for ",NAME_FUNC(attr)); SET_ATTRIBUTE_STORING( attr, false); end); + +CHECK_REPEATED_ATTRIBUTE_SET := function(obj, name, val) + if InfoLevel(InfoAttributes) >= 3 then + if not IsBound(obj!.(name)) then + Info(InfoAttributes, 3, "Attribute ", name, " of ", obj, " is marked as assigned, but it has no value"); + elif obj!.(name) <> val then + Info(InfoAttributes, 3, "Attribute ", name, " of ", obj, " already set to ", obj!.(name), ", cannot be changed to ", val); + fi; + fi; +end; diff --git a/lib/coll.gi b/lib/coll.gi index 3af9cc4f18..5ee18a3c63 100644 --- a/lib/coll.gi +++ b/lib/coll.gi @@ -3105,13 +3105,7 @@ InstallOtherMethod(SetSize,true,[IsObject and IsAttributeStoringRep,IsObject], function(obj,sz) local filt; if HasSize(obj) and Size(obj)<>sz then - if AssertionLevel()>2 then - # Make this an ordinary error (not ErrorNoReturn as suggested) to - # preserve all debugging options -- even use `return` to investigate - # what would have happened before this methods was introduced. - Error("size of ",obj," already set to ",Size(obj), - ", cannot be changed to ",sz); - fi; + CHECK_REPEATED_ATTRIBUTE_SET(obj, "Size", sz); return; fi; if sz=0 then filt:=IsEmpty; diff --git a/lib/module.gi b/lib/module.gi index 6969b7cfd9..46df47ba76 100644 --- a/lib/module.gi +++ b/lib/module.gi @@ -415,7 +415,7 @@ InstallOtherMethod(SetDimension,true,[IsObject and IsAttributeStoringRep,IsObjec function(obj,dim) local filt; if HasDimension(obj) and IsBound(obj!.Dimension) then - Assert(2, Dimension(obj) = dim); + CHECK_REPEATED_ATTRIBUTE_SET(obj, "Dimension", dim); return; fi; if dim=0 then diff --git a/lib/oper1.g b/lib/oper1.g index da7afe5f83..ff000facb3 100644 --- a/lib/oper1.g +++ b/lib/oper1.g @@ -1090,3 +1090,7 @@ InstallMethod( ViewObj, [ IS_OBJECT ], 0, PRINT_OBJ ); + +# A dummy version of this function is installed here, the full version +# is defined in attr.gi, after Info-related functionality is set up. +CHECK_REPEATED_ATTRIBUTE_SET := function(obj, name, val) end; diff --git a/src/c_oper1.c b/src/c_oper1.c index c91e9a2eb5..e7a8dca154 100644 --- a/src/c_oper1.c +++ b/src/c_oper1.c @@ -1,7 +1,7 @@ #ifndef AVOID_PRECOMPILED /* C file produced by GAC */ #include "compiled.h" -#define FILE_CRC "-77878372" +#define FILE_CRC "-76837766" /* global variables used in handlers */ static GVar G_REREADING; @@ -24,6 +24,7 @@ static GVar G_SET__NAME__FUNC; static Obj GF_SET__NAME__FUNC; static GVar G_NARG__FUNC; static Obj GF_NARG__FUNC; +static GVar G_CHECK__REPEATED__ATTRIBUTE__SET; static GVar G_IS__OPERATION; static Obj GF_IS__OPERATION; static GVar G_AINV; @@ -181,7 +182,7 @@ static RNam R_CommandLineOptions; static RNam R_N; /* information for the functions */ -static Obj NameFunc[19]; +static Obj NameFunc[20]; static Obj FileName; /* handler for function 2 */ @@ -5184,6 +5185,27 @@ static Obj HdlrFunc17 ( return 0; } +/* handler for function 19 */ +static Obj HdlrFunc19 ( + Obj self, + Obj a_obj, + Obj a_name, + Obj a_val ) +{ + Bag oldFrame; + + /* allocate new stack frame */ + SWITCH_TO_NEW_FRAME(self,0,0,oldFrame); + + /* return; */ + SWITCH_TO_OLD_FRAME(oldFrame); + return 0; + + /* return; */ + SWITCH_TO_OLD_FRAME(oldFrame); + return 0; +} + /* handler for function 1 */ static Obj HdlrFunc1 ( Obj self ) @@ -5907,6 +5929,18 @@ static Obj HdlrFunc1 ( DoOperation2Args( CallFuncListOper, t_1, NewPlistFromArgs( t_2, t_3, t_4, t_5, INTOBJ_INT(0), t_6 ) ); } + /* CHECK_REPEATED_ATTRIBUTE_SET := function ( obj, name, val ) + return; + end; */ + t_1 = NewFunction( NameFunc[19], 3, ArgStringToList("obj,name,val"), HdlrFunc19 ); + SET_ENVI_FUNC( t_1, STATE(CurrLVars) ); + t_2 = NewFunctionBody(); + SET_STARTLINE_BODY(t_2, 1096); + SET_ENDLINE_BODY(t_2, 1096); + SET_FILENAME_BODY(t_2, FileName); + SET_BODY_FUNC(t_1, t_2); + AssGVar( G_CHECK__REPEATED__ATTRIBUTE__SET, t_1 ); + /* return; */ SWITCH_TO_OLD_FRAME(oldFrame); return 0; @@ -5931,6 +5965,7 @@ static Int PostRestore ( StructInitInfo * module ) G_NAME__FUNC = GVarName( "NAME_FUNC" ); G_SET__NAME__FUNC = GVarName( "SET_NAME_FUNC" ); G_NARG__FUNC = GVarName( "NARG_FUNC" ); + G_CHECK__REPEATED__ATTRIBUTE__SET = GVarName( "CHECK_REPEATED_ATTRIBUTE_SET" ); G_IS__OPERATION = GVarName( "IS_OPERATION" ); G_AINV = GVarName( "AINV" ); G_IS__INT = GVarName( "IS_INT" ); @@ -6031,6 +6066,7 @@ static Int PostRestore ( StructInitInfo * module ) NameFunc[16] = 0; NameFunc[17] = 0; NameFunc[18] = 0; + NameFunc[19] = 0; /* return success */ return 0; @@ -6167,6 +6203,8 @@ static Int InitKernel ( StructInitInfo * module ) InitGlobalBag( &(NameFunc[17]), "GAPROOT/lib/oper1.g:NameFunc[17]("FILE_CRC")" ); InitHandlerFunc( HdlrFunc18, "GAPROOT/lib/oper1.g:HdlrFunc18("FILE_CRC")" ); InitGlobalBag( &(NameFunc[18]), "GAPROOT/lib/oper1.g:NameFunc[18]("FILE_CRC")" ); + InitHandlerFunc( HdlrFunc19, "GAPROOT/lib/oper1.g:HdlrFunc19("FILE_CRC")" ); + InitGlobalBag( &(NameFunc[19]), "GAPROOT/lib/oper1.g:NameFunc[19]("FILE_CRC")" ); /* return success */ return 0; @@ -6201,7 +6239,7 @@ static Int InitLibrary ( StructInitInfo * module ) static StructInitInfo module = { .type = MODULE_STATIC, .name = "GAPROOT/lib/oper1.g", - .crc = -77878372, + .crc = -76837766, .initKernel = InitKernel, .initLibrary = InitLibrary, .postRestore = PostRestore, diff --git a/src/hpc/c_oper1.c b/src/hpc/c_oper1.c index cff23df95f..15ee00a4c8 100644 --- a/src/hpc/c_oper1.c +++ b/src/hpc/c_oper1.c @@ -1,7 +1,7 @@ #ifndef AVOID_PRECOMPILED /* C file produced by GAC */ #include "compiled.h" -#define FILE_CRC "-77878372" +#define FILE_CRC "-76837766" /* global variables used in handlers */ static GVar G_REREADING; @@ -24,6 +24,7 @@ static GVar G_SET__NAME__FUNC; static Obj GF_SET__NAME__FUNC; static GVar G_NARG__FUNC; static Obj GF_NARG__FUNC; +static GVar G_CHECK__REPEATED__ATTRIBUTE__SET; static GVar G_IS__OPERATION; static Obj GF_IS__OPERATION; static GVar G_AINV; @@ -197,7 +198,7 @@ static RNam R_CommandLineOptions; static RNam R_N; /* information for the functions */ -static Obj NameFunc[19]; +static Obj NameFunc[20]; static Obj FileName; /* handler for function 2 */ @@ -5309,6 +5310,27 @@ static Obj HdlrFunc17 ( return 0; } +/* handler for function 19 */ +static Obj HdlrFunc19 ( + Obj self, + Obj a_obj, + Obj a_name, + Obj a_val ) +{ + Bag oldFrame; + + /* allocate new stack frame */ + SWITCH_TO_NEW_FRAME(self,0,0,oldFrame); + + /* return; */ + SWITCH_TO_OLD_FRAME(oldFrame); + return 0; + + /* return; */ + SWITCH_TO_OLD_FRAME(oldFrame); + return 0; +} + /* handler for function 1 */ static Obj HdlrFunc1 ( Obj self ) @@ -6052,6 +6074,18 @@ static Obj HdlrFunc1 ( DoOperation2Args( CallFuncListOper, t_1, NewPlistFromArgs( t_2, t_3, t_4, t_5, INTOBJ_INT(0), t_6 ) ); } + /* CHECK_REPEATED_ATTRIBUTE_SET := function ( obj, name, val ) + return; + end; */ + t_1 = NewFunction( NameFunc[19], 3, ArgStringToList("obj,name,val"), HdlrFunc19 ); + SET_ENVI_FUNC( t_1, STATE(CurrLVars) ); + t_2 = NewFunctionBody(); + SET_STARTLINE_BODY(t_2, 1096); + SET_ENDLINE_BODY(t_2, 1096); + SET_FILENAME_BODY(t_2, FileName); + SET_BODY_FUNC(t_1, t_2); + AssGVar( G_CHECK__REPEATED__ATTRIBUTE__SET, t_1 ); + /* return; */ SWITCH_TO_OLD_FRAME(oldFrame); return 0; @@ -6076,6 +6110,7 @@ static Int PostRestore ( StructInitInfo * module ) G_NAME__FUNC = GVarName( "NAME_FUNC" ); G_SET__NAME__FUNC = GVarName( "SET_NAME_FUNC" ); G_NARG__FUNC = GVarName( "NARG_FUNC" ); + G_CHECK__REPEATED__ATTRIBUTE__SET = GVarName( "CHECK_REPEATED_ATTRIBUTE_SET" ); G_IS__OPERATION = GVarName( "IS_OPERATION" ); G_AINV = GVarName( "AINV" ); G_IS__INT = GVarName( "IS_INT" ); @@ -6184,6 +6219,7 @@ static Int PostRestore ( StructInitInfo * module ) NameFunc[16] = 0; NameFunc[17] = 0; NameFunc[18] = 0; + NameFunc[19] = 0; /* return success */ return 0; @@ -6328,6 +6364,8 @@ static Int InitKernel ( StructInitInfo * module ) InitGlobalBag( &(NameFunc[17]), "GAPROOT/lib/oper1.g:NameFunc[17]("FILE_CRC")" ); InitHandlerFunc( HdlrFunc18, "GAPROOT/lib/oper1.g:HdlrFunc18("FILE_CRC")" ); InitGlobalBag( &(NameFunc[18]), "GAPROOT/lib/oper1.g:NameFunc[18]("FILE_CRC")" ); + InitHandlerFunc( HdlrFunc19, "GAPROOT/lib/oper1.g:HdlrFunc19("FILE_CRC")" ); + InitGlobalBag( &(NameFunc[19]), "GAPROOT/lib/oper1.g:NameFunc[19]("FILE_CRC")" ); /* return success */ return 0; @@ -6362,7 +6400,7 @@ static Int InitLibrary ( StructInitInfo * module ) static StructInitInfo module = { .type = MODULE_STATIC, .name = "GAPROOT/lib/oper1.g", - .crc = -77878372, + .crc = -76837766, .initKernel = InitKernel, .initLibrary = InitLibrary, .postRestore = PostRestore, diff --git a/src/opers.c b/src/opers.c index f5d4fb94ca..729295462f 100644 --- a/src/opers.c +++ b/src/opers.c @@ -1385,6 +1385,7 @@ static UInt RNamIsVerbose; static UInt RNamIsConstructor; static UInt RNamPrecedence; static Obj HANDLE_METHOD_NOT_FOUND; +static Obj CHECK_REPEATED_ATTRIBUTE_SET; static void HandleMethodNotFound(Obj oper, Int nargs, @@ -3283,12 +3284,15 @@ static Obj DoSetterFunction(Obj self, Obj obj, Obj value) flag2 = INT_INTOBJ( FLAG2_FILT(tester) ); type = TYPE_OBJ_FEO(obj); flags = FLAGS_TYPE(type); + + UInt rnam = (UInt)INT_INTOBJ(ELM_PLIST(tmp, 1)); + if ( SAFE_C_ELM_FLAGS(flags,flag2) ) { + CALL_3ARGS(CHECK_REPEATED_ATTRIBUTE_SET, obj, NAME_RNAM(rnam), value); return 0; } /* set the value */ - UInt rnam = (UInt)INT_INTOBJ(ELM_PLIST(tmp,1)); #ifdef HPCGAP if (atomic) SetARecordField( obj, rnam, CopyObj(value,0) ); @@ -3772,6 +3776,9 @@ static Int InitKernel ( ImportFuncFromLibrary("HANDLE_METHOD_NOT_FOUND", &HANDLE_METHOD_NOT_FOUND); + ImportFuncFromLibrary("CHECK_REPEATED_ATTRIBUTE_SET", + &CHECK_REPEATED_ATTRIBUTE_SET); + #ifdef GASMAN ImportGVarFromLibrary( "IsType", &IsType ); ImportFuncFromLibrary( "FLUSH_ALL_METHOD_CACHES", &FLUSH_ALL_METHOD_CACHES ); diff --git a/tst/testinstall/attribute.tst b/tst/testinstall/attribute.tst index bc88fd2dcf..7795b56877 100644 --- a/tst/testinstall/attribute.tst +++ b/tst/testinstall/attribute.tst @@ -1,4 +1,6 @@ gap> START_TEST("attribute.tst"); +gap> attributeinfo := InfoLevel(InfoAttributes);; +gap> SetInfoLevel(InfoAttributes, 3); gap> DeclareAttribute(); Error, Function: number of arguments must be at least 2 (not 0) gap> DeclareAttribute("banana"); @@ -60,6 +62,19 @@ gap> HasSize(foo); true gap> Size(foo); 17 +gap> SetSize(foo, 16); +#I Attribute Size of already set to 17, cannot be changed to 16 +gap> Size(foo); +17 +gap> HasDimension(foo); +false +gap> SetDimension(foo, 3); +gap> HasDimension(foo); +true +gap> SetDimension(foo, 4); +#I Attribute Dimension of already set to 3, cannot be changed to 4 +gap> Dimension(foo); +3 gap> InstallMethod(FavouriteFruit, [HasSize], x-> "pear"); gap> FavouriteFruit(foo); "pear" @@ -70,5 +85,22 @@ gap> FavouriteFruit(foo); "pear" gap> HasFavouriteFruit(foo); true +gap> SetFavouriteFruit(foo, "apple"); +#I Attribute FavouriteFruit of already set to pear, cannot be changed to apple +gap> FavouriteFruit(foo); +"pear" +gap> Unbind(foo!.FavouriteFruit); +gap> SetFavouriteFruit(foo, "apple"); +#I Attribute FavouriteFruit of is marked as assigned, but it has no value #@fi + +# Check a mutable attribute +gap> grp := Group(());; +gap> SetStabChainMutable(grp, [1,2]); +gap> StabChainMutable(grp); +[ 1, 2 ] +gap> SetStabChainMutable(grp, [3,4]); +gap> StabChainMutable(grp); +[ 3, 4 ] +gap> SetInfoLevel(InfoAttributes, attributeinfo); gap> STOP_TEST("attribute.tst", 1);