Răsfoiți Sursa

Merge branch 'earlyreturn'

Simon Polstra 10 ani în urmă
părinte
comite
152a367a82

+ 2 - 2
bin/README

@@ -60,13 +60,13 @@ Some other remarks about the compiler:
 
 
 - the "-upto ..." argument makes the compiler stop after a certain phase and
 - the "-upto ..." argument makes the compiler stop after a certain phase and
   print the AST (or assembly) at that point. E.g. when you are implementing
   print the AST (or assembly) at that point. E.g. when you are implementing
-  context analysis and want to compare your output to that of the refrence
+  context analysis and want to compare your output to that of the reference
   compiler, use "-upto context" or "-upto context -v 2".
   compiler, use "-upto context" or "-upto context -v 2".
 
 
 - The generated assembly code contains comments that shows to which CiviC code
 - The generated assembly code contains comments that shows to which CiviC code
   it corresponds. This can be used for relating instructions to AST nodes
   it corresponds. This can be used for relating instructions to AST nodes
   during debugging, and we recommend you do the same in your own compiler
   during debugging, and we recommend you do the same in your own compiler
   (although it is not required). Note that the last optimization phase
   (although it is not required). Note that the last optimization phase
-  (peephpole optimization) strips these comments, so you will need to pass "-v
+  (peephole optimization) strips these comments, so you will need to pass "-v
   2" or -noopt to actually see them. You can also disable them completely by
   2" or -noopt to actually see them. You can also disable them completely by
   passing "-v 0".
   passing "-v 0".

+ 8 - 7
parser.mly

@@ -130,13 +130,8 @@ dimlist:
   { Dim (name, loc $startpos(name) $endpos(name)) :: head }
   { Dim (name, loc $startpos(name) $endpos(name)) :: head }
 
 
 fun_body:
 fun_body:
-  | llist(var_dec) local_fun_dec* statement* loption(return_statement)
-  { VarDecs (List.rev $1) :: LocalFuns $2 :: ($3 @ $4) }
-
-return_statement:
-  (* return statement: use location of return value *)
-  | RETURN value=expr SEMICOL
-  { [Return (value, loc $startpos(value) $endpos(value))] }
+  | llist(var_dec) local_fun_dec* statement*
+  { VarDecs (List.rev $1) :: LocalFuns $2 :: $3 }
 
 
 (* function: use location of function name *)
 (* function: use location of function name *)
 local_fun_dec:
 local_fun_dec:
@@ -195,6 +190,12 @@ statement:
   { let loc = loc $startpos(cnt) $endpos(cnt) in
   { let loc = loc $startpos(cnt) $endpos(cnt) in
     For (cnt, start, stop, step, Block body, loc) }
     For (cnt, start, stop, step, Block body, loc) }
 
 
+  (* return statement: use location of return value *)
+  | RETURN value=expr SEMICOL
+  { Return (Some value, loc $startpos(value) $endpos(value)) }
+  | ret=RETURN SEMICOL
+  { Return (None, loc $startpos(ret) $endpos(ret)) }
+
 block:
 block:
   | LBRACE stats=statement* RBRACE  { stats }
   | LBRACE stats=statement* RBRACE  { stats }
   | stat=statement                  { [stat] }
   | stat=statement                  { [stat] }

+ 15 - 3
phases/assemble.ml

@@ -57,6 +57,16 @@ let assemble program =
        ImportFun (name, ret_type, List.map typeof params)]
        ImportFun (name, ret_type, List.map typeof params)]
 
 
     | FunDef (export, ret_type, name, params, body, _) ->
     | FunDef (export, ret_type, name, params, body, _) ->
+      let last_if_return body =
+        match List.fold_left (fun _ x -> x) DummyNode (block_body body) with
+        | Return _ as ret -> Some ret
+        | _               -> None
+      in
+      let add_explicit_return body =
+        match last_if_return body with
+        | Some _ -> body
+        | None   -> Block (block_body body @ [Return (None, [])])
+      in
       let label = labelof node in
       let label = labelof node in
       begin
       begin
         if export
         if export
@@ -68,8 +78,7 @@ let assemble program =
        Label label] @
        Label label] @
       (match indexof node with 0 -> [] | n -> [RtnEnter n]) @
       (match indexof node with 0 -> [] | n -> [RtnEnter n]) @
       (trav_all params) @
       (trav_all params) @
-      (trav body) @
-      (match ret_type with Void -> [Ret Void] | _ -> []) @
+      (trav (add_explicit_return body)) @
       [EmptyLine] @
       [EmptyLine] @
       (traverse_localfuns body)
       (traverse_localfuns body)
 
 
