Update to commit a469d404a7dc4e87e18f367eb4d8e05fc32d20a7
[inform.git] / src / states.c
index b569fb26cd933e66974fd551aaa5b7e4460b8766..ea495505cf70bdef2b8f4bce014f0a4b1ff24dfd 100644 (file)
@@ -1,8 +1,8 @@
 /* ------------------------------------------------------------------------- */
 /*   "states" :  Statement translator                                        */
 /*                                                                           */
-/*   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/                  */
 /*                                                                           */
 /* ------------------------------------------------------------------------- */
 
@@ -259,17 +259,17 @@ extern int parse_label(void)
     get_next_token();
 
     if ((token_type == SYMBOL_TT) &&
-        (stypes[token_value] == LABEL_T))
-    {   sflags[token_value] |= USED_SFLAG;
-        return(svals[token_value]);
+        (symbols[token_value].type == LABEL_T))
+    {   symbols[token_value].flags |= USED_SFLAG;
+        return(symbols[token_value].value);
     }
 
-    if ((token_type == SYMBOL_TT) && (sflags[token_value] & UNKNOWN_SFLAG))
+    if ((token_type == SYMBOL_TT) && (symbols[token_value].flags & UNKNOWN_SFLAG))
     {   assign_symbol(token_value, next_label, LABEL_T);
         define_symbol_label(token_value);
         next_label++;
-        sflags[token_value] |= CHANGE_SFLAG + USED_SFLAG;
-        return(svals[token_value]);
+        symbols[token_value].flags |= CHANGE_SFLAG + USED_SFLAG;
+        return(symbols[token_value].value);
     }
 
     ebf_error("label name", token_text);
@@ -429,19 +429,21 @@ static void parse_print_z(int finally_return)
                           break;
 
                         case SYMBOL_TT:
-                          if (sflags[token_value] & UNKNOWN_SFLAG)
+                          if (symbols[token_value].flags & UNKNOWN_SFLAG)
                           {   INITAOT(&AO, LONG_CONSTANT_OT);
                               AO.value = token_value;
                               AO.marker = SYMBOL_MV;
+                              AO.symindex = token_value;
                           }
                           else
                           {   INITAOT(&AO, LONG_CONSTANT_OT);
-                              AO.value = svals[token_value];
+                              AO.value = symbols[token_value].value;
                               AO.marker = IROUTINE_MV;
-                              if (stypes[token_value] != ROUTINE_T)
+                              AO.symindex = token_value;
+                              if (symbols[token_value].type != ROUTINE_T)
                                 ebf_error("printing routine name", token_text);
                           }
-                          sflags[token_value] |= USED_SFLAG;
+                          symbols[token_value].flags |= USED_SFLAG;
 
                           PrintByRoutine:
 
@@ -664,19 +666,21 @@ static void parse_print_g(int finally_return)
                           break;
 
                         case SYMBOL_TT:
-                          if (sflags[token_value] & UNKNOWN_SFLAG)
+                          if (symbols[token_value].flags & UNKNOWN_SFLAG)
                           {   INITAOT(&AO, CONSTANT_OT);
                               AO.value = token_value;
                               AO.marker = SYMBOL_MV;
+                              AO.symindex = token_value;
                           }
                           else
                           {   INITAOT(&AO, CONSTANT_OT);
-                              AO.value = svals[token_value];
+                              AO.value = symbols[token_value].value;
                               AO.marker = IROUTINE_MV;
-                              if (stypes[token_value] != ROUTINE_T)
+                              AO.symindex = token_value;
+                              if (symbols[token_value].type != ROUTINE_T)
                                 ebf_error("printing routine name", token_text);
                           }
-                          sflags[token_value] |= USED_SFLAG;
+                          symbols[token_value].flags |= USED_SFLAG;
 
                           PrintByRoutine:
 
@@ -734,57 +738,73 @@ static void parse_print_g(int finally_return)
     }
 }
 
