Re-introduce definite return analysis.
authorDavid Anderson <dvander@alliedmods.net>
Sat Dec 22 19:48:43 2012 -0800 (2012-12-22)
changeset 2322b8512340180
parent 231 6030e0b46fe2
child 233 bbb498ccb4ac
Re-introduce definite return analysis.
include/keima.h
src/API.cpp
src/Messages.tbl
src/Stack.cpp
src/compiler/CompileContext.cpp
src/compiler/SemanticAnalysis.cpp
src/compiler/SemanticAnalysis.h
src/main.cpp
tests/basic/precall-void-used.out
tests/basic/precall-void-used.sp
tests/basic/return-analyze-for-1.out
tests/basic/return-analyze-for-1.sp
tests/basic/return-analyze-for-2.out
tests/basic/return-analyze-for-2.sp
tests/basic/return-analyze-for-3.err
tests/basic/return-analyze-for-3.sp
tests/basic/return-analyze-for-4.err
tests/basic/return-analyze-for-4.sp
tests/basic/return-analyze-for-5.out
tests/basic/return-analyze-for-5.sp
tests/basic/return-analyze-if-1.out
tests/basic/return-analyze-if-1.sp
tests/basic/return-analyze-if-2.err
tests/basic/return-analyze-if-2.sp
tests/basic/return-analyze-if-3.out
tests/basic/return-analyze-if-3.sp
tests/basic/return-analyze-if-4.err
tests/basic/return-analyze-if-4.sp
tests/basic/return-analyze-if-5.out
tests/basic/return-analyze-if-5.sp
tests/prototype/precall-void-used.sp
tests/prototype/return-analyze-for-1.sp
tests/prototype/return-analyze-for-2.sp
tests/prototype/return-analyze-for-3.sp
tests/prototype/return-analyze-for-4.sp
tests/prototype/return-analyze-for-5.sp
tests/prototype/return-analyze-if-1.sp
tests/prototype/return-analyze-if-2.sp
tests/prototype/return-analyze-if-3.sp
tests/prototype/return-analyze-if-4.sp
tests/prototype/return-analyze-if-5.sp
tests/test.py
     1.1 --- a/include/keima.h	Sat Dec 22 18:25:31 2012 -0800
     1.2 +++ b/include/keima.h	Sat Dec 22 19:48:43 2012 -0800
     1.3 @@ -467,7 +467,7 @@
     1.4  /*
     1.5   * Compiles a file and returns a Module.
     1.6   */
     1.7 -extern Local<Module> Compile(const char *path);
     1.8 +extern bool Compile(const char *path);
     1.9  
    1.10  class Internals
    1.11  {
     2.1 --- a/src/API.cpp	Sat Dec 22 18:25:31 2012 -0800
     2.2 +++ b/src/API.cpp	Sat Dec 22 19:48:43 2012 -0800
     2.3 @@ -91,7 +91,7 @@
     2.4      return Ke::Local<To>(reinterpret_cast<To *>(Ke::HandleScope::CreateHandle(from)));
     2.5  }
     2.6  
     2.7 -ke::Module *
     2.8 +bool
     2.9  InternalCompile(const char *path)
    2.10  {
    2.11      PoolScope poolScope(ZONE()->pool());
    2.12 @@ -102,36 +102,20 @@
    2.13      unit->path_ = path;
    2.14  
    2.15      if (!cc.addTranslationUnit(unit))
    2.16 -        return NULL;
    2.17 +        return false;
    2.18  
    2.19      if (!cc.compile())
    2.20 -        return NULL;
    2.21 -
    2.22 -    return NULL;
    2.23 +        return false;
    2.24  
    2.25 -#if 0
    2.26 -    ke::Local<ke::Module> module(ZONE(),  CompileFromSource(cc, path, bytes, length));
    2.27 -    delete [] bytes;
    2.28 -    if (!module)
    2.29 -        return NULL;
    2.30 -
    2.31 -    ke::Local<ke::String> pathstr(ZONE(), ke::String::New(ZONE(), path, Heap::Tenure_Old));
    2.32 -    if (!pathstr)
    2.33 -        return NULL;
    2.34 -
    2.35 -    module->setPath(pathstr);
    2.36 -    return module;
    2.37 -#endif
    2.38 -    return NULL;
    2.39 +    return true;
    2.40  }
    2.41  
    2.42  // This is split out from InternalCompile so the RootScope doesn't conflict
    2.43  // with making a return handle.
    2.44 -Ke::Local<Ke::Module>
    2.45 +bool
    2.46  Ke::Compile(const char *path)
    2.47  {
    2.48 -    ke::Local<ke::Module> module(ZONE(), InternalCompile(path));
    2.49 -    return ToLocal<ke::Module, Ke::Module>(module);
    2.50 +    return InternalCompile(path);
    2.51  }
    2.52  
    2.53  void
     3.1 --- a/src/Messages.tbl	Sat Dec 22 18:25:31 2012 -0800
     3.2 +++ b/src/Messages.tbl	Sat Dec 22 19:48:43 2012 -0800
     3.3 @@ -53,9 +53,9 @@
     3.4  MSG(NativeNotBound,                 TypeError,              "attempted to call unbound native \"%s\"")
     3.5  MSG(AllocationOverflow,             SystemError,            "allocation request overflowed")
     3.6  MSG(TooMuchRecursion,               SystemError,            "stack overflow: too much recursion")
     3.7 -MSG(MissingReturnValue,             SyntaxError,            "tagged function must return a value")
     3.8 +MSG(MissingReturnValue,             SyntaxError,            "function must return a value")
     3.9  MSG(NotAllPathsReturnValue,         SyntaxError,            "not all paths through function return a value")
    3.10 -MSG(UsedVoidReturn,                 SyntaxError,            "return value expected, but function does not return a value")
    3.11 +MSG(UsedVoidReturn,                 SyntaxError,            "value returned, but function is void")
    3.12  MSG(FunctionDoesNotReturnValue,     SyntaxError,            "cannot use the result of a function with no return value")
    3.13  MSG(IdentifierNotFound,             SyntaxError,            "identifier '%s' could not be found")
    3.14  MSG(TypeMismatchDuringInvoke,       TypeError,              "incoming type '%s' does not match signature type '%s'")
     4.1 --- a/src/Stack.cpp	Sat Dec 22 18:25:31 2012 -0800
     4.2 +++ b/src/Stack.cpp	Sat Dec 22 19:48:43 2012 -0800
     4.3 @@ -201,15 +201,15 @@
     4.4      top_.fp = top_.fp->prev();
     4.5  }
     4.6  
     4.7 -void
     4.8 -Frame::enterLoop(Zone *zone)
     4.9 -{
    4.10 -    assert(loopDepth_ < code_->maxLoopDepth());
    4.11 -    loopStack()[loopDepth_] = zone->heap().lifoPosition();
    4.12 -    loopDepth_++;
    4.13 -}
    4.14 -
    4.15 -void
    4.16 +void
    4.17 +Frame::enterLoop(Zone *zone)
    4.18 +{
    4.19 +    assert(loopDepth_ < code_->maxLoopDepth());
    4.20 +    loopStack()[loopDepth_] = zone->heap().lifoPosition();
    4.21 +    loopDepth_++;
    4.22 +}
    4.23 +
    4.24 +void
    4.25  Frame::leaveLoop(Zone *zone)
    4.26  {
    4.27      assert(loopDepth_ > 0);
     5.1 --- a/src/compiler/CompileContext.cpp	Sat Dec 22 18:25:31 2012 -0800
     5.2 +++ b/src/compiler/CompileContext.cpp	Sat Dec 22 19:48:43 2012 -0800
     5.3 @@ -239,6 +239,9 @@
     5.4          if (!zone_->stack().pushModuleFrame(unit->module_, args))
     5.5              assert(false);
     5.6  
     5.7 +        if (failures_)
     5.8 +            return false;
     5.9 +
    5.10          ke::LocalSlot slot;
    5.11          if (!Interpret(zone_, NULL, &slot))
    5.12              assert(false);
     6.1 --- a/src/compiler/SemanticAnalysis.cpp	Sat Dec 22 18:25:31 2012 -0800
     6.2 +++ b/src/compiler/SemanticAnalysis.cpp	Sat Dec 22 19:48:43 2012 -0800
     6.3 @@ -79,7 +79,8 @@
     6.4      emitter_(pool_),
     6.5      module_(parent->module_),
     6.6      fun_(fun),
     6.7 -    loop_(NULL)
     6.8 +    loop_(NULL),
     6.9 +    control_(NULL)
    6.10  {
    6.11  
    6.12  }
    6.13 @@ -167,8 +168,21 @@
    6.14      }
    6.15  
    6.16      AutoEnterScope varScope(&scope_, stmt->varScope());
    6.17 +    ControlStatus status = visitForControl(stmt->body());
    6.18  
    6.19 -    stmt->body()->accept(this);
    6.20 +    if (!fun_->returnType()->isVoid()) {
    6.21 +        // If the function is not void, report if not all paths return a value.
    6.22 +        // All other incorrect return cases have been caught already.
    6.23 +        if (status.returns() == Returns_Sometimes) {
    6.24 +            cc_.reportError(stmt->pos(), Message_NotAllPathsReturnValue);
    6.25 +            return NULL;
    6.26 +        }
    6.27 +
    6.28 +        if (status.returns() == Returns_Never) {
    6.29 +            cc_.reportError(stmt->pos(), Message_MissingReturnValue);
    6.30 +            return NULL;
    6.31 +        }
    6.32 +    }
    6.33  
    6.34      Local<Code> code(zone_, __ finish());
    6.35      if (!code)
    6.36 @@ -217,28 +231,60 @@
    6.37  }
    6.38  
    6.39  void
    6.40 +SemanticAnalysis::ControlScope::merge(const ControlStatus &left, const ControlStatus &right)
    6.41 +{
    6.42 +    // If both sides terminate in a return, we consider this a return as well.
    6.43 +    if (left.returns() == Returns_Always && right.returns() == Returns_Always) {
    6.44 +        terminate(Returns_Always);
    6.45 +        return;
    6.46 +    }
    6.47 +
    6.48 +    ReturnStatus status = (left.returns() == Returns_Never && right.returns() == Returns_Never)
    6.49 +                          ? Returns_Never
    6.50 +                          : Returns_Sometimes;
    6.51 +
    6.52 +    if (left.terminates() && right.terminates())
    6.53 +        terminate(status);
    6.54 +    else
    6.55 +        returns(status);
    6.56 +}
    6.57 +
    6.58 +SemanticAnalysis::ControlStatus
    6.59 +SemanticAnalysis::visitForControl(Statement *statement)
    6.60 +{
    6.61 +    ControlScope scope(&control_);
    6.62 +    statement->accept(this);
    6.63 +    return scope.status();
    6.64 +}
    6.65 +
    6.66 +void
    6.67  SemanticAnalysis::visitIfStatement(IfStatement *node)
    6.68  {
    6.69      Label join;
    6.70  
    6.71 +    ControlStatus trueBranch;
    6.72 +    ControlStatus falseBranch(false, Returns_Never);
    6.73 +
    6.74      if (!node->ifFalse()) {
    6.75          Label success;
    6.76  
    6.77          visitForTest(node->condition(), &success, &join, &success);
    6.78          __ bind(&success);
    6.79 -        node->ifTrue()->accept(this);
    6.80 +        trueBranch = visitForControl(node->ifTrue());
    6.81      } else {
    6.82          Label success, next;
    6.83  
    6.84          visitForTest(node->condition(), &success, &next, &success);
    6.85          __ bind(&success);
    6.86 -        node->ifTrue()->accept(this);
    6.87 +        trueBranch = visitForControl(node->ifTrue());
    6.88          __ jump(&join);
    6.89          __ bind(&next);
    6.90 -        node->ifFalse()->accept(this);
    6.91 +        falseBranch = visitForControl(node->ifFalse());
    6.92      }
    6.93  
    6.94      __ bind(&join);
    6.95 +
    6.96 +    control_->merge(trueBranch, falseBranch);
    6.97  }
    6.98  
    6.99  void
   6.100 @@ -250,7 +296,7 @@
   6.101      loop_->setUsesLifoStack();
   6.102  }
   6.103  
   6.104 -void
   6.105 +SemanticAnalysis::ControlStatus
   6.106  SemanticAnalysis::visitLoopBody(Statement *stmt)
   6.107  {
   6.108      // We always mark off a loop body, so on each edge, we can balance the
   6.109 @@ -264,11 +310,13 @@
   6.110      // possibility that a long-running loop will overflow the LIFO region
   6.111      // and therefore start requiring nursery allocations.
   6.112      int pos = __ enterloop();
   6.113 -    stmt->accept(this);
   6.114 +    ControlStatus status = visitForControl(stmt);
   6.115      __ leaveloop();
   6.116  
   6.117      if (loop_->usesLifoStack())
   6.118          __ setLoopUsesLifoStack(pos);
   6.119 +
   6.120 +    return status;
   6.121  }
   6.122  
   6.123  void
   6.124 @@ -289,7 +337,7 @@
   6.125          __ jump(&test);
   6.126  
   6.127      __ bind(&header);
   6.128 -    visitLoopBody(node->body());
   6.129 +    ControlStatus status = visitLoopBody(node->body());
   6.130  
   6.131      __ bind(&update);
   6.132      if (node->update())
   6.133 @@ -303,6 +351,14 @@
   6.134      }
   6.135  
   6.136      __ bind(&join);
   6.137 +    
   6.138 +    // If there was a condition, we treat this like a while loop. Otherwise,
   6.139 +    // the join point is only reachable if the loop contains a break. The
   6.140 +    // termination status doesn't tell us anything yet.
   6.141 +    if (node->condition() || loop.breaks())
   6.142 +        control_->merge(status, ControlStatus(false, Returns_Never));
   6.143 +    else
   6.144 +        control_->terminate(status.returns());
   6.145  
   6.146      assert(__ stackDepth() == 0);
   6.147  }
   6.148 @@ -317,19 +373,34 @@
   6.149  
   6.150      __ bind(&header);
   6.151  
   6.152 +    ControlStatus status;
   6.153 +
   6.154      LoopScope loop(scope_, &join, &header, &loop_);
   6.155      if (node->token() == TOK_WHILE) {
   6.156          Label body;
   6.157          visitForTest(node->condition(), &body, &join, &body);
   6.158 -        visitLoopBody(node->body());
   6.159 +        status = visitLoopBody(node->body());
   6.160          __ jump(&header);
   6.161      } else {
   6.162 -        visitLoopBody(node->body());
   6.163 +        status = visitLoopBody(node->body());
   6.164          visitForTest(node->condition(), &header, &join, &join);
   6.165      }
   6.166  
   6.167      __ bind(&join);
   6.168  
   6.169 +    // Detect loops that will always have a fork in the contro-flow graph.
   6.170 +    // This will be a |while| loop, or a |do-while| loop whose body does
   6.171 +    // not terminate. For now, we treat these as an |if| regardless of the
   6.172 +    // direction the branch can take. That means the control-flow is always
   6.173 +    // conditional.
   6.174 +    if (node->token() == TOK_WHILE || status.terminates() || loop.breaks()) {
   6.175 +        control_->merge(status, ControlStatus(false, Returns_Never));
   6.176 +    } else {
   6.177 +        // At this point, we've got a do-while loop with a |return| or
   6.178 +        // |continue|, meaning that the rest of the block is unreachable.
   6.179 +        control_->terminate(status.returns());
   6.180 +    }
   6.181 +
   6.182      assert(__ stackDepth() == 0);
   6.183  }
   6.184  
   6.185 @@ -398,9 +469,11 @@
   6.186  void
   6.187  SemanticAnalysis::visitReturnStatement(ReturnStatement *node)
   6.188  {
   6.189 +    control_->returns(Returns_Always);
   6.190 +
   6.191      if (!node->expression()) {
   6.192          if (!fun_->returnType()->isVoid()) {
   6.193 -            cc_.reportError(node->pos(), Message_UsedVoidReturn);
   6.194 +            cc_.reportError(node->pos(), Message_MissingReturnValue);
   6.195              return;
   6.196          }
   6.197  
   6.198 @@ -408,6 +481,11 @@
   6.199          return;
   6.200      }
   6.201  
   6.202 +    if (fun_->returnType()->isVoid()) {
   6.203 +        cc_.reportError(node->pos(), Message_UsedVoidReturn);
   6.204 +        return;
   6.205 +    }
   6.206 +
   6.207      PoolScope poolScope(zone_->pool());
   6.208      RootScope rootScope(zone_);
   6.209  
   6.210 @@ -420,7 +498,11 @@
   6.211          return;
   6.212  
   6.213      discharge(hir);
   6.214 -    __ return_();
   6.215 +
   6.216 +    if (hir->type()->isVoid())
   6.217 +        __ returnvoid();
   6.218 +    else
   6.219 +        __ return_();
   6.220  }
   6.221  
   6.222  void
   6.223 @@ -601,6 +683,7 @@
   6.224          return;
   6.225      }
   6.226  
   6.227 +    loop_->setBreaks();
   6.228      __ j(OP_BREAK, loop_->break_());
   6.229  }
   6.230  
     7.1 --- a/src/compiler/SemanticAnalysis.h	Sat Dec 22 18:25:31 2012 -0800
     7.2 +++ b/src/compiler/SemanticAnalysis.h	Sat Dec 22 19:48:43 2012 -0800
     7.3 @@ -88,8 +88,85 @@
     7.4          Coerce_Assign
     7.5      };
     7.6  
     7.7 +    // For a given control flow path, whether it returns.
     7.8 +    enum ReturnStatus
     7.9 +    {
    7.10 +        Returns_Never,
    7.11 +        Returns_Sometimes,
    7.12 +        Returns_Always
    7.13 +    };
    7.14 +
    7.15 +    // For a given control flow path, whether it terminates,
    7.16 +    // and if it terminates, whether it returns.
    7.17 +    class ControlStatus
    7.18 +    {
    7.19 +        bool terminates_;
    7.20 +        ReturnStatus returns_;
    7.21 +
    7.22 +      public:
    7.23 +        ControlStatus()
    7.24 +        {
    7.25 +        }
    7.26 +        ControlStatus(bool terminates, ReturnStatus returns)
    7.27 +          : terminates_(terminates),
    7.28 +            returns_(returns)
    7.29 +        {
    7.30 +        }
    7.31 +
    7.32 +        bool terminates() const {
    7.33 +            return terminates_;
    7.34 +        }
    7.35 +        ReturnStatus returns() const {
    7.36 +            return returns_;
    7.37 +        }
    7.38 +    };
    7.39 +
    7.40 +    class ControlScope
    7.41 +    {
    7.42 +        ReturnStatus returns_;
    7.43 +        ControlScope **prevp_;
    7.44 +        ControlScope *prev_;
    7.45 +        bool terminates_;
    7.46 +
    7.47 +      public:
    7.48 +        ControlScope(ControlScope **prevp)
    7.49 +          : returns_(Returns_Never),
    7.50 +            prevp_(prevp),
    7.51 +            prev_(*prevp),
    7.52 +            terminates_(false)
    7.53 +        {
    7.54 +            *prevp_ = this;
    7.55 +        }
    7.56 +        ~ControlScope() {
    7.57 +            *prevp_ = prev_;
    7.58 +        }
    7.59 +        ControlStatus status() {
    7.60 +            return ControlStatus(terminates_, returns_);
    7.61 +        }
    7.62 +
    7.63 +        void returns(ReturnStatus status) {
    7.64 +            // We allow multiple terminates, but only the first one takes
    7.65 +            // effect. For example, |break; continue;| is legal, but the
    7.66 +            // continue is unreachable.
    7.67 +            if (terminates_)
    7.68 +                return;
    7.69 +            if (status > returns_)
    7.70 +                returns_ = status;
    7.71 +        }
    7.72 +        void terminate(ReturnStatus status) {
    7.73 +            if (terminates_)
    7.74 +                return;
    7.75 +            terminates_ = true;
    7.76 +            returns_ = status;
    7.77 +        }
    7.78 +        void merge(const ControlStatus &left, const ControlStatus &right);
    7.79 +        void inherit(const ControlStatus &other);
    7.80 +    };
    7.81 +
    7.82 +
    7.83      void requireLifoStack();
    7.84 -    void visitLoopBody(Statement *stmt);
    7.85 +    ControlStatus visitForControl(Statement *statement);
    7.86 +    ControlStatus visitLoopBody(Statement *stmt);
    7.87      void visitForTest(Expression *expr,
    7.88                        BytecodeEmitter::Label *trueBranch,
    7.89                        BytecodeEmitter::Label *falseBranch,
    7.90 @@ -117,6 +194,7 @@
    7.91          LoopScope **prevp_;
    7.92          LoopScope *prev_;
    7.93          bool usesLifoStack_;
    7.94 +        bool breaks_;
    7.95  
    7.96        public:
    7.97          LoopScope(Scope *scope, BytecodeEmitter::Label *bk, BytecodeEmitter::Label *ct,
    7.98 @@ -126,7 +204,8 @@
    7.99              ct_(ct),
   7.100              prevp_(prevp),
   7.101              prev_(*prevp),
   7.102 -            usesLifoStack_(false)
   7.103 +            usesLifoStack_(false),
   7.104 +            breaks_(false)
   7.105          {
   7.106              *prevp_ = this;
   7.107          }
   7.108 @@ -151,6 +230,12 @@
   7.109          bool usesLifoStack() const {
   7.110              return usesLifoStack_;
   7.111          }
   7.112 +        void setBreaks() {
   7.113 +            breaks_ = true;
   7.114 +        }
   7.115 +        bool breaks() const {
   7.116 +            return breaks_;
   7.117 +        }
   7.118      };
   7.119      
   7.120    private:
   7.121 @@ -164,6 +249,7 @@
   7.122      ScopedRoot<Module> module_;
   7.123      ScopedRoot<FunctionType> fun_;
   7.124      LoopScope *loop_;
   7.125 +    ControlScope *control_;
   7.126  };
   7.127  
   7.128  } // namespace ke
     8.1 --- a/src/main.cpp	Sat Dec 22 18:25:31 2012 -0800
     8.2 +++ b/src/main.cpp	Sat Dec 22 19:48:43 2012 -0800
     8.3 @@ -45,7 +45,7 @@
     8.4          }
     8.5      }
     8.6      virtual void beginCompilationErrors(const char *file) {
     8.7 -        fprintf(stderr, "\nDisplaying compilation errors for \"%s\"\n", file);
     8.8 +        fprintf(stderr, "Displaying compilation errors for \"%s\"\n", file);
     8.9      }
    8.10      virtual void reportCompilationError(const char *message, unsigned line, unsigned col) {
    8.11          fprintf(stderr, "  line %d, col %d: %s\n", line, col, message);
    8.12 @@ -110,12 +110,15 @@
    8.13      int rval = 0;
    8.14  
    8.15      Keima::InstallMessageReporter(&s_Reporter);
    8.16 -    {
    8.17 +    do {
    8.18          MainCollector entry;
    8.19          Keima::SetPublicFunctionListener(&entry);
    8.20  
    8.21          HandleScope scope;
    8.22 -        Compile(argv[1]);
    8.23 +        if (!Compile(argv[1])) {
    8.24 +            rval = 1;
    8.25 +            break;
    8.26 +        }
    8.27  
    8.28          Keima::CollectGarbage();
    8.29  
    8.30 @@ -131,7 +134,7 @@
    8.31              if (!Keima::Invoke(fun, NULL, 0, true, &rval))
    8.32                  rval = 1;
    8.33          }
    8.34 -    }
    8.35 +    } while (0);
    8.36  
    8.37      Keima::Shutdown();
    8.38  
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/tests/basic/precall-void-used.out	Sat Dec 22 19:48:43 2012 -0800
     9.3 @@ -0,0 +1,1 @@
     9.4 +0
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/tests/basic/precall-void-used.sp	Sat Dec 22 19:48:43 2012 -0800
    10.3 @@ -0,0 +1,17 @@
    10.4 +native PrintNum(num);
    10.5 +
    10.6 +g()
    10.7 +{
    10.8 +    PrintNum(f());
    10.9 +}
   10.10 +
   10.11 +f()
   10.12 +{
   10.13 +    return 0;
   10.14 +}
   10.15 +
   10.16 +public main()
   10.17 +{
   10.18 +    g();
   10.19 +}
   10.20 +
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/tests/basic/return-analyze-for-1.sp	Sat Dec 22 19:48:43 2012 -0800
    12.3 @@ -0,0 +1,12 @@
    12.4 +public f()
    12.5 +{
    12.6 +     for (;;) {
    12.7 +     }
    12.8 +}
    12.9 +
   12.10 +public main()
   12.11 +{
   12.12 +}
   12.13 +
   12.14 +
   12.15 +
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/tests/basic/return-analyze-for-2.out	Sat Dec 22 19:48:43 2012 -0800
    13.3 @@ -0,0 +1,1 @@
    13.4 +5
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/tests/basic/return-analyze-for-2.sp	Sat Dec 22 19:48:43 2012 -0800
    14.3 @@ -0,0 +1,16 @@
    14.4 +native PrintNum(num);
    14.5 +
    14.6 +f()
    14.7 +{
    14.8 +     for (;;) {
    14.9 +          return 5;
   14.10 +     }
   14.11 +}
   14.12 +
   14.13 +public main()
   14.14 +{
   14.15 +    PrintNum(f());
   14.16 +}
   14.17 +
   14.18 +
   14.19 +
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/tests/basic/return-analyze-for-3.err	Sat Dec 22 19:48:43 2012 -0800
    15.3 @@ -0,0 +1,1 @@
    15.4 +  line 1, col 2: not all paths through function return a value
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/tests/basic/return-analyze-for-3.sp	Sat Dec 22 19:48:43 2012 -0800
    16.3 @@ -0,0 +1,13 @@
    16.4 +f(x)
    16.5 +{
    16.6 +    for (;;) {
    16.7 +        if (x)
    16.8 +            return 5;
    16.9 +    }
   16.10 +}
   16.11 +
   16.12 +public main()
   16.13 +{
   16.14 +    f(5);
   16.15 +}
   16.16 +
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/tests/basic/return-analyze-for-4.err	Sat Dec 22 19:48:43 2012 -0800
    17.3 @@ -0,0 +1,1 @@
    17.4 +  line 1, col 8: not all paths through function return a value
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/tests/basic/return-analyze-for-4.sp	Sat Dec 22 19:48:43 2012 -0800
    18.3 @@ -0,0 +1,13 @@
    18.4 +public f(x)
    18.5 +{
    18.6 +    for (; x;) {
    18.7 +        return 5;
    18.8 +    }
    18.9 +}
   18.10 +
   18.11 +public main()
   18.12 +{
   18.13 +    return f(5);
   18.14 +}
   18.15 +
   18.16 +
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/tests/basic/return-analyze-for-5.out	Sat Dec 22 19:48:43 2012 -0800
    19.3 @@ -0,0 +1,1 @@
    19.4 +54
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/tests/basic/return-analyze-for-5.sp	Sat Dec 22 19:48:43 2012 -0800
    20.3 @@ -0,0 +1,17 @@
    20.4 +native PrintNum(num);
    20.5 +
    20.6 +f(x)
    20.7 +{
    20.8 +    for (; x;) {
    20.9 +        return 5;
   20.10 +    }
   20.11 +    return 4;
   20.12 +}
   20.13 +
   20.14 +public main()
   20.15 +{
   20.16 +    PrintNum(f(5));
   20.17 +    PrintNum(f(0));
   20.18 +}
   20.19 +
   20.20 +
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/tests/basic/return-analyze-if-1.out	Sat Dec 22 19:48:43 2012 -0800
    21.3 @@ -0,0 +1,1 @@
    21.4 +25
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/tests/basic/return-analyze-if-1.sp	Sat Dec 22 19:48:43 2012 -0800
    22.3 @@ -0,0 +1,16 @@
    22.4 +native PrintNum(num);
    22.5 +
    22.6 +f(x)
    22.7 +{
    22.8 +    if (x)
    22.9 +        return 5;
   22.10 +    return 2;
   22.11 +}
   22.12 +
   22.13 +public main()
   22.14 +{
   22.15 +    PrintNum(f(0));
   22.16 +    PrintNum(f(1));
   22.17 +}
   22.18 +
   22.19 +
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/tests/basic/return-analyze-if-2.err	Sat Dec 22 19:48:43 2012 -0800
    23.3 @@ -0,0 +1,1 @@
    23.4 +  line 3, col 3: not all paths through function return a value
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/tests/basic/return-analyze-if-2.sp	Sat Dec 22 19:48:43 2012 -0800
    24.3 @@ -0,0 +1,14 @@
    24.4 +native PrintNum(num);
    24.5 +
    24.6 +f(x)
    24.7 +{
    24.8 +    if (x)
    24.9 +        return 5;
   24.10 +}
   24.11 +
   24.12 +public main()
   24.13 +{
   24.14 +    PrintNum(f(4));
   24.15 +}
   24.16 +
   24.17 +
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/tests/basic/return-analyze-if-3.out	Sat Dec 22 19:48:43 2012 -0800
    25.3 @@ -0,0 +1,1 @@
    25.4 +65
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/tests/basic/return-analyze-if-3.sp	Sat Dec 22 19:48:43 2012 -0800
    26.3 @@ -0,0 +1,17 @@
    26.4 +native PrintNum(num);
    26.5 +
    26.6 +f(x)
    26.7 +{
    26.8 +    if (x)
    26.9 +        return 5;
   26.10 +    else
   26.11 +        return 6;
   26.12 +}
   26.13 +
   26.14 +public main()
   26.15 +{
   26.16 +    PrintNum(f(0));
   26.17 +    PrintNum(f(1));
   26.18 +}
   26.19 +
   26.20 +
    27.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.2 +++ b/tests/basic/return-analyze-if-4.err	Sat Dec 22 19:48:43 2012 -0800
    27.3 @@ -0,0 +1,1 @@
    27.4 +  line 3, col 3: not all paths through function return a value
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/tests/basic/return-analyze-if-4.sp	Sat Dec 22 19:48:43 2012 -0800
    28.3 @@ -0,0 +1,17 @@
    28.4 +native PrintNum(num);
    28.5 +
    28.6 +f(x)
    28.7 +{
    28.8 +    if (x)
    28.9 +        return 5;
   28.10 +    else if (x)
   28.11 +        return 6;
   28.12 +}
   28.13 +
   28.14 +public main()
   28.15 +{
   28.16 +    PrintNum(f(0));
   28.17 +    PrintNum(f(1));
   28.18 +}
   28.19 +
   28.20 +
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/tests/basic/return-analyze-if-5.out	Sat Dec 22 19:48:43 2012 -0800
    29.3 @@ -0,0 +1,1 @@
    29.4 +596
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/tests/basic/return-analyze-if-5.sp	Sat Dec 22 19:48:43 2012 -0800
    30.3 @@ -0,0 +1,20 @@
    30.4 +native PrintNum(num);
    30.5 +
    30.6 +f(x)
    30.7 +{
    30.8 +    if (x == 5)
    30.9 +        return 5;
   30.10 +    else if (x == 7)
   30.11 +        return 9;
   30.12 +    else
   30.13 +        return 6;
   30.14 +}
   30.15 +
   30.16 +public main()
   30.17 +{
   30.18 +    PrintNum(f(5));
   30.19 +    PrintNum(f(7));
   30.20 +    PrintNum(f(2));
   30.21 +}
   30.22 +
   30.23 +
    31.1 --- a/tests/prototype/precall-void-used.sp	Sat Dec 22 18:25:31 2012 -0800
    31.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.3 @@ -1,11 +0,0 @@
    31.4 -native PrintToServer(const String:str[], ...);
    31.5 -
    31.6 -f()
    31.7 -{
    31.8 -    return main();
    31.9 -}
   31.10 -
   31.11 -main()
   31.12 -{
   31.13 -}
   31.14 -
    32.1 --- a/tests/prototype/return-analyze-for-1.sp	Sat Dec 22 18:25:31 2012 -0800
    32.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.3 @@ -1,14 +0,0 @@
    32.4 -native PrintToServer(const String:str[], ...);
    32.5 -
    32.6 -main(x)
    32.7 -{
    32.8 -     for (;;) {
    32.9 -     }
   32.10 -}
   32.11 -
   32.12 -public f()
   32.13 -{
   32.14 -    return main(5);
   32.15 -}
   32.16 -
   32.17 -
    33.1 --- a/tests/prototype/return-analyze-for-2.sp	Sat Dec 22 18:25:31 2012 -0800
    33.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    33.3 @@ -1,15 +0,0 @@
    33.4 -native PrintToServer(const String:str[], ...);
    33.5 -
    33.6 -main(x)
    33.7 -{
    33.8 -     for (;;) {
    33.9 -          return 5;
   33.10 -     }
   33.11 -}
   33.12 -
   33.13 -public f()
   33.14 -{
   33.15 -    return main(5);
   33.16 -}
   33.17 -
   33.18 -
    34.1 --- a/tests/prototype/return-analyze-for-3.sp	Sat Dec 22 18:25:31 2012 -0800
    34.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.3 @@ -1,16 +0,0 @@
    34.4 -native PrintToServer(const String:str[], ...);
    34.5 -
    34.6 -main(x)
    34.7 -{
    34.8 -     for (;;) {
    34.9 -         if (x)
   34.10 -             return 5;
   34.11 -     }
   34.12 -}
   34.13 -
   34.14 -public f()
   34.15 -{
   34.16 -    return main(5);
   34.17 -}
   34.18 -
   34.19 -
    35.1 --- a/tests/prototype/return-analyze-for-4.sp	Sat Dec 22 18:25:31 2012 -0800
    35.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.3 @@ -1,15 +0,0 @@
    35.4 -native PrintToServer(const String:str[], ...);
    35.5 -
    35.6 -main(x)
    35.7 -{
    35.8 -     for (; x;) {
    35.9 -         return 5;
   35.10 -     }
   35.11 -}
   35.12 -
   35.13 -public f()
   35.14 -{
   35.15 -    return main(5);
   35.16 -}
   35.17 -
   35.18 -
    36.1 --- a/tests/prototype/return-analyze-for-5.sp	Sat Dec 22 18:25:31 2012 -0800
    36.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    36.3 @@ -1,16 +0,0 @@
    36.4 -native PrintToServer(const String:str[], ...);
    36.5 -
    36.6 -main(x)
    36.7 -{
    36.8 -     for (; x;) {
    36.9 -         return 5;
   36.10 -     }
   36.11 -     return 4;
   36.12 -}
   36.13 -
   36.14 -public f()
   36.15 -{
   36.16 -    return main(5);
   36.17 -}
   36.18 -
   36.19 -
    37.1 --- a/tests/prototype/return-analyze-if-1.sp	Sat Dec 22 18:25:31 2012 -0800
    37.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    37.3 @@ -1,15 +0,0 @@
    37.4 -native PrintToServer(const String:str[], ...);
    37.5 -
    37.6 -main(x)
    37.7 -{
    37.8 -    if (x)
    37.9 -        return 5;
   37.10 -    return 2;
   37.11 -}
   37.12 -
   37.13 -f()
   37.14 -{
   37.15 -    return main(5);
   37.16 -}
   37.17 -
   37.18 -
    38.1 --- a/tests/prototype/return-analyze-if-2.sp	Sat Dec 22 18:25:31 2012 -0800
    38.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    38.3 @@ -1,14 +0,0 @@
    38.4 -native PrintToServer(const String:str[], ...);
    38.5 -
    38.6 -main(x)
    38.7 -{
    38.8 -    if (x)
    38.9 -        return 5;
   38.10 -}
   38.11 -
   38.12 -f()
   38.13 -{
   38.14 -    return main(5);
   38.15 -}
   38.16 -
   38.17 -
    39.1 --- a/tests/prototype/return-analyze-if-3.sp	Sat Dec 22 18:25:31 2012 -0800
    39.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    39.3 @@ -1,16 +0,0 @@
    39.4 -native PrintToServer(const String:str[], ...);
    39.5 -
    39.6 -main(x)
    39.7 -{
    39.8 -    if (x)
    39.9 -        return 5;
   39.10 -    else
   39.11 -        return 6;
   39.12 -}
   39.13 -
   39.14 -public f()
   39.15 -{
   39.16 -    return main(5);
   39.17 -}
   39.18 -
   39.19 -
    40.1 --- a/tests/prototype/return-analyze-if-4.sp	Sat Dec 22 18:25:31 2012 -0800
    40.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    40.3 @@ -1,16 +0,0 @@
    40.4 -native PrintToServer(const String:str[], ...);
    40.5 -
    40.6 -main(x)
    40.7 -{
    40.8 -    if (x)
    40.9 -        return 5;
   40.10 -    else if (x)
   40.11 -        return 6;
   40.12 -}
   40.13 -
   40.14 -public f()
   40.15 -{
   40.16 -    return main(5);
   40.17 -}
   40.18 -
   40.19 -
    41.1 --- a/tests/prototype/return-analyze-if-5.sp	Sat Dec 22 18:25:31 2012 -0800
    41.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    41.3 @@ -1,18 +0,0 @@
    41.4 -native PrintToServer(const String:str[], ...);
    41.5 -
    41.6 -main(x)
    41.7 -{
    41.8 -    if (x)
    41.9 -        return 5;
   41.10 -    else if (x)
   41.11 -        return 6;
   41.12 -    else
   41.13 -        return 6;
   41.14 -}
   41.15 -
   41.16 -public f()
   41.17 -{
   41.18 -    return main(5);
   41.19 -}
   41.20 -
   41.21 -
    42.1 --- a/tests/test.py	Sat Dec 22 18:25:31 2012 -0800
    42.2 +++ b/tests/test.py	Sat Dec 22 19:48:43 2012 -0800
    42.3 @@ -29,6 +29,9 @@
    42.4        i = 0
    42.5        for line in f:
    42.6          line = line.rstrip('\r\n')
    42.7 +        if "Displaying compilation errors for" in lines[i]:
    42.8 +          i += 1
    42.9 +          continue
   42.10          if line != lines[i]:
   42.11            message = 'Got "' + lines[i] + '", expected "' + line + '"'
   42.12            ok = False