Procházet zdrojové kódy

Merge branch 'earlyreturn'

Simon Polstra před 10 roky
rodič
revize
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
   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".
 
 - The generated assembly code contains comments that shows to which CiviC code
   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
   (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
   passing "-v 0".

+ 8 - 7
parser.mly

@@ -130,13 +130,8 @@ dimlist:
   { Dim (name, loc $startpos(name) $endpos(name)) :: head }
 
 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 *)
 local_fun_dec:
@@ -195,6 +190,12 @@ statement:
   { let loc = loc $startpos(cnt) $endpos(cnt) in
     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:
   | LBRACE stats=statement* RBRACE  { stats }
   | stat=statement                  { [stat] }

+ 15 - 3
phases/assemble.ml

@@ -57,6 +57,16 @@ let assemble program =
        ImportFun (name, ret_type, List.map typeof params)]
 
     | 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
       begin
         if export
@@ -68,8 +78,7 @@ let assemble program =
        Label label] @
       (match indexof node with 0 -> [] | n -> [RtnEnter n]) @
       (trav_all params) @
-      (trav body) @
-      (match ret_type with Void -> [Ret Void] | _ -> []) @
+      (trav (add_explicit_return body)) @
       [EmptyLine] @
       (traverse_localfuns body)
 
@@ -89,7 +98,10 @@ let assemble program =
       let store = Store (typeof dec, get_scope node dec, indexof dec) in
       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)]
 
     | If (cond, body, _) ->

+ 2 - 4
phases/context.ml

@@ -123,6 +123,8 @@ let analyse do_rename program =
     | Program (decls, 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
 
     | GlobalDef (export, ctype, name, init, ann) ->
@@ -223,10 +225,6 @@ let analyse do_rename program =
     | Allocate (dec, 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
 
   and trav_funs scope = function

+ 1 - 1
phases/desug.ml

@@ -2,7 +2,7 @@ open Printf
 open Types
 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). *)
 let move_array_dims node =
   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, 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) ->
     let params, perr = err_map typecheck params 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
     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
-          "expected return value of type %s for function \"%s\""
+          "function \"%s\" must end with a return of type %s"
           (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
     (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 ^ ";"
   | Expr expr ->
     str expr ^ ";"
-  | Return (value, _) ->
-    "return " ^ (str value) ^ ";"
+  | Return (None, _) ->
+    "return;"
+  | Return (Some value, _) ->
+    "return " ^ str value ^ ";"
   | If (cond, body, _) ->
     "if (" ^ str cond ^ ") " ^ str 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() {
+    printInt(fac(0));  printNewlines(1);
     printInt(fac(3));  printNewlines(1);
     printInt(fac(5));  printNewlines(1);
     printInt(fac(10)); printNewlines(1);

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

@@ -1,3 +1,4 @@
+1
 6
 120
 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
     (** 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
     (** Expression statement [<expr>;] *)
   | 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 value, res_value = trav value in
     (VarLet (dec, Some dims, value, ann), res_dims *: res_value)
-  | Return (value, ann) ->
+  | Return (Some value, ann) ->
     let value, res_value = trav value in
-    (Return (value, ann), res_value)
+    (Return (Some value, ann), res_value)
   | If (cond, body, ann) ->
     let cond, res_cond = trav cond in
     let body, res_body = trav body in
@@ -464,9 +464,13 @@ let prerr_loc_msg loc msg =
 
 let block_body = function
   | Block nodes -> nodes
-  (*| node -> [node]*)
   | _ -> 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
   | ArrayDims (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. *)
 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} *)
 
 (** Add a single annotation to a node. *)