diff --git a/docs/internals.txt b/docs/internals.txt index d3ebb1fd..123f5fb1 100644 --- a/docs/internals.txt +++ b/docs/internals.txt @@ -34,17 +34,14 @@ Euphoria. ==== The Euphoria representation of a Euphoria Object ===== -Every Euphoria object is stored as-is. A special unlikely floating point value +Every Euphoria object in the Euphoria backend is stored as-is. A special unlikely floating point value is used for ##NOVALUE##. ##NOVALUE## signifies that a variable has not been assigned a value or the end of a sequence. ==== The C Representation of a Euphoria Object ===== Every Euphoria object is either stored as is, or as an encoded pointer. A -Euphoria ##integer## is stored in a 32-bit signed integer. If the number is too -big for a Euphoria ##integer##, it is assigned to a 64-bit double float in a -structure and an encoded pointer to that structure is stored in the said 32-bit -memory space. Sequences are stored in a similar way. +Euphoria ##integer## is often stored in a 32-bit or 64-bit signed integer. If the number is too big for a Euphoria ##integer##, has a fraction part, or has a destructor associated with it; it is assigned to a 64-bit double float in a structure and an encoded pointer to that structure is stored in the said 64 bit or 32-bit memory space. Sequences are stored in a similar way: An encoded pointer is stored, and this pointer points to a sequence structure. {{{ @@ -63,15 +60,13 @@ memory space. Sequences are stored in a similar way. |<-------- integer --------->| |<--------------------- object ---------------------->| + }}} +The 64-bit system is not documented here. Refer to euphoria.h for detail on the 64-bit version of the scheme outlined above. -Euphoria integers are stored in object variables as-is. An object variable is a -four byte signed integer. Legal integer values for Euphoria integers are -between -1,073,741,824 ( -2^^30^^ ) and +1,073,741,823 ( 2^^30^^-1 ). -Unsigned hexadecimal numbers from C000_0000 to FFFF_FFFF are the negative -integers and numbers from 0000_0000 to 3FFF_FFFF are the positive integers. The -hexadecimal values not used as integers are thus 4000_0000 to BFFF_FFFF. Other +Euphoria integers are stored in object variables as-is. An object variable is a four byte signed integer and is even its own value or an encoded pointer. Legal integer values for Euphoria integers are between -1,073,741,824 ( -2^^30^^ ) and +1,073,741,823 ( 2^^30^^-1 ). +Unsigned hexadecimal numbers from C000_0000 to FFFF_FFFF are the negative integers and numbers from 0000_0000 to 3FFF_FFFF are the positive integers. The hexadecimal values not used as integers are thus 4000_0000 to BFFF_FFFF. Other values are for encoded pointers. Pointers are always 8 byte aligned. So a pointer is stored in 29-bits instead of 32 and can fit in a hexadecimal range 0x2000_0000 long. The pointers are encoded in such a way that their encoded values @@ -84,11 +79,13 @@ variable and it also signifies the end of a sequence. In C, values of this type are stored in the 'object' type. The range 4000_0000 to 7FFF_FFFF is unused. A double structure 'struct d' could indeed contain a value that is legally in -the range of a Euphoria integer. So the encoded pointer to this structure is +the range of a Euphoria integer and has no fraction part. So the encoded pointer to this structure is recognized by the interpreter as an 'integer' but in this internals document when we say Euphoria integer we mean it actually is a C integer in the legal Euphoria integer range. +Objects declared as integer may use encoded pointers to double structures in its representation even after type checking +due to changes made for ticket 937. === The C Representations of a Euphoria Sequence and a Euphoria Atom ===== @@ -182,13 +179,13 @@ in struct s1. To this end, the 64bit implementation of 4.1 has these members in The macros are imperfect. For example, ##IS_SEQUENCE(NOVALUE)## returns ##TRUE## and ##IS_ATOM_DBL## will return ##TRUE## for integer values as well -as encoded pointers to -'struct d's. This is why there is an order that these tests are made: We test -##IS_ATOM_INT## and if that fails we can use ##IS_ATOM_DBL## and then that will -only be true if we pass an encoded pointer to a double. We must be sure that -something is not ##NOVALUE## before we use ##IS_SEQUENCE## on it. +as encoded pointers to 'struct d's. This is why there is an order that these tests are made: +First, we must either by logic or testing ensure that a value cannot be NOVALUE, and +then we can rely on ##IS_ATOM_INT## to determine that a value is not a pointer to a double structure, +or a sequence structure. Then we may use IS_ATOM_DBL to test that it is a pointer to a double structure. +We must be sure that something is not ##NOVALUE## before we use ##IS_SEQUENCE## on it. -// Often we know foo is not NOVALUE before getting into this:// +// Often we know something (foo) is not NOVALUE before getting into this:// {{{ // object foo @@ -207,6 +204,8 @@ A sequence is held in a 'struct s1' type and a double is contained in a === Type Value Functions and Macros + + @[:internals:IS_ATOM_INT|] ==== IS_ATOM_INT diff --git a/include/euphoria.h b/include/euphoria.h index 75d7ebf6..4aabd927 100644 --- a/include/euphoria.h +++ b/include/euphoria.h @@ -375,6 +375,8 @@ object Dor_bits(d_ptr a, d_ptr b); object Dxor_bits(d_ptr a, d_ptr b); object not_bits(object a); object Dxor(object a, object b); +object Dor(object a, object b); +object Xor(object a, object b); object Date(); cleanup_ptr ChainDeleteRoutine( cleanup_ptr old, cleanup_ptr prev ); cleanup_ptr DeleteRoutine( object e_index ); diff --git a/source/be_execute.c b/source/be_execute.c index d4005616..a81211e5 100644 --- a/source/be_execute.c +++ b/source/be_execute.c @@ -1934,7 +1934,7 @@ void do_exec(intptr_t *start_pc) int end_pos; int going_up; object_ptr result_ptr; - object result_val; + object result_val=0; int cf; int seqlen; opcode_type *patch; @@ -2772,12 +2772,6 @@ void do_exec(intptr_t *start_pc) tpc = pc; a = DoubleToInt(top); if (IS_ATOM_INT(a)) { - if (UNIQUE(DBL_PTR(top)) && (DBL_PTR(top)->cleanup != 0)) { - tpc = pc - 1; //RTFatalType(pc-1); - RTFatal("Cannot assign value with a destructor to an integer"); - } - DeRefDS(top); - *(object_ptr)pc[-1] = a; BREAK; } } diff --git a/source/compile.e b/source/compile.e index 61aac045..7cc50d9f 100644 --- a/source/compile.e +++ b/source/compile.e @@ -2988,12 +2988,12 @@ procedure opINTEGER_CHECK() LeftSym = TRUE c_stmt("_1 = (object)(DBL_PTR(@)->dbl);\n", sym) LeftSym = TRUE - c_stmt( "if (UNIQUE(DBL_PTR(@)) && (DBL_PTR(@)->cleanup != 0))\n" & - "RTFatal(\"Cannot assign value with a destructor to an integer\");", {sym, sym}) - c_stmt("DeRefDS(@);\n", sym) - c_stmt("@ = _1;\n", sym) + -- The BB_var_type may still be a Dptr structure after an integer check. + c_stmt( "if (!UNIQUE(DBL_PTR(@)) || (DBL_PTR(@)->cleanup == 0)) {\n", {sym, sym}) + c_stmt( "DeRefDS(@);\n", {sym}) + c_stmt( "@ = _1;\n", {sym}) c_stmt0("}\n") - SetBBType(sym, TYPE_INTEGER, novalue, TYPE_OBJECT, 0 ) + c_stmt0("}\n") end if pc += 2 end procedure diff --git a/source/emit.e b/source/emit.e index dcab9efe..6085b05a 100644 --- a/source/emit.e +++ b/source/emit.e @@ -220,23 +220,11 @@ function IsInteger(symtab_index sym) if sym < 1 then -- probably a forward reference - return 0 + return FALSE end if mode = SymTab[sym][S_MODE] - if mode = M_NORMAL then - t = SymTab[sym][S_VTYPE] - if t = integer_type then - return TRUE - end if - if t > 0 then - pt = SymTab[t][S_NEXT] - if pt and SymTab[pt][S_VTYPE] = integer_type then - return TRUE -- usertype(integer x) - end if - end if - - elsif mode = M_CONSTANT then + if mode = M_CONSTANT then if integer(SymTab[sym][S_OBJ]) then -- bug fixed: can't allow PLUS1_I op return TRUE end if diff --git a/source/parser.e b/source/parser.e index 85d7a424..b7a49600 100644 --- a/source/parser.e +++ b/source/parser.e @@ -3366,7 +3366,7 @@ export function CompileType(symtab_index type_ptr) return TYPE_OBJECT elsif type_ptr = integer_type then - return TYPE_INTEGER + return TYPE_ATOM elsif type_ptr = atom_type then return TYPE_ATOM @@ -3381,7 +3381,7 @@ export function CompileType(symtab_index type_ptr) -- user defined - look at type of the parameter of the type t = SymTab[SymTab[type_ptr][S_NEXT]][S_VTYPE] if t = integer_type then - return TYPE_INTEGER + return TYPE_ATOM elsif t = atom_type then return TYPE_ATOM elsif t = sequence_type then diff --git a/tests/t_937.e b/tests/t_937.e new file mode 100644 index 00000000..858998d1 --- /dev/null +++ b/tests/t_937.e @@ -0,0 +1,73 @@ +include std/io.e +include std/error.e +include std/unittest.e +with trace +trace(1) + + +type enum boolean T,F=0 end type +type file_number(integer x) + return x >= -1 +end type + +boolean enable_my_close = F + +procedure my_close(integer fh) + if fh > io:STDERR then + -- Closing file + if not enable_my_close then + test_fail("premature file closing") + end if + close(fh) + end if + test_pass("dstor called") +end procedure + +procedure do_nothing(integer bool) +end procedure + +-- Make sure a user defined type that is integer based can have routine_ids too. +procedure use_integer_with_dtor() + file_number f_debug = open("example.log", "w") + if f_debug =-1 then + f_debug = open("/dev/null", "w") + puts(io:STDERR, "Unable to create log file.") + else + f_debug = delete_routine(f_debug, routine_id("my_close")) + end if +end procedure + +enable_my_close = T + +-- dtor will be called when this routine exits. +use_integer_with_dtor() + + +enable_my_close = F +file_number f_debug = open("example.log", "w") +if f_debug =-1 then + f_debug = open("/dev/null", "w") + puts(io:STDERR, "Unable to create log file.") +else + f_debug = delete_routine(f_debug, routine_id("my_close")) +end if + +enable_my_close = T +-- dtor will be executed by changing its value to a new one. +f_debug = -1 + + +enable_my_close = F +f_debug = open("example.log", "w") +if f_debug =-1 then + f_debug = open("/dev/null", "w") + puts(io:STDERR, "Unable to create log file.") +else + f_debug = delete_routine(f_debug, routine_id("my_close")) +end if + +enable_my_close = T +-- dtor will be executed by changing its value to a new one. +f_debug += 1 + +test_report() diff --git a/tests/t_autoclose.e b/tests/t_autoclose.e new file mode 100644 index 00000000..9ddb5f16 --- /dev/null +++ b/tests/t_autoclose.e @@ -0,0 +1,26 @@ +include std/unittest.e + +type enum boolean + FALSE=0,TRUE +end type + +boolean cleanedup = FALSE + +procedure cleanup_now(object in_put) + cleanedup = TRUE +end procedure + +integer fd = open("t_autoclose.e", "r", 1) + +test_pass( "able to assign integers to integer with destructors" ) + +function wrapper() + return delete_routine(4, routine_id("cleanup_now")) +end function + +integer other = wrapper() + +other = 3 + +test_equal("Cleaned up called", TRUE, cleanedup) +test_report() \ No newline at end of file