compiler: Fix an issue with scoping rules.
authorTyler J. Stachecki <stachecki.tyler@gmail.com>
Fri, 7 Apr 2017 18:57:51 +0000 (14:57 -0400)
committerTyler J. Stachecki <stachecki.tyler@gmail.com>
Fri, 7 Apr 2017 18:57:51 +0000 (14:57 -0400)
The compiler's IR has no concept of scope. Thus, you could
previously write horrible code such as the following:

```
if (condition) (
  devar magic as i32
) else (
  devar magic as i32
)

assign value to magic
```

and the semantic analyzer would be unable to detect any
problems. This commit adds the minimal amount of scope
information to conditional branches in order to be able
to detect this sort of thing and disallow it.

Signed-off-by: Tyler J. Stachecki <stachecki.tyler@gmail.com>
compiler/nodes.h
compiler/parser.c
compiler/passes/semantic_analysis.c
compiler/printer.c
compiler/variable_ht.c
compiler/variable_ht.h

index f23935e..b8675db 100644 (file)
@@ -102,8 +102,10 @@ struct cen64_compiler_binary_operation_node {
 struct cen64_compiler_branch_node {
   struct cen64_compiler_node *condition;
   enum cen64_branch_type type;
+
   unsigned nt_target_bb;
   unsigned target_bb;
+  unsigned scope_end_bb;
 };
 
 /* Force the compiler node to be 64 bytes in size, regardless of the size */
index 6ebdfbd..2e79665 100644 (file)
@@ -297,6 +297,7 @@ enum cen64_lexer_token parse_function_body(struct cen64_parser *parser,
 
             node->node.branch.condition = expr;
             node->node.branch.type = kBranchConditional;
+            node->node.branch.scope_end_bb = end->line_number_or_id;
             add_node_to_bb(bb, node);
           }
 
@@ -419,6 +420,7 @@ enum cen64_lexer_token parse_function_body(struct cen64_parser *parser,
         node->node.branch.type = kBranchConditional;
         node->node.branch.target_bb = inner_bb->line_number_or_id;
         node->node.branch.nt_target_bb = end->line_number_or_id;
+        node->node.branch.scope_end_bb = end->line_number_or_id;
 
         /* Parse the body of the while loop */
         token = parse_function_body(parser, cunit, namespace,
index 9eda8d7..ad3d4fb 100644 (file)
 #include <string.h>
 
 static int check_body_with_existing_scope(struct cen64_compiler *compiler,
-    struct cen64_compiler_node *node, struct cen64_variable_ht *variables);
+    struct cen64_compiler_node *node, struct cen64_variable_ht *variables,
+    unsigned end_scope_bb);
+
 static enum cen64_lexer_token check_expr_with_existing_scope(
     struct cen64_compiler *compiler, struct cen64_compiler_node *node,
     struct cen64_variable_ht *variables);
 
 static int check_function(struct cen64_compiler *compiler,
     struct cen64_compiler_node *node);
+
 static int check_object(struct cen64_compiler *compiler,
     struct cen64_compiler_node *node);
 
@@ -40,7 +43,8 @@ const struct cen64_compiler_pass_info cen64_compiler_pass_semantic_analysis = {
 };
 
 int check_body_with_existing_scope(struct cen64_compiler *compiler,
-    struct cen64_compiler_node *head, struct cen64_variable_ht *variables) {
+    struct cen64_compiler_node *head, struct cen64_variable_ht *variables,
+    unsigned end_scope_bb) {
   struct cen64_compiler_node *bb, *node, *self, *check;
   struct cen64_compiler_function_node *function;
   enum cen64_lexer_token type;
@@ -49,6 +53,9 @@ int check_body_with_existing_scope(struct cen64_compiler *compiler,
     if (compiler->bb_flag_list[bb->line_number_or_id])
       return 0;
 
+    if (end_scope_bb && bb->line_number_or_id == end_scope_bb)
+      return 0;
+
     compiler->bb_flag_list[bb->line_number_or_id] = 1;
 
     for (node = bb->node.basic_block.head; node != NULL; node = node->next) {
@@ -67,15 +74,25 @@ int check_body_with_existing_scope(struct cen64_compiler *compiler,
             scoped_variables = cen64_variable_ht_create(
                 &compiler->arena_alloc, variables);
 
-            if (check_body_with_existing_scope(
-                compiler, bb, scoped_variables)) {
+            if (check_body_with_existing_scope(compiler,
+                bb, scoped_variables, node->node.branch.scope_end_bb)) {
+              return 1;
+            }
+
+            bb = compiler->bb_list[node->node.branch.target_bb];
+            cen64_variable_ht_reset(scoped_variables, variables);
+
+            if (check_body_with_existing_scope(compiler,
+                bb, scoped_variables, node->node.branch.scope_end_bb)) {
               return 1;
             }
 
             cen64_variable_ht_destroy(scoped_variables, &compiler->arena_alloc);
+            bb = compiler->bb_list[node->node.branch.scope_end_bb];
+          } else {
+            bb = compiler->bb_list[node->node.branch.target_bb];
           }
 
-          bb = compiler->bb_list[node->node.branch.target_bb];
           assert(node->next == NULL && "Branch is not last node in block");
           continue;
 
@@ -293,7 +310,7 @@ int check_function(struct cen64_compiler *compiler,
   node = node->node.function_entry_exit.entry;
 
   memset(compiler->bb_flag_list, 0, compiler->cunit.bb_count);
-  status = check_body_with_existing_scope(compiler, node, variables);
+  status = check_body_with_existing_scope(compiler, node, variables, 0);
 
   for (bb = node; bb != NULL; bb = bb->next) {
     if (!compiler->bb_flag_list[bb->line_number_or_id]) {
index 6f6f65e..262d1f8 100644 (file)
@@ -161,9 +161,13 @@ void print_function_body(FILE *stream, struct cen64_compiler *compiler,
 
           switch (temp->node.branch.type) {
             case kBranchConditional:
-              fprintf(stream, "Branch [type=Conditional, target=%u(T)/%u(NT), "
-                  "condition=", temp->node.branch.target_bb,
-                  temp->node.branch.nt_target_bb);
+              fprintf(stream, "Branch [type=Conditional, target=%u(T)/%u(NT), ",
+                  temp->node.branch.target_bb, temp->node.branch.nt_target_bb);
+
+              if (temp->node.branch.scope_end_bb)
+                fprintf(stream, "end=%u, ", temp->node.branch.scope_end_bb);
+
+              fprintf(stream, "condition=");
 
               assert(temp->node.branch.condition->prev == NULL &&
                   "prev field in expression head in function body is non-NULL");
index ab19e78..499d40d 100644 (file)
@@ -52,7 +52,7 @@ struct cen64_variable_ht *cen64_variable_ht_create(
       cen64_arena_alloc(arena_alloc, sizeof(*parent_ht));
 
   if (parent_ht != NULL)
-    memcpy(ht, parent_ht, sizeof(*ht));
+    cen64_variable_ht_reset(ht, parent_ht);
 
   return ht;
 }
@@ -88,6 +88,11 @@ const struct cen64_compiler_variable_node *cen64_variable_ht_lookup(
   return NULL;
 }
 
+void cen64_variable_ht_reset(struct cen64_variable_ht *ht,
+    const struct cen64_variable_ht *parent_ht) {
+  memcpy(ht, parent_ht, sizeof(*ht));
+}
+
 /* djb2; can replace with whatever if it's inadequate... */
 static unsigned long hash(const char *str) {
   unsigned long hash = 5381;
index 697bfd6..e367c26 100644 (file)
@@ -58,6 +58,9 @@ void cen64_variable_ht_destroy(struct cen64_variable_ht *ht,
 void cen64_variable_ht_insert(struct cen64_variable_ht *ht,
     struct cen64_compiler_variable_node *variable);
 
+void cen64_variable_ht_reset(struct cen64_variable_ht *ht,
+    const struct cen64_variable_ht *parent_ht);
+
 pure_attribute const struct cen64_compiler_variable_node *cen64_variable_ht_lookup(
     const struct cen64_variable_ht *ht, const char *name);