Update to commit a469d404a7dc4e87e18f367eb4d8e05fc32d20a7
[inform.git] / src / symbols.c
index 6d736e940832cf1893b288d8417811a57e61fd24..8f2a09e52c90a4bd563e73bec844235caa60cb5f 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "symbols" :  The symbols table; creating stock of reserved words        */
 /*                                                                           */
-/*   Part of Inform 6.35                                                     */
-/*   copyright (c) Graham Nelson 1993 - 2021                                 */
+/*   Part of Inform 6.40                                                     */
+/*   copyright (c) Graham Nelson 1993 - 2022                                 */
 /*                                                                           */
 /* Inform is free software: you can redistribute it and/or modify            */
 /* it under the terms of the GNU General Public License as published by      */
@@ -15,7 +15,7 @@
 /* GNU General Public License for more details.                              */
 /*                                                                           */
 /* You should have received a copy of the GNU General Public License         */
-/* along with Inform. If not, see https://gnu.org/licenses/                  *
+/* along with Inform. If not, see https://gnu.org/licenses/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
@@ -30,24 +30,32 @@ int no_symbols;                        /* Total number of symbols defined    */
 int no_named_constants;                         /* Copied into story file    */
 
 /* ------------------------------------------------------------------------- */
-/*   Plus six arrays.  Each symbol has its own index n (an int32) and        */
+/*   Plus an array of symbolinfo.  Each symbol has its own index n (an       */
+/*   int32) in the array. The struct there contains:                         */
 /*                                                                           */
-/*       svals[n]   is its value (must be 32 bits wide, i.e. an int32, tho'  */
-/*                  it is used to hold an unsigned 16 bit Z-machine value)   */
-/*       sflags[n]  holds flags (see "header.h" for a list)                  */
-/*       stypes[n]  is the "type", distinguishing between the data type of   */
+/*       value   is its value. In Z-code, this holds both the 16-bit value   */
+/*                  and the 16-bit backpatch marker, so it is an int32.      */
+/*       marker   is the backpatch marker in Glulx.                          */
+/*       flags  holds flags (see "header.h" for a list of ?_SFLAGS)          */
+/*       type  is the "type", distinguishing between the data type of        */
 /*                  different kinds of constants/variables.                  */
-/*                  (See the "typename()" below.)                            */
-/*       symbs[n]   (needs to be cast to (char *) to be used) is the name    */
-/*                  of the symbol, in the same case form as when created.    */
-/*       slines[n]  is the source line on which the symbol value was first   */
+/*                  (A ?_T constant; see the "typename()" below.)            */
+/*       name   is the name of the symbol, in the same case form as          */
+/*                  when created.                                            */
+/*       line  is the source line on which the symbol value was first        */
 /*                  assigned                                                 */
-/*       symbol_debug_backpatch_positions[n]                                 */
+/*       next_entry  is the forward link in the symbol hash table. (See      */
+/*                  start_of_list, below.)                                   */
+/*                                                                           */
+/*   When generating a debug file (-k switch), we also allocate an array     */
+/*   of symboldebuginfo, which contains:                                     */
+/*                                                                           */
+/*       backpatch_pos                                                       */
 /*                  is a file position in the debug information file where   */
 /*                  the symbol's value should be written after backpatching, */
 /*                  or else the null position if the value was known and     */
 /*                  written beforehand                                       */
-/*       replacement_debug_backpatch_positions[n]                            */
+/*       replacement_backpatch_pos                                           */
 /*                  is a file position in the debug information file where   */
 /*                  the symbol's name can be erased if it is replaced, or    */
 /*                  else null if the name will never need to be replaced     */
@@ -55,39 +63,28 @@ int no_named_constants;                         /* Copied into story file    */
 /*   Comparison is case insensitive.                                         */
 /*   Note that local variable names are not entered into the symbols table,  */
 /*   as their numbers and scope are too limited for this to be efficient.    */
-/* ------------------------------------------------------------------------- */
-/*   Caveat editor: some array types are set up to work even on machines     */
-/*   where sizeof(int32 *) differs from, e.g., sizeof(char *): so do not     */
-/*   alter the types unless you understand what is going on!                 */
 /* ------------------------------------------------------------------------- */
 
-  int32  **symbs;
-  int32  *svals;
-  int    *smarks;            /* Glulx-only */
-  brief_location  *slines;
-  int    *sflags;
-#ifdef VAX
-  char   *stypes;            /* In VAX C, insanely, "signed char" is illegal */
-#else
-  signed char *stypes;
-#endif
-  maybe_file_position  *symbol_debug_backpatch_positions;
-  maybe_file_position  *replacement_debug_backpatch_positions;
+symbolinfo *symbols;                           /* Allocated up to no_symbols */
+static memory_list symbols_memlist;
+symboldebuginfo *symbol_debug_info;            /* Allocated up to no_symbols */
+static memory_list symbol_debug_info_memlist;
 
 /* ------------------------------------------------------------------------- */
 /*   Memory to hold the text of symbol names: note that this memory is       */
 /*   allocated as needed in chunks of size SYMBOLS_CHUNK_SIZE.               */
 /* ------------------------------------------------------------------------- */
 
-#define MAX_SYMBOL_CHUNKS (100)
+#define SYMBOLS_CHUNK_SIZE (4096)
 