@@ -89,7 +98,10 @@ let assemble program =
       let store = Store (typeof dec, get_scope node dec, indexof dec) in
       let store = Store (typeof dec, get_scope node dec, indexof dec) in
       trav value @ [InlineComment (store, node2str node)]
       trav value @ [InlineComment (store, node2str node)]
 
 
-    | Return (value, _) ->
+    | Return (None, _) ->
+      [InlineComment (Ret Void, node2str node)]
+
+    | Return (Some value, _) ->
       trav value @ [InlineComment (Ret (typeof value), node2str node)]
       trav value @ [InlineComment (Ret (typeof value), node2str node)]
 
 
     | If (cond, body, _) ->
     | If (cond, body, _) ->

+ 2 - 4
phases/context.ml

@@ -123,6 +123,8 @@ let analyse do_rename program =
     | Program (decls, ann) ->
     | Program (decls, ann) ->
       Program (Block decls |> trav |> trav_funs scope |> block_body, ann)
       Program (Block decls |> trav |> trav_funs scope |> block_body, ann)
 
 
+    (* Do not traverse into external function declarations, since their
+     * parameters must not be added to the namespace *)
     | FunDec _ | FunDef _ -> node
     | FunDec _ | FunDef _ -> node
 
 
     | GlobalDef (export, ctype, name, init, ann) ->
     | GlobalDef (export, ctype, name, init, ann) ->
@@ -223,10 +225,6 @@ let analyse do_rename program =
     | Allocate (dec, dims, ann) ->
     | Allocate (dec, dims, ann) ->
       Allocate (Hashtbl.find vars (nameof dec), List.map trav dims, ann)
       Allocate (Hashtbl.find vars (nameof dec), List.map trav dims, ann)
 
 
-    (* Do not traverse into external function declarations, since their
-     * parameters must not be added to the namespace *)
-    | FunDec _ -> node
-
     | _ -> traverse_unit trav node
     | _ -> traverse_unit trav node
 
 
   and trav_funs scope = function
   and trav_funs scope = function

+ 1 - 1
phases/desug.ml

@@ -2,7 +2,7 @@ open Printf
 open Types
 open Types
 open Util
 open Util
 
 
-(* Generate new variables for array dimensions, to avoid re-evalutation when
+(* Generate new variables for array dimensions, to avoid re-evaluation when
  * array dimensions are used (e.g., after array dimension reduction). *)
  * array dimensions are used (e.g., after array dimension reduction). *)
 let move_array_dims node =
 let move_array_dims node =
   let patch_dims basename values make_decs make_dimname =
   let patch_dims basename values make_decs make_dimname =

+ 20 - 18
phases/typecheck.ml

@@ -172,31 +172,33 @@ let rec typecheck node =
   | Dim (name, ann) ->
   | Dim (name, ann) ->
     (Dim (name, Type Int :: ann), [])
     (Dim (name, Type Int :: ann), [])
 
 
-  (* Void functions may have no return statement, other functions must have a
-   * return statement of valid type *)
+  (* Void functions may have a return statement without a value, other functions
+   * must have a return statement of a valid type *)
   | FunDef (export, ret_type, name, params, body, ann) ->
   | FunDef (export, ret_type, name, params, body, ann) ->
     let params, perr = err_map typecheck params in
     let params, perr = err_map typecheck params in
     let body, berr = typecheck body in
     let body, berr = typecheck body in
-    let rec find_return = function
-      | []                         -> None
-      | [Return (value, _) as ret] -> Some (ret, typeof value)
-      | hd :: tl                   -> find_return tl
+    let rec check_types = function
+      | Return (Some _, _) as ret when ret_type = Void ->
+        ret, [NodeMsg (ret, "void function should not have a return value")]
+
+      | Return (Some value, _) as ret when typeof value <> ret_type ->
+        ret, [NodeMsg (ret, sprintf
+          "function \"%s\" has return type %s, got %s"
+          name (type2str ret_type) (type2str (typeof value)))]
+
+      | Return (None, _) as ret when ret_type <> Void ->
+        ret, [NodeMsg (ret, sprintf "expected return value of type %s"
+          (type2str ret_type))]
+
+      | node -> traverse_list check_types node
     in
     in
     let err =
     let err =
-      match (ret_type, find_return (block_body body)) with
-      | (Void, Some (ret, _)) ->
-        [NodeMsg (ret, "void function should not have a return value")]
-
-      | ((Bool | Int | Float), None) ->
+      snd (check_types body) @
+      match last_if_return body with
+      | None when ret_type <> Void ->
         [NodeMsg (node, sprintf
         [NodeMsg (node, sprintf
-          "expected return value of type %s for function \"%s\""
+          "function \"%s\" must end with a return of type %s"
           (type2str ret_type) name)]
           (type2str ret_type) name)]
