BOO-988: Added else block to the for and while statements, and include a suite of...
[boo.git] / src / Boo.Lang.Compiler / Steps / BranchChecking.cs
blobdf3f2b414f093678e8ebe3ab0f4b2788558aad21
1 #region license
2 // Copyright (c) 2003, 2004, 2005 Rodrigo B. de Oliveira (rbo@acm.org)
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution.
13 // * Neither the name of Rodrigo B. de Oliveira nor the names of its
14 // contributors may be used to endorse or promote products derived from this
15 // software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #endregion
30 using System;
31 using System.Collections;
32 using System.Collections.Generic;
33 using Boo.Lang.Compiler.Ast;
34 using Boo.Lang.Compiler.TypeSystem;
36 namespace Boo.Lang.Compiler.Steps
38 class MethodBodyState
40 private int _loopDepth;
42 private int _protectedBlockDepth;
43 private int _exceptionHandlerDepth;
45 private Stack<TryStatement> _tryBlocks = new Stack<TryStatement>();
47 private List _labelReferences = new List();
49 private Hashtable _labels = new Hashtable();
51 public void Reset()
53 _loopDepth = 0;
54 _exceptionHandlerDepth = 0;
55 _tryBlocks.Clear();
56 _labelReferences.Clear();
57 _labels.Clear();
60 public void AddLabelReference(ReferenceExpression node)
62 _labelReferences.Add(node);
65 public List LabelReferences
67 get { return _labelReferences; }
70 public void EnterTryBlock(TryStatement tryBlock)
72 _tryBlocks.Push(tryBlock);
75 public void LeaveTryBlock()
77 _tryBlocks.Pop();
80 public void EnterProtectedBlock()
82 ++_protectedBlockDepth;
85 public void LeaveProtectedBlock()
87 --_protectedBlockDepth;
90 public int TryBlockDepth {
91 get { return _tryBlocks.Count; }
94 public IEnumerable<TryStatement> TryBlocks {
95 get { return _tryBlocks; }
98 public int ProtectedBlockDepth {
99 get { return _protectedBlockDepth; }
102 public void EnterExceptionHandler()
104 ++_exceptionHandlerDepth;
107 public bool InExceptionHandler
109 get { return _exceptionHandlerDepth > 0; }
112 public void LeaveExceptionHandler()
114 --_exceptionHandlerDepth;
117 public void EnterLoop()
119 ++_loopDepth;
122 public bool InLoop
124 get { return _loopDepth > 0; }
127 public void LeaveLoop()
129 --_loopDepth;
132 public InternalLabel ResolveLabel(string name)
134 return (InternalLabel)_labels[name];
137 public void AddLabel(InternalLabel label)
139 _labels.Add(label.Name, label);
143 public class BranchChecking : AbstractVisitorCompilerStep
145 private InternalMethod _currentMethod;
147 private MethodBodyState _state = new MethodBodyState();
149 public override void Run()
151 Visit(CompileUnit);
154 override public void OnTryStatement(TryStatement node)
156 _state.EnterTryBlock(node);
157 _state.EnterProtectedBlock();
158 Visit(node.ProtectedBlock);
159 _state.LeaveProtectedBlock();
161 Visit(node.ExceptionHandlers);
163 Visit(node.FailureBlock);
165 Visit(node.EnsureBlock);
166 _state.LeaveTryBlock();
169 override public void OnExceptionHandler(ExceptionHandler node)
171 _state.EnterExceptionHandler();
172 Visit(node.Block);
173 _state.LeaveExceptionHandler();
176 override public void LeaveRaiseStatement(RaiseStatement node)
178 if (node.Exception != null) return;
179 if (_state.InExceptionHandler) return;
180 Error(CompilerErrorFactory.ReRaiseOutsideExceptionHandler(node));
183 public override void OnConstructor(Constructor node)
185 OnMethod(node);
188 public override void OnDestructor(Destructor node)
190 OnMethod(node);
193 override public void OnMethod(Method node)
195 _currentMethod = (InternalMethod) node.Entity;
196 _state.Reset();
197 Visit(node.Body);
198 ResolveLabelReferences();
201 void ResolveLabelReferences()
203 foreach (ReferenceExpression reference in _state.LabelReferences)
205 InternalLabel label = _state.ResolveLabel(reference.Name);
206 if (null == label)
208 Error(reference, CompilerErrorFactory.NoSuchLabel(reference, reference.Name));
210 else
212 reference.Entity = label;
217 override public void OnWhileStatement(WhileStatement node)
219 VisitLoop(node.Block);
222 private void VisitLoop(Block block)
224 _state.EnterLoop();
225 Visit(block);
226 _state.LeaveLoop();
229 override public void OnForStatement(ForStatement node)
231 VisitLoop(node.Block);
234 override public void OnLabelStatement(LabelStatement node)
236 AstAnnotations.SetTryBlockDepth(node, _state.TryBlockDepth);
238 if (null == _state.ResolveLabel(node.Name))
240 _state.AddLabel(new InternalLabel(node));
242 else
244 Error(
245 CompilerErrorFactory.LabelAlreadyDefined(node,
246 _currentMethod.FullName,
247 node.Name));
251 override public void OnYieldStatement(YieldStatement node)
253 if (_state.TryBlockDepth == _state.ProtectedBlockDepth) {
254 // we are currently only in the protected blocks, not in any "except" or "ensure" blocks.
255 foreach (TryStatement tryBlock in _state.TryBlocks) {
256 // only allow yield in the try part of try-ensure blocks, fail if it is a try-except block
257 if (tryBlock.FailureBlock != null || tryBlock.ExceptionHandlers.Count > 0) {
258 Error(CompilerErrorFactory.YieldInsideTryExceptOrEnsureBlock(node));
259 break;
262 } else {
263 Error(CompilerErrorFactory.YieldInsideTryExceptOrEnsureBlock(node));
267 public override void OnMethodInvocationExpression(MethodInvocationExpression node)
269 if (BuiltinFunction.Switch != node.Target.Entity) return;
271 for (int i = 1; i < node.Arguments.Count; ++i)
273 ReferenceExpression label = (ReferenceExpression)node.Arguments[i];
274 _state.AddLabelReference(label);
278 override public void OnGotoStatement(GotoStatement node)
280 AstAnnotations.SetTryBlockDepth(node, _state.TryBlockDepth);
282 _state.AddLabelReference(node.Label);
285 override public void OnBreakStatement(BreakStatement node)
287 CheckInLoop(node);
290 override public void OnContinueStatement(ContinueStatement node)
292 CheckInLoop(node);
295 override public void OnBlockExpression(BlockExpression node)
297 // nothing to do
300 private void CheckInLoop(Statement node)
302 if (!_state.InLoop)
304 Error(CompilerErrorFactory.NoEnclosingLoop(node));