-static uchar *symbols_free_space,       /* Next byte free to hold new names  */
+static char *symbols_free_space,        /* Next byte free to hold new names  */
            *symbols_ceiling;            /* Pointer to the end of the current
                                            allocation of memory for names    */
 
 static char** symbol_name_space_chunks; /* For chunks of memory used to hold
                                            the name strings of symbols       */
 static int no_symbol_name_space_chunks;
+static memory_list symbol_name_space_chunks_memlist;
 
 /* Symbol replacements (used by the "Replace X Y" directive). */
 
@@ -131,8 +128,8 @@ static int symbol_definitions_size = 0; /* calloced size */
 /*   idea to choose HASH_TAB_SIZE as large as conveniently possible.         */
 /* ------------------------------------------------------------------------- */
 
-static int   *next_entry;
-static int32 *start_of_list;
+static int32 *start_of_list; /* Allocated array of size HASH_TAB_SIZE */
+/* The next_entry field is part of the symbolinfo struct. */
 
 /* ------------------------------------------------------------------------- */
 /*   Initialisation.                                                         */
@@ -187,7 +184,8 @@ extern int strcmpcis(char *p, char *q)
 /* ------------------------------------------------------------------------- */
 
 extern void add_config_symbol_definition(char *symbol, int32 value)