-
-      | ((Bool | Int | Float), Some (ret, t)) when t != ret_type ->
-        [NodeMsg (ret, sprintf
-          "function \"%s\" has return type %s, got %s"
-          name (type2str ret_type) (type2str t))]
-
       | _ -> []
       | _ -> []
     in
     in
     (FunDef (export, ret_type, name, params, body, ann), perr @ berr @ err)
     (FunDef (export, ret_type, name, params, body, ann), perr @ berr @ err)

+ 4 - 2
stringify.ml

@@ -93,8 +93,10 @@ and node2str node =
     name ^ "[" ^ concat ", " dims ^ "] = " ^ str value ^ ";"
     name ^ "[" ^ concat ", " dims ^ "] = " ^ str value ^ ";"
   | Expr expr ->
   | Expr expr ->
     str expr ^ ";"
     str expr ^ ";"
-  | Return (value, _) ->
-    "return " ^ (str value) ^ ";"
+  | Return (None, _) ->
+    "return;"
+  | Return (Some value, _) ->
+    "return " ^ str value ^ ";"
   | If (cond, body, _) ->
   | If (cond, body, _) ->
     "if (" ^ str cond ^ ") " ^ str body
     "if (" ^ str cond ^ ") " ^ str body
   | IfElse (cond, true_body, false_body, _) ->
   | IfElse (cond, true_body, false_body, _) ->

+ 5 - 0
test/basic/check_error/different_early_return.cvc

@@ -0,0 +1,5 @@
+void foo(int p) {
+    if (p > 1)
+        return;
+    return 1.0;
+}

+ 7 - 0
test/basic/check_error/different_early_return2.cvc

@@ -0,0 +1,7 @@
+int foo(int p) {
+    if (p > 1)
+        return 1;
+    else
+        return 1.1;
+    return 0;
+}

+ 4 - 0
test/basic/check_error/invalid_return_voidfunc.cvc

@@ -0,0 +1,4 @@
+void f()
+{
+  return true;
+}

+ 2 - 0
test/basic/check_error/no_return.cvc

@@ -0,0 +1,2 @@
+int baz() {
+}

+ 4 - 0
test/basic/check_error/only_early_return.cvc

@@ -0,0 +1,4 @@
+int foo(int p) {
+    if (p > 1)
+        return 1;
+}

+ 4 - 0
test/basic/check_error/void_return_nonvoidfunc.cvc

@@ -0,0 +1,4 @@
+bool f()
+{
+  return;
+}

+ 6 - 0
test/basic/check_success/early_return.cvc

@@ -0,0 +1,6 @@
+void foo(int p) {
+    int i = 1;
+    if (p > 1)
+        return;
+    i = 2;
+}

+ 2 - 0
test/basic/check_success/no_return.cvc

@@ -0,0 +1,2 @@
+void baz() {
+}

+ 3 - 0
test/basic/check_success/void_return.cvc

@@ -0,0 +1,3 @@
+void baz() {
+    return;
+}

+ 19 - 0
test/basic/functional/deadcode.cvc

@@ -0,0 +1,19 @@
+extern void printInt( int val);
+extern void printSpaces( int num);
+extern void printNewlines( int num);
+
+export int main()
+{
+    int m=10;
+
+    for(int i=0,m)
+    {
+            printInt(i);
+            printSpaces(1);
+    }
+    printNewlines(1);
+    return 0;
+    printInt(112);
+    printNewlines(1);
+    return 1;
+}

+ 1 - 0
test/basic/functional/deadcode.out

@@ -0,0 +1 @@
+0 1 2 3 4 5 6 7 8 9 

+ 24 - 0
test/basic/functional/early_return.cvc

@@ -0,0 +1,24 @@
+extern void printInt(int val);
+extern void printNewlines(int num);
+
+void foo(int p) {
+    int i = 1;
+    if (p < 2)
+        return;
+    i = -p;
+    printInt(i);
+    printNewlines(1);
+}
+
+void bar() {
+    return;
+    printInt(1);
+    printNewlines(1);
+}
+
+export int main() {
+    foo(0);
+    foo(2);  // prints -2
+    bar();
+    return 0;
+}

+ 1 - 0
test/basic/functional/early_return.out

@@ -0,0 +1 @@
+-2

+ 1 - 0
test/basic/functional/factorial.cvc