-static void parse_statement_z(int break_label, int continue_label)
-{   int ln, ln2, ln3, ln4, flag;
-    assembly_operand AO, AO2, AO3, AO4;
-    debug_location spare_debug_location1, spare_debug_location2;
-
-    ASSERT_ZCODE();
-
-    if ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
+/* Parse any number of ".Label;" lines before a statement.
+   Returns whether a statement can in fact follow. */
+static int parse_named_label_statements()
+{
+    while ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
     {   /*  That is, a full stop, signifying a label  */
 
         get_next_token();
-        if (token_type == SYMBOL_TT)
+        if (token_type != SYMBOL_TT)
         {
-            if (sflags[token_value] & UNKNOWN_SFLAG)
-            {   assign_symbol(token_value, next_label, LABEL_T);
-                sflags[token_value] |= USED_SFLAG;
-                assemble_label_no(next_label);
-                define_symbol_label(token_value);
-                next_label++;
+            ebf_error("label name", token_text);
+            return TRUE;
+        }
+
+        if (symbols[token_value].flags & UNKNOWN_SFLAG)
+        {   assign_symbol(token_value, next_label, LABEL_T);
+            symbols[token_value].flags |= USED_SFLAG;
+            assemble_label_no(next_label);
+            define_symbol_label(token_value);
+            next_label++;
+        }
+        else
+        {   if (symbols[token_value].type != LABEL_T) {
+                ebf_error("label name", token_text);
+                return TRUE;
             }
-            else
-            {   if (stypes[token_value] != LABEL_T) goto LabelError;
-                if (sflags[token_value] & CHANGE_SFLAG)
-                {   sflags[token_value] &= (~(CHANGE_SFLAG));
-                    assemble_label_no(svals[token_value]);
-                    define_symbol_label(token_value);
-                }
-                else error_named("Duplicate definition of label:", token_text);
+            if (symbols[token_value].flags & CHANGE_SFLAG)
+            {   symbols[token_value].flags &= (~(CHANGE_SFLAG));
+                assemble_label_no(symbols[token_value].value);
+                define_symbol_label(token_value);
             }
+            else error_named("Duplicate definition of label:", token_text);
+        }
 
-            get_next_token();
-            if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
-            {   ebf_error("';'", token_text);
-                put_token_back(); return;
-            }
+        get_next_token();
+        if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
+        {   ebf_error("';'", token_text);
+            put_token_back(); return FALSE;
+        }
 
-            /*  Interesting point of Inform grammar: a statement can only
-                consist solely of a label when it is immediately followed
-                by a "}".                                                    */
+        /*  Interesting point of Inform grammar: a statement can only
+            consist solely of a label when it is immediately followed
+            by a "}".                                                    */
 
-            get_next_token();
-            if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
-            {   put_token_back(); return;
-            }
-            statement_debug_location = get_token_location();
-            parse_statement(break_label, continue_label);
-            return;
+        get_next_token();
+        if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
+        {   put_token_back(); return FALSE;
         }
-        LabelError: ebf_error("label name", token_text);
+        /* The following line prevents labels from influencing the positions
+           of sequence points. */
+        statement_debug_location = get_token_location();
+        
+        /* Another label might follow */
     }
 
+    /* On with the statement */
+    return TRUE;
+}
+
+static void parse_statement_z(int break_label, int continue_label)
+{   int ln, ln2, ln3, ln4, flag;
+    int pre_unreach, labelexists;
+    assembly_operand AO, AO2, AO3, AO4;
+    debug_location spare_debug_location1, spare_debug_location2;
+
+    ASSERT_ZCODE();
+
     if ((token_type == SEP_TT) && (token_value == HASH_SEP))
     {   parse_directive(TRUE);
         parse_statement(break_label, continue_label); return;
@@ -901,7 +921,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  get_next_token();
                  if ((token_type == STATEMENT_TT)
                      && (token_value == UNTIL_CODE))
-                 {   assemble_label_no(ln2);
+                 {   assemble_forward_label_no(ln2);
                      match_open_bracket();
                      AO = parse_expression(CONDITION_CONTEXT);
                      match_close_bracket();
@@ -909,7 +929,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  }
                  else error("'do' without matching 'until'");
 
-                 assemble_label_no(ln3);
+                 assemble_forward_label_no(ln3);
                  break;
 
     /*  -------------------------------------------------------------------- */
@@ -997,7 +1017,7 @@ static void parse_statement_z(int break_label, int continue_label)
                              sequence_point_follows = FALSE;
                              if (!execution_never_reaches_here)
                                  assemblez_jump(ln);
-                             assemble_label_no(ln2);
+                             assemble_forward_label_no(ln2);
                              return;
                          }
                          goto ParseUpdate;
@@ -1106,7 +1126,7 @@ static void parse_statement_z(int break_label, int continue_label)
                      }
                  }
 
-                 assemble_label_no(ln3);
+                 assemble_forward_label_no(ln3);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -1116,6 +1136,7 @@ static void parse_statement_z(int break_label, int continue_label)
         case GIVE_CODE:
                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                           QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
                  if ((AO.type == VARIABLE_OT) && (AO.value == 0))
                  {   INITAOTV(&AO, SHORT_CONSTANT_OT, 252);
                      if (version_number != 6) assemblez_1(pull_zc, AO);
@@ -1130,15 +1151,12 @@ static void parse_statement_z(int break_label, int continue_label)
                      if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
                          ln = clear_attr_zc;
                      else
-                     {   if ((token_type == SYMBOL_TT)
-                             && (stypes[token_value] != ATTRIBUTE_T))
-                           warning_named("This is not a declared Attribute:",
-                             token_text);
-                         ln = set_attr_zc;
+                     {   ln = set_attr_zc;
                          put_token_back();
                      }
                      AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                                QUANTITY_CONTEXT, -1);
+                     check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
                      if (runtime_error_checking_switch)
                      {   ln2 = (ln==set_attr_zc)?RT__ChG_VR:RT__ChGt_VR;
                          if (version_number >= 5)
@@ -1159,8 +1177,9 @@ static void parse_statement_z(int break_label, int continue_label)
     /*  -------------------------------------------------------------------- */
 
         case IF_CODE:
-                 flag = FALSE;
+                 flag = FALSE; /* set if there's an "else" */
                  ln2 = 0;
+                 pre_unreach = execution_never_reaches_here;
 
                  match_open_bracket();
                  AO = parse_expression(CONDITION_CONTEXT);
@@ -1178,8 +1197,17 @@ static void parse_statement_z(int break_label, int continue_label)
                      ln = next_label++;
                  }
 
+                 /* The condition */
                  code_generate(AO, CONDITION_CONTEXT, ln);
 
+                 if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
+                     /* If the condition never falls through to here, then
+                        it was an "if (0)" test. Our convention is to skip
+                        the "not reached" warnings for this case. */
+                     execution_never_reaches_here |= EXECSTATE_NOWARN;
+                 }
+
+                 /* The "if" block */
                  if (ln >= 0) parse_code_block(break_label, continue_label, 0);
                  else
                  {   get_next_token();
@@ -1212,13 +1240,46 @@ static void parse_statement_z(int break_label, int continue_label)
                  }
                  else put_token_back();
 
-                 if (ln >= 0) assemble_label_no(ln);
+                 /* The "else" label (or end of statement, if there is no "else") */
+                 labelexists = FALSE;
+                 if (ln >= 0) labelexists = assemble_forward_label_no(ln);
 
                  if (flag)
-                 {   parse_code_block(break_label, continue_label, 0);
-                     if (ln >= 0) assemble_label_no(ln2);
-                 }
+                 {
+                     /* If labelexists is false, then we started with
+                        "if (1)". In this case, we don't want a "not
+                        reached" warning on the "else" block. We
+                        temporarily disable the NOWARN flag, and restore it
+                        afterwards. */
+                     int saved_unreach = 0;
+                     if (execution_never_reaches_here && !labelexists) {
+                         saved_unreach = execution_never_reaches_here;
+                         execution_never_reaches_here |= EXECSTATE_NOWARN;
+                     }
+
+                     /* The "else" block */
+                     parse_code_block(break_label, continue_label, 0);
 
+                     if (execution_never_reaches_here && !labelexists) {
+                         if (saved_unreach & EXECSTATE_NOWARN)
+                             execution_never_reaches_here |= EXECSTATE_NOWARN;
+                         else
+                             execution_never_reaches_here &= ~EXECSTATE_NOWARN;
+                     }
+
+                     /* The post-"else" label */
+                     if (ln >= 0) assemble_forward_label_no(ln2);
+                 }
+                 else
+                 {
+                     /* There was no "else". If we're unreachable, then the
+                        statement returned unconditionally, which means 
+                        "if (1) return". Skip warnings. */
+                     if (!pre_unreach && execution_never_reaches_here) {
+                         execution_never_reaches_here |= EXECSTATE_NOWARN;
+                     }
+                 }
+                         
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -1271,6 +1332,8 @@ static void parse_statement_z(int break_label, int continue_label)
                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
                  AO = code_generate(AO, QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
+                 check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
                  {   if (version_number >= 5)
                          assemblez_3(call_vn_zc, veneer_routine(RT__ChT_VR),
@@ -1303,8 +1366,8 @@ static void parse_statement_z(int break_label, int continue_label)
                      AO.value = token_value;
                  else
                  if ((token_type == SYMBOL_TT) &&
-                     (stypes[token_value] == GLOBAL_VARIABLE_T))
-                     AO.value = svals[token_value];
+                     (symbols[token_value].type == GLOBAL_VARIABLE_T))
+                     AO.value = symbols[token_value].value;
                  else
                  {   ebf_error("'objectloop' variable", token_text);
                      panic_mode_error_recovery(); break;
@@ -1490,6 +1553,7 @@ static void parse_statement_z(int break_label, int continue_label)
         case REMOVE_CODE:
                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
                  {   if (version_number >= 5)
                          assemblez_2(call_2n_zc, veneer_routine(RT__ChR_VR),
@@ -1599,13 +1663,11 @@ static void parse_statement_z(int break_label, int continue_label)
                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
                  if (is_constant_ot(AO2.type) && AO2.marker == 0) {
-                     if (AO2.value >= 96)
-                     {   error("Z-machine dynamic strings are limited to 96");
+                     /* Compile-time check */
+                     if (AO2.value < 0 || AO2.value >= 96 || AO2.value >= MAX_DYNAMIC_STRINGS) {
+                         error_max_dynamic_strings(AO2.value);
                          AO2.value = 0;
                      }
-                     if (AO2.value < 0 || AO2.value >= MAX_DYNAMIC_STRINGS) {
-                         memoryerror("MAX_DYNAMIC_STRINGS", MAX_DYNAMIC_STRINGS);
-                     }
                  }
                  get_next_token();
                  if (token_type == DQ_TT)
@@ -1673,7 +1735,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  assemblez_store(AO2, AO);
 
                  parse_code_block(ln = next_label++, continue_label, 1);
-                 assemble_label_no(ln);
+                 assemble_forward_label_no(ln);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -1691,7 +1753,7 @@ static void parse_statement_z(int break_label, int continue_label)
                  parse_code_block(ln2, ln, 0);
                  sequence_point_follows = FALSE;
                  assemblez_jump(ln);
-                 assemble_label_no(ln2);
+                 assemble_forward_label_no(ln2);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -1716,57 +1778,12 @@ static void parse_statement_z(int break_label, int continue_label)
 
 static void parse_statement_g(int break_label, int continue_label)
 {   int ln, ln2, ln3, ln4, flag, onstack;
+    int pre_unreach, labelexists;
     assembly_operand AO, AO2, AO3, AO4;
     debug_location spare_debug_location1, spare_debug_location2;
 
     ASSERT_GLULX();
 
-    if ((token_type == SEP_TT) && (token_value == PROPERTY_SEP))
-    {   /*  That is, a full stop, signifying a label  */
-
-        get_next_token();
-        if (token_type == SYMBOL_TT)
-        {
-            if (sflags[token_value] & UNKNOWN_SFLAG)
-            {   assign_symbol(token_value, next_label, LABEL_T);
-                sflags[token_value] |= USED_SFLAG;
-                assemble_label_no(next_label);
-                define_symbol_label(token_value);
-                next_label++;
-            }
-            else
-            {   if (stypes[token_value] != LABEL_T) goto LabelError;
-                if (sflags[token_value] & CHANGE_SFLAG)
-                {   sflags[token_value] &= (~(CHANGE_SFLAG));
-                    assemble_label_no(svals[token_value]);
-                    define_symbol_label(token_value);
-                }
-                else error_named("Duplicate definition of label:", token_text);
-            }
-
-            get_next_token();
-            if ((token_type != SEP_TT) || (token_value != SEMICOLON_SEP))
-            {   ebf_error("';'", token_text);
-                put_token_back(); return;
-            }
-
-            /*  Interesting point of Inform grammar: a statement can only
-                consist solely of a label when it is immediately followed
-                by a "}".                                                    */
-
-            get_next_token();
-            if ((token_type == SEP_TT) && (token_value == CLOSE_BRACE_SEP))
-            {   put_token_back(); return;
-            }
-            /* The following line prevents labels from influencing the positions
-               of sequence points. */
-            statement_debug_location = get_token_location();
-            parse_statement(break_label, continue_label);
-            return;
-        }
-        LabelError: ebf_error("label name", token_text);
-    }
-
     if ((token_type == SEP_TT) && (token_value == HASH_SEP))
     {   parse_directive(TRUE);
         parse_statement(break_label, continue_label); return;
@@ -1880,7 +1897,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  get_next_token();
                  if ((token_type == STATEMENT_TT)
                      && (token_value == UNTIL_CODE))
-                 {   assemble_label_no(ln2);
+                 {   assemble_forward_label_no(ln2);
                      match_open_bracket();
                      AO = parse_expression(CONDITION_CONTEXT);
                      match_close_bracket();
@@ -1888,7 +1905,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  }
                  else error("'do' without matching 'until'");
 
-                 assemble_label_no(ln3);
+                 assemble_forward_label_no(ln3);
                  break;
 
     /*  -------------------------------------------------------------------- */
@@ -1958,7 +1975,7 @@ static void parse_statement_g(int break_label, int continue_label)
                              sequence_point_follows = FALSE;
                              if (!execution_never_reaches_here)
                                  assembleg_jump(ln);
-                             assemble_label_no(ln2);
+                             assemble_forward_label_no(ln2);
                              return;
                          }
                          goto ParseUpdate;
@@ -2071,7 +2088,7 @@ static void parse_statement_g(int break_label, int continue_label)
                      }
                  }
 
-                 assemble_label_no(ln3);
+                 assemble_forward_label_no(ln3);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -2081,6 +2098,7 @@ static void parse_statement_g(int break_label, int continue_label)
         case GIVE_CODE:
                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                           QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"give\" statement");
                  if ((AO.type == LOCALVAR_OT) && (AO.value == 0))
                      onstack = TRUE;
                  else
@@ -2098,15 +2116,12 @@ static void parse_statement_g(int break_label, int continue_label)
                      if ((token_type == SEP_TT)&&(token_value == ARTNOT_SEP))
                          ln = 0;
                      else
-                     {   if ((token_type == SYMBOL_TT)
-                             && (stypes[token_value] != ATTRIBUTE_T))
-                           warning_named("This is not a declared Attribute:",
-                             token_text);
-                         ln = 1;
+                     {   ln = 1;
                          put_token_back();
                      }
                      AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                                QUANTITY_CONTEXT, -1);
+                     check_warn_symbol_type(&AO2, ATTRIBUTE_T, 0, "\"give\" statement");
                      if (runtime_error_checking_switch && (!veneer_mode))
                      {   ln2 = (ln ? RT__ChG_VR : RT__ChGt_VR);
                          if ((AO2.type == LOCALVAR_OT) && (AO2.value == 0)) {
@@ -2153,8 +2168,9 @@ static void parse_statement_g(int break_label, int continue_label)
     /*  -------------------------------------------------------------------- */
 
         case IF_CODE:
-                 flag = FALSE;
+                 flag = FALSE; /* set if there's an "else" */
                  ln2 = 0;
+                 pre_unreach = execution_never_reaches_here;
 
                  match_open_bracket();
                  AO = parse_expression(CONDITION_CONTEXT);
@@ -2172,8 +2188,17 @@ static void parse_statement_g(int break_label, int continue_label)
                      ln = next_label++;
                  }
 
+                 /* The condition */
                  code_generate(AO, CONDITION_CONTEXT, ln);
 
+                 if (!pre_unreach && ln >= 0 && execution_never_reaches_here) {
+                     /* If the condition never falls through to here, then
+                        it was an "if (0)" test. Our convention is to skip
+                        the "not reached" warnings for this case. */
+                     execution_never_reaches_here |= EXECSTATE_NOWARN;
+                 }
+
+                 /* The "if" block */
                  if (ln >= 0) parse_code_block(break_label, continue_label, 0);
                  else
                  {   get_next_token();
@@ -2206,11 +2231,44 @@ static void parse_statement_g(int break_label, int continue_label)
                  }
                  else put_token_back();
 
-                 if (ln >= 0) assemble_label_no(ln);
+                 /* The "else" label (or end of statement, if there is no "else") */
+                 labelexists = FALSE;
+                 if (ln >= 0) labelexists = assemble_forward_label_no(ln);
 
                  if (flag)
-                 {   parse_code_block(break_label, continue_label, 0);
-                     if (ln >= 0) assemble_label_no(ln2);
+                 {
+                     /* If labelexists is false, then we started with
+                        "if (1)". In this case, we don't want a "not
+                        reached" warning on the "else" block. We
+                        temporarily disable the NOWARN flag, and restore it
+                        afterwards. */
+                     int saved_unreach = 0;
+                     if (execution_never_reaches_here && !labelexists) {
+                         saved_unreach = execution_never_reaches_here;
+                         execution_never_reaches_here |= EXECSTATE_NOWARN;
+                     }
+
+                     /* The "else" block */
+                     parse_code_block(break_label, continue_label, 0);
+
+                     if (execution_never_reaches_here && !labelexists) {
+                         if (saved_unreach & EXECSTATE_NOWARN)
+                             execution_never_reaches_here |= EXECSTATE_NOWARN;
+                         else
+                             execution_never_reaches_here &= ~EXECSTATE_NOWARN;
+                     }
+
+                     /* The post-"else" label */
+                     if (ln >= 0) assemble_forward_label_no(ln2);
+                 }
+                 else
+                 {
+                     /* There was no "else". If we're unreachable, then the
+                        statement returned unconditionally, which means 
+                        "if (1) return". Skip warnings. */
+                     if (!pre_unreach && execution_never_reaches_here) {
+                         execution_never_reaches_here |= EXECSTATE_NOWARN;
+                     }
                  }
 
                  return;
@@ -2291,6 +2349,8 @@ static void parse_statement_g(int break_label, int continue_label)
                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
                  AO = code_generate(AO, QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"move\" statement");
+                 check_warn_symbol_type(&AO2, OBJECT_T, CLASS_T, "\"move\" statement");
                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
                      assembleg_call_2(veneer_routine(RT__ChT_VR), AO, AO2,
                          zero_operand);
@@ -2320,8 +2380,8 @@ static void parse_statement_g(int break_label, int continue_label)
                      INITAOTV(&AO, LOCALVAR_OT, token_value);
                  }
                  else if ((token_type == SYMBOL_TT) &&
-                   (stypes[token_value] == GLOBAL_VARIABLE_T)) {
-                     INITAOTV(&AO, GLOBALVAR_OT, svals[token_value]);
+                   (symbols[token_value].type == GLOBAL_VARIABLE_T)) {
+                     INITAOTV(&AO, GLOBALVAR_OT, symbols[token_value].value);
                  }
                  else {
                      ebf_error("'objectloop' variable", token_text);
@@ -2423,7 +2483,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  sequence_point_follows = TRUE;
                  ln = symbol_index("Class", -1);
                  INITAOT(&AO2, CONSTANT_OT);
-                 AO2.value = svals[ln];
+                 AO2.value = symbols[ln].value;
                  AO2.marker = OBJECT_MV;
                  assembleg_store(AO, AO2);
 
@@ -2473,6 +2533,7 @@ static void parse_statement_g(int break_label, int continue_label)
         case REMOVE_CODE:
                  AO = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
+                 check_warn_symbol_type(&AO, OBJECT_T, 0, "\"remove\" statement");
                  if ((runtime_error_checking_switch) && (veneer_mode == FALSE))
                      assembleg_call_1(veneer_routine(RT__ChR_VR), AO,
                          zero_operand);
@@ -2543,8 +2604,9 @@ static void parse_statement_g(int break_label, int continue_label)
                  AO2 = code_generate(parse_expression(QUANTITY_CONTEXT),
                      QUANTITY_CONTEXT, -1);
                  if (is_constant_ot(AO2.type) && AO2.marker == 0) {
+                     /* Compile-time check */
                      if (AO2.value < 0 || AO2.value >= MAX_DYNAMIC_STRINGS) {
-                         memoryerror("MAX_DYNAMIC_STRINGS", MAX_DYNAMIC_STRINGS);
+                         error_max_dynamic_strings(AO2.value);
                      }
                  }
                  get_next_token();
@@ -2629,7 +2691,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  assembleg_store(temp_var1, AO); 
 
                  parse_code_block(ln = next_label++, continue_label, 1);
-                 assemble_label_no(ln);
+                 assemble_forward_label_no(ln);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -2647,7 +2709,7 @@ static void parse_statement_g(int break_label, int continue_label)
                  parse_code_block(ln2, ln, 0);
                  sequence_point_follows = FALSE;
                  assembleg_jump(ln);
-                 assemble_label_no(ln2);
+                 assemble_forward_label_no(ln2);
                  return;
 
     /*  -------------------------------------------------------------------- */
@@ -2681,10 +2743,26 @@ static void parse_statement_g(int break_label, int continue_label)
 
 extern void parse_statement(int break_label, int continue_label)
 {
-  if (!glulx_mode)
-    parse_statement_z(break_label, continue_label);
-  else
-    parse_statement_g(break_label, continue_label);
+    int res;
+    int saved_entire_flag;
+    
+    res = parse_named_label_statements();
+    if (!res)
+        return;
+
+    saved_entire_flag = (execution_never_reaches_here & EXECSTATE_ENTIRE);
+    if (execution_never_reaches_here)
+        execution_never_reaches_here |= EXECSTATE_ENTIRE;
+    if (!glulx_mode)
+        parse_statement_z(break_label, continue_label);
+    else
+        parse_statement_g(break_label, continue_label);
+
+    if (saved_entire_flag)
+        execution_never_reaches_here |= EXECSTATE_ENTIRE;
+    else
+        execution_never_reaches_here &= ~EXECSTATE_ENTIRE;
 }
 
 /* ========================================================================= */