-{
+{   char *str;
+
     if (symbol_definitions_count == symbol_definitions_size) {
         int oldsize = symbol_definitions_size;
         if (symbol_definitions_size == 0) 
@@ -198,7 +196,7 @@ extern void add_config_symbol_definition(char *symbol, int32 value)
             symbol_definitions_size, "symbol definition table");
     }
 
-    char *str = my_malloc(strlen(symbol)+1, "symbol name");
+    str = my_malloc(strlen(symbol)+1, "symbol name");
     strcpy(str, symbol);
     
     symbol_definitions[symbol_definitions_count].symbol = str;
@@ -210,17 +208,48 @@ extern void add_config_symbol_definition(char *symbol, int32 value)
 /*   Symbol finding, creating, and removing.                                 */
 /* ------------------------------------------------------------------------- */
 
+extern int get_symbol_index(char *p)
+{
+    /*  Return the index in the symbols array of symbol "p", or -1
+        if it isn't there. Does not create a new symbol or mark the
+        symbol as used. */
+
+    int32 new_entry, this;
+    char *r;
+    int hashcode = hash_code_from_string(p);
+
+    this = start_of_list[hashcode];
+
+    do
+    {   if (this == -1) break;
+
+        r = symbols[this].name;
+        new_entry = strcmpcis(r, p);
+        if (new_entry == 0) 
+        {
+            return this;
+        }
+        if (new_entry > 0) break;
+
+        this = symbols[this].next_entry;
+    } while (this != -1);
+
+    return -1;
+}
+
 extern int symbol_index(char *p, int hashcode)
 {
-    /*  Return the index in the symbs/svals/sflags/stypes/... arrays of symbol
-        "p", creating a new symbol with that name if it isn't already there.
+    /*  Return the index in the symbols array of symbol "p", creating a
+        new symbol with that name if it isn't already there.
 
         New symbols are created with flag UNKNOWN_SFLAG, value 0x100
         (a 2-byte quantity in Z-machine terms) and type CONSTANT_T.
 
         The string "p" is undamaged.                                         */
 
-    int32 new_entry, this, last; char *r;
+    int32 new_entry, this, last;
+    char *r;
+    int len;    
 
     if (hashcode == -1) hashcode = hash_code_from_string(p);
 
@@ -229,7 +258,7 @@ extern int symbol_index(char *p, int hashcode)
     do
     {   if (this == -1) break;
 
-        r = (char *)symbs[this];
+        r = symbols[this].name;
         new_entry = strcmpcis(r, p);
         if (new_entry == 0) 
         {
@@ -240,51 +269,55 @@ extern int symbol_index(char *p, int hashcode)
         if (new_entry > 0) break;
 
         last = this;
-        this = next_entry[this];
+        this = symbols[this].next_entry;
     } while (this != -1);
 
-    if (no_symbols >= MAX_SYMBOLS)
-        memoryerror("MAX_SYMBOLS", MAX_SYMBOLS);
+    if (symdef_trace_setting)
+        printf("Encountered symbol %d '%s'\n", no_symbols, p);
+    
+    ensure_memory_list_available(&symbols_memlist, no_symbols+1);
+    if (debugfile_switch)
+        ensure_memory_list_available(&symbol_debug_info_memlist, no_symbols+1);
 
     if (last == -1)
-    {   next_entry[no_symbols]=start_of_list[hashcode];
+    {   symbols[no_symbols].next_entry=start_of_list[hashcode];
         start_of_list[hashcode]=no_symbols;
     }
     else
-    {   next_entry[no_symbols]=this;
-        next_entry[last]=no_symbols;
+    {   symbols[no_symbols].next_entry=this;
+        symbols[last].next_entry=no_symbols;
     }
 
-    if (symbols_free_space+strlen(p)+1 >= symbols_ceiling)
+    len = strlen(p);
+    if (symbols_free_space+len+1 >= symbols_ceiling)
     {   symbols_free_space
             = my_malloc(SYMBOLS_CHUNK_SIZE, "symbol names chunk");
         symbols_ceiling = symbols_free_space + SYMBOLS_CHUNK_SIZE;
-        /* If we've passed MAX_SYMBOL_CHUNKS chunks, we print an error
-           message telling the user to increase SYMBOLS_CHUNK_SIZE.
-           That is the correct cure, even though the error comes out
-           worded inaccurately. */
-        if (no_symbol_name_space_chunks >= MAX_SYMBOL_CHUNKS)
-            memoryerror("SYMBOLS_CHUNK_SIZE", SYMBOLS_CHUNK_SIZE);
+        ensure_memory_list_available(&symbol_name_space_chunks_memlist, no_symbol_name_space_chunks+1);
         symbol_name_space_chunks[no_symbol_name_space_chunks++]
-            = (char *) symbols_free_space;
-        if (symbols_free_space+strlen(p)+1 >= symbols_ceiling)
-            memoryerror("SYMBOLS_CHUNK_SIZE", SYMBOLS_CHUNK_SIZE);
+            = symbols_free_space;
+        if (symbols_free_space+len+1 >= symbols_ceiling)
+        {
+            /* This should be impossible, since SYMBOLS_CHUNK_SIZE > MAX_IDENTIFIER_LENGTH. */
+            fatalerror("Symbol exceeds the maximum possible length");
+        }
     }
 
-    strcpy((char *) symbols_free_space, p);
-    symbs[no_symbols] = (int32 *) symbols_free_space;
-    symbols_free_space += strlen((char *)symbols_free_space) + 1;
+    strcpy(symbols_free_space, p);
+    symbols[no_symbols].name   = symbols_free_space;
+    symbols_free_space += (len+1);
 
-    svals[no_symbols]   =  0x100; /* ###-wrong? Would this fix the
+    symbols[no_symbols].value   =  0x100; /* ###-wrong? Would this fix the
                                      unbound-symbol-causes-asm-error? */
-    sflags[no_symbols]  =  UNKNOWN_SFLAG;
-    stypes[no_symbols]  =  CONSTANT_T;
-    slines[no_symbols]  =  get_brief_location(&ErrorReport);
+    symbols[no_symbols].flags  =  UNKNOWN_SFLAG;
+    symbols[no_symbols].marker =  0;
+    symbols[no_symbols].type  =  CONSTANT_T;
+    symbols[no_symbols].line  =  get_brief_location(&ErrorReport);
     if (debugfile_switch)
     {   nullify_debug_file_position
-            (&symbol_debug_backpatch_positions[no_symbols]);
+            (&symbol_debug_info[no_symbols].backpatch_pos);
         nullify_debug_file_position
-            (&replacement_debug_backpatch_positions[no_symbols]);
+            (&symbol_debug_info[no_symbols].replacement_backpatch_pos);
     }
 
     if (track_unused_routines)
@@ -300,19 +333,19 @@ extern void end_symbol_scope(int k)
     */
 
     int j;
-    j = hash_code_from_string((char *) symbs[k]);
+    j = hash_code_from_string(symbols[k].name);
     if (start_of_list[j] == k)
-    {   start_of_list[j] = next_entry[k];
+    {   start_of_list[j] = symbols[k].next_entry;
         return;
     }
     j = start_of_list[j];
     while (j != -1)
     {
-        if (next_entry[j] == k)
-        {   next_entry[j] = next_entry[k];
+        if (symbols[j].next_entry == k)
+        {   symbols[j].next_entry = symbols[k].next_entry;
             return;
         }
-        j = next_entry[j];
+        j = symbols[j].next_entry;
     }
 }
 
@@ -340,6 +373,10 @@ extern char *typename(int type)
         case OBJECT_T:              return("Object");
         case CLASS_T:               return("Class");
         case FAKE_ACTION_T:         return("Fake action");
+            
+        /*  These are not symbol types, but they get printed in errors. */
+        case STRING_REQ_T:          return("String");
+        case DICT_WORD_REQ_T:       return("Dictionary word");
 
         default:                   return("(Unknown type)");
     }
@@ -347,40 +384,140 @@ extern char *typename(int type)
 
 static void describe_flags(int flags)
 {   if (flags & UNKNOWN_SFLAG)  printf("(?) ");
-    if (flags & USED_SFLAG)     printf("(used) ");
     if (flags & REPLACE_SFLAG)  printf("(Replaced) ");
+    if (flags & USED_SFLAG)     printf("(used) ");
     if (flags & DEFCON_SFLAG)   printf("(Defaulted) ");
     if (flags & STUB_SFLAG)     printf("(Stubbed) ");
-    if (flags & CHANGE_SFLAG)   printf("(value will change) ");
     if (flags & IMPORT_SFLAG)   printf("(Imported) ");
     if (flags & EXPORT_SFLAG)   printf("(Exported) ");
+    if (flags & ALIASED_SFLAG)  printf("(aliased) ");
+    if (flags & CHANGE_SFLAG)   printf("(value will change) ");
     if (flags & SYSTEM_SFLAG)   printf("(System) ");
     if (flags & INSF_SFLAG)     printf("(created in sys file) ");
     if (flags & UERROR_SFLAG)   printf("('Unknown' error issued) ");
-    if (flags & ALIASED_SFLAG)  printf("(aliased) ");
     if (flags & ACTION_SFLAG)   printf("(Action name) ");
     if (flags & REDEFINABLE_SFLAG) printf("(Redefinable) ");
+    if (flags & STAR_SFLAG)     printf("(*) ");
 }
 
 extern void describe_symbol(int k)
 {   printf("%4d  %-16s  %2d:%04d  %04x  %s  ",
-        k, (char *) (symbs[k]), 
-        (int)(slines[k].file_index),
-        (int)(slines[k].line_number),
-        svals[k], typename(stypes[k]));
-    describe_flags(sflags[k]);
+        k, (symbols[k].name), 
+        (int)(symbols[k].line.file_index),
+        (int)(symbols[k].line.line_number),
+        symbols[k].value, typename(symbols[k].type));
+    describe_flags(symbols[k].flags);
 }
 
 extern void list_symbols(int level)
 {   int k;
     for (k=0; k<no_symbols; k++)
-    {   if ((level==2) ||
-            ((sflags[k] & (SYSTEM_SFLAG + UNKNOWN_SFLAG + INSF_SFLAG)) == 0))
+    {   if ((level>=2) ||
+            ((symbols[k].flags & (SYSTEM_SFLAG + UNKNOWN_SFLAG + INSF_SFLAG)) == 0))
         {   describe_symbol(k); printf("\n");
         }
     }
 }
 
+/* Check that the operand is of the given symbol type (XXX_T). If wanttype2 is nonzero, that's a second allowable type.
+   Generate a warning if no match. */
+extern void check_warn_symbol_type(const assembly_operand *AO, int wanttype, int wanttype2, char *context)
+{
+    symbolinfo *sym;
+    int symtype;
+    
+    if (AO->symindex < 0)
+    {
+        /* This argument is not a symbol; it's a local variable, a literal, or a computed expression. */
+        /* We can recognize and type-check some literals. */
+        if (AO->marker == DWORD_MV) {
+            if (wanttype != DICT_WORD_REQ_T && wanttype2 != DICT_WORD_REQ_T)
+                symtype_warning(context, NULL, typename(DICT_WORD_REQ_T), typename(wanttype));
+        }
+        if (AO->marker == STRING_MV) {
+            if (wanttype != STRING_REQ_T && wanttype2 != STRING_REQ_T)
+                symtype_warning(context, NULL, typename(STRING_REQ_T), typename(wanttype));
+        }
+        return;
+    }
+    
+    sym = &symbols[AO->symindex];
+    symtype = sym->type;
+    
+    if (symtype == GLOBAL_VARIABLE_T)
+    {
+        /* A global variable could have any value. No way to generate a warning. */
+        return;
+    }
+    if (symtype == CONSTANT_T)
+    {
+        /* A constant could also have any value. This case also includes forward-declared constants (UNKNOWN_SFLAG). */
+        /* We try inferring its type by looking at the backpatch marker. Sadly, this only works for objects. (And not in Z-code, where object values are not backpatched.) */
+        if (sym->marker == OBJECT_MV) {
+            /* Continue with inferred type. */
+            symtype = OBJECT_T;
+        }
+        else {
+            /* Give up. */
+            return;
+        }
+    }
+    
+    if (!(   (symtype == wanttype)
+          || (wanttype2 != 0 && symtype == wanttype2)))
+    {
+        symtype_warning(context, sym->name, typename(symtype), typename(wanttype));
+    }
+}
+
+/* Similar, but we allow any type that has a metaclass: Object, Class, String, or Routine.
+   Generate a warning if no match. */
+extern void check_warn_symbol_has_metaclass(const assembly_operand *AO, char *context)
+{
+    symbolinfo *sym;
+    int symtype;
+    
+    if (AO->symindex < 0)
+    {
+        /* This argument is not a symbol; it's a local variable, a literal, or a computed expression. */
+        /* We can recognize and type-check some literals. */
+        if (AO->marker == DWORD_MV) {
+            symtype_warning(context, NULL, typename(DICT_WORD_REQ_T), "Object/Class/Routine/String");
+        }
+        if (AO->marker == STRING_MV) {
+            /* Strings are good here. */
+        }
+        return;
+    }
+    
+    sym = &symbols[AO->symindex];
+    symtype = sym->type;
+    
+    if (symtype == GLOBAL_VARIABLE_T)
+    {
+        /* A global variable could have any value. No way to generate a warning. */
+        return;
+    }
+    if (symtype == CONSTANT_T)
+    {
+        /* A constant could also have any value. This case also includes forward-declared constants (UNKNOWN_SFLAG). */
+        /* We try inferring its type by looking at the backpatch marker. Sadly, this only works for objects. (And not in Z-code, where object values are not backpatched.) */
+        if (sym->marker == OBJECT_MV) {
+            /* Continue with inferred type. */
+            symtype = OBJECT_T;
+        }
+        else {
+            /* Give up. */
+            return;
+        }
+    }
+
+    if (!(symtype == ROUTINE_T || symtype == CLASS_T || symtype == OBJECT_T))
+    {
+        symtype_warning(context, sym->name, typename(symtype), "Object/Class/Routine/String");
+    }
+}
+
 extern void issue_unused_warnings(void)
 {   int32 i;
 
@@ -393,14 +530,25 @@ extern void issue_unused_warnings(void)
     /*  Now back to mark anything necessary as used  */
 
     i = symbol_index("Main", -1);
-    if (!(sflags[i] & UNKNOWN_SFLAG)) sflags[i] |= USED_SFLAG;
+    if (!(symbols[i].flags & UNKNOWN_SFLAG)) symbols[i].flags |= USED_SFLAG;
 
     for (i=0;i<no_symbols;i++)
-    {   if (((sflags[i]
+    {   if (((symbols[i].flags
              & (SYSTEM_SFLAG + UNKNOWN_SFLAG + EXPORT_SFLAG
                 + INSF_SFLAG + USED_SFLAG + REPLACE_SFLAG)) == 0)
-             && (stypes[i] != OBJECT_T))
-            dbnu_warning(typename(stypes[i]), (char *) symbs[i], slines[i]);
+             && (symbols[i].type != OBJECT_T))
+            dbnu_warning(typename(symbols[i].type), symbols[i].name, symbols[i].line);
+    }
+}
+
+extern void issue_debug_symbol_warnings(void)
+{
+    int value = get_symbol_index("DEBUG");
+    if (value >= 0 && (symbols[value].flags & USED_SFLAG) && !(symbols[value].flags & UNKNOWN_SFLAG)) {
+        value = get_symbol_index("debug_flag");
+        if (value >= 0 && (symbols[value].flags & USED_SFLAG) && (symbols[value].flags & UNKNOWN_SFLAG)) {
+            warning("DEBUG mode is on, but this story or library does not appear to support it");
+        }
     }
 }
 
@@ -431,112 +579,112 @@ extern void write_the_identifier_names(void)
     for (i=0; i<NUM_ATTR_BYTES*8; i++) attribute_name_strings[i] = null_value;
 
     for (i=0; i<no_symbols; i++)
-    {   t=stypes[i];
+    {   t=symbols[i].type;
         if ((t == INDIVIDUAL_PROPERTY_T) || (t == PROPERTY_T))
-        {   if (sflags[i] & ALIASED_SFLAG)
-            {   if (individual_name_strings[svals[i]] == 0)
-                {   sprintf(idname_string, "%s", (char *) symbs[i]);
+        {   if (symbols[i].flags & ALIASED_SFLAG)
+            {   if (individual_name_strings[symbols[i].value] == 0)
+                {   sprintf(idname_string, "%s", symbols[i].name);
 
                     for (j=i+1, k=0; (j<no_symbols && k<3); j++)
-                    {   if ((stypes[j] == stypes[i])
-                            && (svals[j] == svals[i]))
+                    {   if ((symbols[j].type == symbols[i].type)
+                            && (symbols[j].value == symbols[i].value))
                         {   sprintf(idname_string+strlen(idname_string),
-                                "/%s", (char *) symbs[j]);
+                                "/%s", symbols[j].name);
                             k++;
                         }
                     }
 
-                    individual_name_strings[svals[i]]
+                    individual_name_strings[symbols[i].value]
                         = compile_string(idname_string, STRCTX_SYMBOL);
                 }
             }
             else
-            {   sprintf(idname_string, "%s", (char *) symbs[i]);
+            {   sprintf(idname_string, "%s", symbols[i].name);
 
-                individual_name_strings[svals[i]]
+                individual_name_strings[symbols[i].value]
                     = compile_string(idname_string, STRCTX_SYMBOL);
             }
         }
         if (t == ATTRIBUTE_T)
-        {   if (sflags[i] & ALIASED_SFLAG)
-            {   if (attribute_name_strings[svals[i]] == null_value)
-                {   sprintf(idname_string, "%s", (char *) symbs[i]);
+        {   if (symbols[i].flags & ALIASED_SFLAG)
+            {   if (attribute_name_strings[symbols[i].value] == null_value)
+                {   sprintf(idname_string, "%s", symbols[i].name);
 
                     for (j=i+1, k=0; (j<no_symbols && k<3); j++)
-                    {   if ((stypes[j] == stypes[i])
-                            && (svals[j] == svals[i]))
+                    {   if ((symbols[j].type == symbols[i].type)
+                            && (symbols[j].value == symbols[i].value))
                         {   sprintf(idname_string+strlen(idname_string),
-                                "/%s", (char *) symbs[j]);
+                                "/%s", symbols[j].name);
                             k++;
                         }
                     }
 
-                    attribute_name_strings[svals[i]]
+                    attribute_name_strings[symbols[i].value]
                         = compile_string(idname_string, STRCTX_SYMBOL);
                 }
             }
             else
-            {   sprintf(idname_string, "%s", (char *) symbs[i]);
+            {   sprintf(idname_string, "%s", symbols[i].name);
 
-                attribute_name_strings[svals[i]]
+                attribute_name_strings[symbols[i].value]
                     = compile_string(idname_string, STRCTX_SYMBOL);
             }
         }
-        if (sflags[i] & ACTION_SFLAG)
-        {   sprintf(idname_string, "%s", (char *) symbs[i]);
+        if (symbols[i].flags & ACTION_SFLAG)
+        {   sprintf(idname_string, "%s", symbols[i].name);
             idname_string[strlen(idname_string)-3] = 0;
 
             if (debugfile_switch)
             {   debug_file_printf("<action>");
                 debug_file_printf
                     ("<identifier>##%s</identifier>", idname_string);
-                debug_file_printf("<value>%d</value>", svals[i]);
+                debug_file_printf("<value>%d</value>", symbols[i].value);
                 debug_file_printf("</action>");
             }
 
-            action_name_strings[svals[i]]
+            action_name_strings[symbols[i].value]
                 = compile_string(idname_string, STRCTX_SYMBOL);
         }
     }
 
     for (i=0; i<no_symbols; i++)
-    {   if (stypes[i] == FAKE_ACTION_T)
-        {   sprintf(idname_string, "%s", (char *) symbs[i]);
+    {   if (symbols[i].type == FAKE_ACTION_T)
+        {   sprintf(idname_string, "%s", symbols[i].name);
             idname_string[strlen(idname_string)-3] = 0;
 
-            action_name_strings[svals[i]
+            action_name_strings[symbols[i].value
                     - ((grammar_version_number==1)?256:4096) + no_actions]
                 = compile_string(idname_string, STRCTX_SYMBOL);
         }
     }
 
     for (j=0; j<no_arrays; j++)
-    {   i = array_symbols[j];
-        sprintf(idname_string, "%s", (char *) symbs[i]);
+    {   i = arrays[j].symbol;
+        sprintf(idname_string, "%s", symbols[i].name);
 
         array_name_strings[j]
             = compile_string(idname_string, STRCTX_SYMBOL);
     }
   if (define_INFIX_switch)
   { for (i=0; i<no_symbols; i++)
-    {   if (stypes[i] == GLOBAL_VARIABLE_T)
-        {   sprintf(idname_string, "%s", (char *) symbs[i]);
-            array_name_strings[no_arrays + svals[i] -16]
+    {   if (symbols[i].type == GLOBAL_VARIABLE_T)
+        {   sprintf(idname_string, "%s", symbols[i].name);
+            array_name_strings[no_arrays + symbols[i].value -16]
                 = compile_string(idname_string, STRCTX_SYMBOL);
         }
     }
 
     for (i=0; i<no_named_routines; i++)
-    {   sprintf(idname_string, "%s", (char *) symbs[named_routine_symbols[i]]);
+    {   sprintf(idname_string, "%s", symbols[named_routine_symbols[i]].name);
             array_name_strings[no_arrays + no_globals + i]
                 = compile_string(idname_string, STRCTX_SYMBOL);
     }
 
     for (i=0, no_named_constants=0; i<no_symbols; i++)
-    {   if (((stypes[i] == OBJECT_T) || (stypes[i] == CLASS_T)
-            || (stypes[i] == CONSTANT_T))
-            && ((sflags[i] & (UNKNOWN_SFLAG+ACTION_SFLAG))==0))
-        {   sprintf(idname_string, "%s", (char *) symbs[i]);
+    {   if (((symbols[i].type == OBJECT_T) || (symbols[i].type == CLASS_T)
+            || (symbols[i].type == CONSTANT_T))
+            && ((symbols[i].flags & (UNKNOWN_SFLAG+ACTION_SFLAG))==0))
+        {   sprintf(idname_string, "%s", symbols[i].name);
             array_name_strings[no_arrays + no_globals + no_named_routines
                 + no_named_constants++]
                 = compile_string(idname_string, STRCTX_SYMBOL);
@@ -551,36 +699,29 @@ extern void write_the_identifier_names(void)
 /* ------------------------------------------------------------------------- */
 
 static void assign_symbol_base(int index, int32 value, int type)
-{   svals[index]  = value;
-    stypes[index] = type;
-    if (sflags[index] & UNKNOWN_SFLAG)
-    {   sflags[index] &= (~UNKNOWN_SFLAG);
-        if (is_systemfile()) sflags[index] |= INSF_SFLAG;
-        slines[index] = get_brief_location(&ErrorReport);
+{   symbols[index].value  = value;
+    symbols[index].type = type;
+    if (symbols[index].flags & UNKNOWN_SFLAG)
+    {   symbols[index].flags &= (~UNKNOWN_SFLAG);
+        if (is_systemfile()) symbols[index].flags |= INSF_SFLAG;
+        symbols[index].line = get_brief_location(&ErrorReport);
     }
 }
 
 extern void assign_symbol(int index, int32 value, int type)
 {
-    if (!glulx_mode) {
-        assign_symbol_base(index, value, type);
-    }
-    else {
-        smarks[index] = 0;
-        assign_symbol_base(index, value, type);
-    }
+    assign_symbol_base(index, value, type);
+    symbols[index].marker = 0;
+    if (symdef_trace_setting)
+        printf("Defined symbol %d '%s' as %d (%s)\n", index, symbols[index].name, value, typename(type));
 }
 
 extern void assign_marked_symbol(int index, int marker, int32 value, int type)
 {
-    if (!glulx_mode) {
-        assign_symbol_base(index, (int32)marker*0x10000 + (value % 0x10000),
-            type);
-    }
-    else {
-        smarks[index] = marker;
-        assign_symbol_base(index, value, type);
-    }
+    assign_symbol_base(index, value, type);
+    symbols[index].marker = marker;
+    if (symdef_trace_setting)
+        printf("Defined symbol %d '%s' as %s %d (%s)\n", index, symbols[index].name, describe_mv(marker), value, typename(type));
 }
 
 static void emit_debug_information_for_predefined_symbol
@@ -633,21 +774,21 @@ static void emit_debug_information_for_predefined_symbol
 
 static void create_symbol(char *p, int32 value, int type)
 {   int i = symbol_index(p, -1);
-    if (!(sflags[i] & (UNKNOWN_SFLAG + REDEFINABLE_SFLAG))) {
+    if (!(symbols[i].flags & (UNKNOWN_SFLAG + REDEFINABLE_SFLAG))) {
         /* Symbol already defined! */
-        if (svals[i] == value && stypes[i] == type) {
+        if (symbols[i].value == value && symbols[i].type == type) {
             /* Special case: the symbol was already defined with this same
                value. We let it pass. */
             return;
         }
         else {
-            ebf_symbol_error("new symbol", p, typename(stypes[i]), slines[i]);
+            ebf_symbol_error("new symbol", p, typename(symbols[i].type), symbols[i].line);
             return;
         }
     }
-    svals[i] = value; stypes[i] = type; slines[i] = blank_brief_location;
+    symbols[i].value = value; symbols[i].type = type; symbols[i].line = blank_brief_location;
     /* If the symbol already existed with REDEFINABLE_SFLAG, we keep that. */
-    sflags[i] = USED_SFLAG + SYSTEM_SFLAG + (sflags[i] & REDEFINABLE_SFLAG);
+    symbols[i].flags = USED_SFLAG + SYSTEM_SFLAG + (symbols[i].flags & REDEFINABLE_SFLAG);
     emit_debug_information_for_predefined_symbol(p, i, value, type);
 }
 
@@ -655,8 +796,8 @@ static void create_rsymbol(char *p, int value, int type)
 {   int i = symbol_index(p, -1);
     /* This is only called for a few symbols with known names.
        They will not collide. */
-    svals[i] = value; stypes[i] = type; slines[i] = blank_brief_location;
-    sflags[i] = USED_SFLAG + SYSTEM_SFLAG + REDEFINABLE_SFLAG;
+    symbols[i].value = value; symbols[i].type = type; symbols[i].line = blank_brief_location;
+    symbols[i].flags = USED_SFLAG + SYSTEM_SFLAG + REDEFINABLE_SFLAG;
     emit_debug_information_for_predefined_symbol(p, i, value, type);
 }
 
@@ -698,7 +839,8 @@ static void stockup_symbols(void)
     }
 
     create_symbol("WORDSIZE",        WORDSIZE, CONSTANT_T);
-    create_symbol("DICT_ENTRY_BYTES", DICT_ENTRY_BYTE_LENGTH, CONSTANT_T);
+    /* DICT_ENTRY_BYTES must be REDEFINABLE_SFLAG because the Version directive can change it. */
+    create_rsymbol("DICT_ENTRY_BYTES", DICT_ENTRY_BYTE_LENGTH, CONSTANT_T);
     if (!glulx_mode) {
         create_symbol("DICT_WORD_SIZE", ((version_number==3)?4:6), CONSTANT_T);
         create_symbol("NUM_ATTR_BYTES", ((version_number==3)?4:6), CONSTANT_T);
@@ -820,7 +962,7 @@ extern void add_symbol_replacement_mapping(int original, int renamed)
     int ix;
 
     if (original == renamed) {
-        error_named("A routine cannot be 'Replace'd to itself:", (char *)symbs[original]);
+        error_named("A routine cannot be 'Replace'd to itself:", symbols[original].name);
         return;        
     }
 
@@ -841,10 +983,10 @@ extern void add_symbol_replacement_mapping(int original, int renamed)
 
     for (ix=0; ix<symbol_replacements_count; ix++) {
         if (original == symbol_replacements[ix].original_symbol) {
-            error_named("A routine cannot be 'Replace'd to more than one new name:", (char *)symbs[original]);
+            error_named("A routine cannot be 'Replace'd to more than one new name:", symbols[original].name);
         }
         if (renamed == symbol_replacements[ix].original_symbol) {
-            error_named("A routine cannot be 'Replace'd to a 'Replace'd name:", (char *)symbs[original]);
+            error_named("A routine cannot be 'Replace'd to a 'Replace'd name:", symbols[original].name);
         }
     }
 
@@ -1039,13 +1181,16 @@ extern void df_note_function_symbol(int symbol)
        of a function being defined.) */
     if (df_dont_note_global_symbols)
         return;
+    /* If we're compiling an unreachable statement, no reference. */
+    if (execution_never_reaches_here)
+        return;
 
     /* We are only interested in functions, or forward-declared symbols
        that might turn out to be functions. */
-    symtype = stypes[symbol];
+    symtype = symbols[symbol].type;
     if (symtype != ROUTINE_T && symtype != CONSTANT_T)
         return;
-    if (symtype == CONSTANT_T && !(sflags[symbol] & UNKNOWN_SFLAG))
+    if (symtype == CONSTANT_T && !(symbols[symbol].flags & UNKNOWN_SFLAG))
         return;
 
     bucket = (df_current_function_addr ^ (uint32)symbol) % DF_SYMBOL_HASH_BUCKETS;
@@ -1094,15 +1239,15 @@ extern void locate_dead_functions(void)
        we'll mark them specially. */
 
     ix = symbol_index("Main__", -1);
-    if (stypes[ix] == ROUTINE_T) {
-        uint32 addr = svals[ix] * (glulx_mode ? 1 : scale_factor);
+    if (symbols[ix].type == ROUTINE_T) {
+        uint32 addr = symbols[ix].value * (glulx_mode ? 1 : scale_factor);
         tofunc = df_function_for_address(addr);
         if (tofunc)
             tofunc->usage |= DF_USAGE_MAIN;
     }
     ix = symbol_index("Main", -1);
-    if (stypes[ix] == ROUTINE_T) {
-        uint32 addr = svals[ix] * (glulx_mode ? 1 : scale_factor);
+    if (symbols[ix].type == ROUTINE_T) {
+        uint32 addr = symbols[ix].value * (glulx_mode ? 1 : scale_factor);
         tofunc = df_function_for_address(addr);
         if (tofunc)
             tofunc->usage |= DF_USAGE_MAIN;
@@ -1120,12 +1265,12 @@ extern void locate_dead_functions(void)
     for (ent = func->refs; ent; ent=ent->refsnext) {
         uint32 addr;
         int symbol = ent->symbol;
-        if (stypes[symbol] != ROUTINE_T)
+        if (symbols[symbol].type != ROUTINE_T)
             continue;
-        addr = svals[symbol] * (glulx_mode ? 1 : scale_factor);
+        addr = symbols[symbol].value * (glulx_mode ? 1 : scale_factor);
         tofunc = df_function_for_address(addr);
         if (!tofunc) {
-            error_named("Internal error in stripping: global ROUTINE_T symbol is not found in df_function map:", (char *)symbs[symbol]);
+            error_named("Internal error in stripping: global ROUTINE_T symbol is not found in df_function map:", symbols[symbol].name);
             continue;
         }
         /* A function may be marked here more than once. That's fine. */
@@ -1177,12 +1322,12 @@ extern void locate_dead_functions(void)
             for (ent = func->refs; ent; ent=ent->refsnext) {
                 uint32 addr;
                 int symbol = ent->symbol;
-                if (stypes[symbol] != ROUTINE_T)
+                if (symbols[symbol].type != ROUTINE_T)
                     continue;
-                addr = svals[symbol] * (glulx_mode ? 1 : scale_factor);
+                addr = symbols[symbol].value * (glulx_mode ? 1 : scale_factor);
                 tofunc = df_function_for_address(addr);
                 if (!tofunc) {
-                    error_named("Internal error in stripping: function ROUTINE_T symbol is not found in df_function map:", (char *)symbs[symbol]);
+                    error_named("Internal error in stripping: function ROUTINE_T symbol is not found in df_function map:", symbols[symbol].name);
                     continue;
                 }
                 if (tofunc->usage)
@@ -1391,18 +1536,14 @@ extern uint32 df_next_function_iterate(int *funcused)
 
 extern void init_symbols_vars(void)
 {
-    symbs = NULL;
-    svals = NULL;
-    smarks = NULL;
-    stypes = NULL;
-    sflags = NULL;
-    next_entry = NULL;
+    symbols = NULL;
     start_of_list = NULL;
+    symbol_debug_info = NULL;
 
     symbol_name_space_chunks = NULL;
     no_symbol_name_space_chunks = 0;
     symbols_free_space=NULL;
-    symbols_ceiling=symbols_free_space;
+    symbols_ceiling=NULL;
 
     no_symbols = 0;
 
@@ -1433,28 +1574,21 @@ extern void symbols_begin_pass(void)
 
 extern void symbols_allocate_arrays(void)
 {
-    symbs      = my_calloc(sizeof(char *),  MAX_SYMBOLS, "symbols");
-    svals      = my_calloc(sizeof(int32),   MAX_SYMBOLS, "symbol values");
-    if (glulx_mode)
-        smarks = my_calloc(sizeof(int),     MAX_SYMBOLS, "symbol markers");
-    slines     = my_calloc(sizeof(brief_location), MAX_SYMBOLS, "symbol lines");
-    stypes     = my_calloc(sizeof(char),    MAX_SYMBOLS, "symbol types");
-    sflags     = my_calloc(sizeof(int),     MAX_SYMBOLS, "symbol flags");
+    initialise_memory_list(&symbols_memlist,
+        sizeof(symbolinfo), 6400, (void**)&symbols,
+        "symbols");
     if (debugfile_switch)
-    {   symbol_debug_backpatch_positions =
-            my_calloc(sizeof(maybe_file_position), MAX_SYMBOLS,
-                      "symbol debug information backpatch positions");
-        replacement_debug_backpatch_positions =
-            my_calloc(sizeof(maybe_file_position), MAX_SYMBOLS,
-                      "replacement debug information backpatch positions");
+    {
+        initialise_memory_list(&symbol_debug_info_memlist,
+            sizeof(symboldebuginfo), 6400, (void**)&symbol_debug_info,
+            "symbol debug backpatch info");
     }
-    next_entry = my_calloc(sizeof(int),     MAX_SYMBOLS,
-                     "symbol linked-list forward links");
     start_of_list = my_calloc(sizeof(int32), HASH_TAB_SIZE,
                      "hash code list beginnings");
 
-    symbol_name_space_chunks
-        = my_calloc(sizeof(char *), MAX_SYMBOL_CHUNKS, "symbol names chunk addresses");
+    initialise_memory_list(&symbol_name_space_chunks_memlist,
+        sizeof(char *), 32, (void**)&symbol_name_space_chunks,
+        "symbol names chunk addresses");
 
     if (track_unused_routines) {
         df_tables_closed = FALSE;
@@ -1495,23 +1629,13 @@ extern void symbols_free_arrays(void)
         my_free(&(symbol_name_space_chunks[i]),
             "symbol names chunk");
 
-    my_free(&symbol_name_space_chunks, "symbol names chunk addresses");
+    deallocate_memory_list(&symbol_name_space_chunks_memlist);
 
-    my_free(&symbs, "symbols");
-    my_free(&svals, "symbol values");
-    my_free(&smarks, "symbol markers");
-    my_free(&slines, "symbol lines");
-    my_free(&stypes, "symbol types");
-    my_free(&sflags, "symbol flags");
+    deallocate_memory_list(&symbols_memlist);
     if (debugfile_switch)
-    {   my_free
-            (&symbol_debug_backpatch_positions,
-             "symbol debug information backpatch positions");
-        my_free
-            (&replacement_debug_backpatch_positions,
-             "replacement debug information backpatch positions");
+    {
+        deallocate_memory_list(&symbol_debug_info_memlist);
     }
-    my_free(&next_entry, "symbol linked-list forward links");
     my_free(&start_of_list, "hash code list beginnings");
 
     if (symbol_replacements)