diff --git a/lib/attr.gd b/lib/attr.gd index bfbf6bec1c3..9d0d14d725f 100644 --- a/lib/attr.gd +++ b/lib/attr.gd @@ -37,8 +37,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 +## for messages about attributes. The current warnings are: +## +## Attribute storing being disabled (level 2) +## Atribute storing being enabled (level 3) +## Trying to assign a non-mutable attribute a different value (level 3) +## An attribute which is marked as assigned, but has no value (level 3) +## +## It may be used in the future for other messages ## concerning changes to attribute behaviour. ## ## diff --git a/lib/attr.gi b/lib/attr.gi index 4d17d8d24be..2ee6f815f78 100644 --- a/lib/attr.gi +++ b/lib/attr.gi @@ -37,3 +37,11 @@ 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 not IsBound(obj!.(name)) then + Info(InfoAttributes + InfoWarning, 3, "Attribute ", name, " of ", obj, " is marked as assigned, but it has no value"); + elif obj!.(name) <> val then + Info(InfoAttributes + InfoWarning, 3, name, " of ", obj, " already set to ", obj!.(name), ", cannot be changed to ", val); + fi; +end; diff --git a/lib/coll.gi b/lib/coll.gi index 54435d672aa..730417c412c 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/oper1.g b/lib/oper1.g index 61c2e9e822e..015f02b3863 100644 --- a/lib/oper1.g +++ b/lib/oper1.g @@ -1090,3 +1090,7 @@ InstallMethod( ViewObj, [ IS_OBJECT ], 0, PRINT_OBJ ); + +# An dummy version of this function is installed here, the full version +# is installed in attr.gd, 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 bcfb9d4b410..bfe4db925c7 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 "50470712" +#define FILE_CRC "19934316" /* 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 = 50470712, + .crc = 19934316, .initKernel = InitKernel, .initLibrary = InitLibrary, .postRestore = PostRestore, diff --git a/src/hpc/c_oper1.c b/src/hpc/c_oper1.c index f98590b9b62..3a3eb51c7bd 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 "50470712" +#define FILE_CRC "19934316" /* 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 = 50470712, + .crc = 19934316, .initKernel = InitKernel, .initLibrary = InitLibrary, .postRestore = PostRestore, diff --git a/src/opers.c b/src/opers.c index f5d4fb94caf..fabea7dcefc 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 bc88fd2dcf7..14b57b6bf56 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,10 @@ gap> HasSize(foo); true gap> Size(foo); 17 +gap> SetSize(foo, 16); +#I Size of already set to 17, cannot be changed to 16 +gap> Size(foo); +17 gap> InstallMethod(FavouriteFruit, [HasSize], x-> "pear"); gap> FavouriteFruit(foo); "pear" @@ -70,5 +76,13 @@ gap> FavouriteFruit(foo); "pear" gap> HasFavouriteFruit(foo); true +gap> SetFavouriteFruit(foo, "apple"); +#I 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 +gap> SetInfoLevel(InfoAttributes, attributeinfo); gap> STOP_TEST("attribute.tst", 1);