@@ -18,6 +18,7 @@ int fac(int num) {
 }
 }
 
 
 export int main() {
 export int main() {
+    printInt(fac(0));  printNewlines(1);
     printInt(fac(3));  printNewlines(1);
     printInt(fac(3));  printNewlines(1);
     printInt(fac(5));  printNewlines(1);
     printInt(fac(5));  printNewlines(1);
     printInt(fac(10)); printNewlines(1);
     printInt(fac(10)); printNewlines(1);

+ 1 - 0
test/basic/functional/factorial.out

@@ -1,3 +1,4 @@
+1
 6
 6
 120
 120
 3628800
 3628800

+ 27 - 0
test/basic/functional/factorial_early_return.cvc

@@ -0,0 +1,27 @@
+extern void printInt(int val);
+extern void printNewlines(int num);
+
+int fac(int num) {
+    int input = num;
+    int output = 1;
+    int i = 1;
+
+    if (input == 0) {
+        return 1;
+    } else {
+        while (i <= input) {
+            output = output * i;
+            i = i + 1;
+        }
+    }
+    return output;
+}
+
+export int main() {
+    printInt(fac(0));  printNewlines(1);
+    printInt(fac(3));  printNewlines(1);
+    printInt(fac(5));  printNewlines(1);
+    printInt(fac(10)); printNewlines(1);
+    return 0;
+}
+

+ 4 - 0
test/basic/functional/factorial_early_return.out

@@ -0,0 +1,4 @@
+1
+6
+120
+3628800

+ 20 - 0
test/basic/functional/recursion_early_return.cvc

@@ -0,0 +1,20 @@
+extern void printInt(int val);
+extern void printNewlines(int num);
+extern void printSpaces(int num);
+
+int factorial(int i) {
+    if (i == 0)
+        return 1;
+
+    return i * factorial(i - 1);
+}
+
+export int main() {
+    for (int i = 0, 6) {
+        printInt(factorial(i));
+        printSpaces(1);
+    }
+    printNewlines(1);
+
+    return 0;
+}

+ 1 - 0
test/basic/functional/recursion_early_return.out

@@ -0,0 +1 @@
+1 1 2 6 24 120 

+ 2 - 2
types.mli

@@ -87,8 +87,8 @@ and node =
   | For of string * node * node * node * node * ann
   | For of string * node * node * node * node * ann
     (** counter, start, stop, step, body
     (** counter, start, stop, step, body
         [for (int <counter> = <start>, <stop>[, <step>]) <body>] *)
         [for (int <counter> = <start>, <stop>[, <step>]) <body>] *)
-  | Return of node * ann
-    (** Return value [return <value>;] *)
+  | Return of node option * ann
+    (** Return value [return[ <value>];] *)
   | Expr of node
   | Expr of node
     (** Expression statement [<expr>;] *)
     (** Expression statement [<expr>;] *)
   | Block of node list
   | Block of node list

+ 7 - 3
util.ml

@@ -146,9 +146,9 @@ let rec traverse u ( *: ) trav node =
     let dims, res_dims = trav_all dims in
     let dims, res_dims = trav_all dims in
     let value, res_value = trav value in
     let value, res_value = trav value in
     (VarLet (dec, Some dims, value, ann), res_dims *: res_value)
     (VarLet (dec, Some dims, value, ann), res_dims *: res_value)
-  | Return (value, ann) ->
+  | Return (Some value, ann) ->
     let value, res_value = trav value in
     let value, res_value = trav value in
-    (Return (value, ann), res_value)
+    (Return (Some value, ann), res_value)
   | If (cond, body, ann) ->
   | If (cond, body, ann) ->
     let cond, res_cond = trav cond in
     let cond, res_cond = trav cond in
     let body, res_body = trav body in
     let body, res_body = trav body in
@@ -464,9 +464,13 @@ let prerr_loc_msg loc msg =
 
 
 let block_body = function
 let block_body = function
   | Block nodes -> nodes
   | Block nodes -> nodes
-  (*| node -> [node]*)
   | _ -> raise InvalidNode
   | _ -> raise InvalidNode
 
 
+let last_if_return body =
+  match List.fold_left (fun _ x -> x) DummyNode (block_body body) with
+  | Return _ as ret -> Some ret
+  | _               -> None
+
 let basetypeof node = match typeof node with
 let basetypeof node = match typeof node with
   | ArrayDims (ctype, _)
   | ArrayDims (ctype, _)
   | Array ctype
   | Array ctype

+ 3 - 0
util.mli

@@ -70,6 +70,9 @@ val flatten_blocks : node list -> node list
 (** Extract the list of child nodes from a [Block] node. *)
 (** Extract the list of child nodes from a [Block] node. *)
 val block_body : node -> node list
 val block_body : node -> node list
 
 
+(** Get the last node of a [Block] node if it is a [Return]. *)
+val last_if_return : node -> node option
+
 (** {2 AST node annotations} *)
 (** {2 AST node annotations} *)
 
 
 (** Add a single annotation to a node. *)
 (** Add a single annotation to a node. *)