[vala] dova: Add initial support for error handling



commit 31cb78817cb9cc87c18f0d9b12e6a4efa4ec5222
Author: Jürg Billeter <j bitron ch>
Date:   Sat Feb 6 14:25:41 2010 +0100

    dova: Add initial support for error handling

 codegen/Makefile.am                   |    1 +
 codegen/valaccodegenerator.vala       |    3 +-
 codegen/valadovabasemodule.vala       |    2 +
 codegen/valadovaerrormodule.vala      |  343 +++++++++++++++++++++++++++++++++
 codegen/valadovamethodcallmodule.vala |    7 +
 codegen/valadovaobjectmodule.vala     |   19 ++
 vala/valacatchclause.vala             |    7 +-
 vala/valaflowanalyzer.vala            |   56 ++++--
 vala/valamethod.vala                  |    3 +-
 vala/valasemanticanalyzer.vala        |    2 +
 vala/valathrowstatement.vala          |    4 +-
 11 files changed, 428 insertions(+), 19 deletions(-)
---
diff --git a/codegen/Makefile.am b/codegen/Makefile.am
index c62b6f6..7581eb6 100644
--- a/codegen/Makefile.am
+++ b/codegen/Makefile.am
@@ -39,6 +39,7 @@ libvala_la_VALASOURCES = \
 	valadovabasemodule.vala \
 	valadovacontrolflowmodule.vala \
 	valadovadelegatemodule.vala \
+	valadovaerrormodule.vala \
 	valadovamemberaccessmodule.vala \
 	valadovamethodcallmodule.vala \
 	valadovamethodmodule.vala \
diff --git a/codegen/valaccodegenerator.vala b/codegen/valaccodegenerator.vala
index e2155cf..b32ba3c 100644
--- a/codegen/valaccodegenerator.vala
+++ b/codegen/valaccodegenerator.vala
@@ -65,8 +65,9 @@ public class Vala.CCodeGenerator : CodeGenerator {
 			head = new DovaArrayModule (this, head);
 			head = new DovaObjectModule (this, head);
 			head = new DovaValueModule (this, head);
-			*/
 			head = new DovaDelegateModule (this, head);
+			*/
+			head = new DovaErrorModule (this, head);
 		} else {
 			/* included by inheritance
 			head = new CCodeBaseModule (this, head);
diff --git a/codegen/valadovabasemodule.vala b/codegen/valadovabasemodule.vala
index f58a525..01c3fb7 100644
--- a/codegen/valadovabasemodule.vala
+++ b/codegen/valadovabasemodule.vala
@@ -130,6 +130,7 @@ internal class Vala.DovaBaseModule : CCodeModule {
 	public Class value_class;
 	public Class array_class;
 	public Class delegate_class;
+	public Class error_class;
 
 	Set<Symbol> generated_external_symbols;
 
@@ -208,6 +209,7 @@ internal class Vala.DovaBaseModule : CCodeModule {
 		value_class = (Class) dova_ns.scope.lookup ("Value");
 		array_class = (Class) dova_ns.scope.lookup ("Array");
 		delegate_class = (Class) dova_ns.scope.lookup ("Delegate");
+		error_class = (Class) dova_ns.scope.lookup ("Error");
 
 		header_declarations = new CCodeDeclarationSpace ();
 		internal_header_declarations = new CCodeDeclarationSpace ();
diff --git a/codegen/valadovaerrormodule.vala b/codegen/valadovaerrormodule.vala
new file mode 100644
index 0000000..9da609a
--- /dev/null
+++ b/codegen/valadovaerrormodule.vala
@@ -0,0 +1,343 @@
+/* valadovaerrormodule.vala
+ *
+ * Copyright (C) 2008-2010  Jürg Billeter
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
+ *
+ * Author:
+ *	Jürg Billeter <j bitron ch>
+ *	Thijs Vermeir <thijsvermeir gmail com>
+ */
+
+using GLib;
+
+internal class Vala.DovaErrorModule : DovaDelegateModule {
+	private int current_try_id = 0;
+	private int next_try_id = 0;
+	private bool is_in_catch = false;
+
+	public DovaErrorModule (CCodeGenerator codegen, CCodeModule? next) {
+		base (codegen, next);
+	}
+
+	public override void visit_throw_statement (ThrowStatement stmt) {
+		stmt.accept_children (codegen);
+
+		var cfrag = new CCodeFragment ();
+
+		// method will fail
+		current_method_inner_error = true;
+		var cassign = new CCodeAssignment (get_variable_cexpression ("_inner_error_"), (CCodeExpression) stmt.error_expression.ccodenode);
+		cfrag.append (new CCodeExpressionStatement (cassign));
+
+		head.add_simple_check (stmt, cfrag, true);
+
+		stmt.ccodenode = cfrag;
+
+		create_temp_decl (stmt, stmt.error_expression.temp_vars);
+	}
+
+	public virtual CCodeStatement return_with_exception () {
+		// propagate error
+		var cerror_block = new CCodeBlock ();
+		cerror_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (new CCodeUnaryExpression (CCodeUnaryOperator.POINTER_INDIRECTION, new CCodeIdentifier ("error")), get_variable_cexpression ("_inner_error_"))));
+		cerror_block.add_statement (new CCodeExpressionStatement (new CCodeAssignment (get_variable_cexpression ("_inner_error_"), new CCodeConstant ("NULL"))));
+
+		// free local variables
+		var free_frag = new CCodeFragment ();
+		append_local_free (current_symbol, free_frag, false);
+		cerror_block.add_statement (free_frag);
+
+		if (current_method is CreationMethod) {
+			var cl = current_method.parent_symbol as Class;
+			var unref_call = new CCodeFunctionCall (new CCodeIdentifier (cl.get_unref_function ()));
+			unref_call.add_argument (new CCodeIdentifier ("self"));
+			cerror_block.add_statement (new CCodeExpressionStatement (unref_call));
+			cerror_block.add_statement (new CCodeReturnStatement (new CCodeConstant ("NULL")));
+		} else if (current_return_type is VoidType) {
+			cerror_block.add_statement (new CCodeReturnStatement ());
+		} else {
+			cerror_block.add_statement (new CCodeReturnStatement (default_value_for_type (current_return_type, false)));
+		}
+
+		return cerror_block;
+	}
+
+	CCodeStatement uncaught_error_statement (CCodeExpression inner_error, CCodeBlock? block = null, bool unexpected = false) {
+		var cerror_block = block;
+		if (cerror_block == null) {
+			cerror_block = new CCodeBlock ();
+		}
+
+		// free local variables
+		var free_frag = new CCodeFragment ();
+		append_local_free (current_symbol, free_frag, false);
+		cerror_block.add_statement (free_frag);
+
+		// TODO log uncaught error as critical warning
+
+		if (current_method is CreationMethod) {
+			cerror_block.add_statement (new CCodeReturnStatement (new CCodeConstant ("NULL")));
+		} else if (current_return_type is VoidType) {
+			cerror_block.add_statement (new CCodeReturnStatement ());
+		} else if (current_return_type != null) {
+			cerror_block.add_statement (new CCodeReturnStatement (default_value_for_type (current_return_type, false)));
+		}
+
+		return cerror_block;
+	}
+
+	bool in_finally_block (CodeNode node) {
+		var current_node = node;
+		while (current_node != null) {
+			var try_stmt = current_node.parent_node as TryStatement;
+			if (try_stmt != null && try_stmt.finally_body == current_node) {
+				return true;
+			}
+			current_node = current_node.parent_node;
+		}
+		return false;
+	}
+
+	public override void add_simple_check (CodeNode node, CCodeFragment cfrag, bool always_fails = false) {
+		current_method_inner_error = true;
+
+		var inner_error = get_variable_cexpression ("_inner_error_");
+
+		CCodeStatement cerror_handler = null;
+
+		if (current_try != null) {
+			// surrounding try found
+			var cerror_block = new CCodeBlock ();
+
+			// free local variables
+			var free_frag = new CCodeFragment ();
+			append_error_free (current_symbol, free_frag, current_try);
+			cerror_block.add_statement (free_frag);
+
+			var error_types = new ArrayList<DataType> ();
+			foreach (DataType node_error_type in node.get_error_types ()) {
+				error_types.add (node_error_type);
+			}
+
+			bool has_general_catch_clause = false;
+
+			if (!is_in_catch) {
+				var handled_error_types = new ArrayList<DataType> ();
+				foreach (CatchClause clause in current_try.get_catch_clauses ()) {
+					// keep track of unhandled error types
+					foreach (DataType node_error_type in error_types) {
+						if (clause.error_type == null || node_error_type.compatible (clause.error_type)) {
+							handled_error_types.add (node_error_type);
+						}
+					}
+					foreach (DataType handled_error_type in handled_error_types) {
+						error_types.remove (handled_error_type);
+					}
+					handled_error_types.clear ();
+
+					// go to catch clause if error domain matches
+					var cgoto_stmt = new CCodeGotoStatement (clause.clabel_name);
+
+					if (clause.error_type.equals (new ObjectType (error_class))) {
+						// general catch clause, this should be the last one
+						has_general_catch_clause = true;
+						cerror_block.add_statement (cgoto_stmt);
+						break;
+					} else {
+						var catch_type = clause.error_type as ObjectType;
+						var cgoto_block = new CCodeBlock ();
+						cgoto_block.add_statement (cgoto_stmt);
+
+						var type_check = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_is_a"));
+						type_check.add_argument (inner_error);
+						type_check.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (catch_type.type_symbol.get_lower_case_cname ()))));
+
+						cerror_block.add_statement (new CCodeIfStatement (type_check, cgoto_block));
+					}
+				}
+			}
+
+			if (has_general_catch_clause) {
+				// every possible error is already caught
+				// as there is a general catch clause
+				// no need to do anything else
+			} else if (error_types.size > 0) {
+				// go to finally clause if no catch clause matches
+				// and there are still unhandled error types
+				cerror_block.add_statement (new CCodeGotoStatement ("__finally%d".printf (current_try_id)));
+			} else if (in_finally_block (node)) {
+				// do not check unexpected errors happening within finally blocks
+				// as jump out of finally block is not supported
+			} else {
+				// should never happen with correct bindings
+				uncaught_error_statement (inner_error, cerror_block, true);
+			}
+
+			cerror_handler = cerror_block;
+		} else if (current_method != null && current_method.get_error_types ().size > 0) {
+			// current method can fail, propagate error
+			CCodeExpression ccond = null;
+
+			foreach (DataType error_type in current_method.get_error_types ()) {
+				// If Dova.Error is allowed we propagate everything
+				if (error_type.equals (new ObjectType (error_class))) {
+					ccond = null;
+					break;
+				}
+
+				// Check the allowed error domains to propagate
+				var type_check = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_is_a"));
+				type_check.add_argument (inner_error);
+				type_check.add_argument (new CCodeFunctionCall (new CCodeIdentifier ("%s_type_get".printf (error_class.get_lower_case_cname ()))));
+				if (ccond == null) {
+					ccond = type_check;
+				} else {
+					ccond = new CCodeBinaryExpression (CCodeBinaryOperator.OR, ccond, type_check);
+				}
+			}
+
+			if (ccond == null) {
+				cerror_handler = return_with_exception ();
+			} else {
+				var cerror_block = new CCodeBlock ();
+				cerror_block.add_statement (new CCodeIfStatement (ccond,
+					return_with_exception (),
+					uncaught_error_statement (inner_error)));
+				cerror_handler = cerror_block;
+			}
+		} else {
+			cerror_handler = uncaught_error_statement (inner_error);
+		}
+
+		if (always_fails) {
+			// inner_error is always set, avoid unnecessary if statement
+			// eliminates C warnings
+			cfrag.append (cerror_handler);
+		} else {
+			var ccond = new CCodeBinaryExpression (CCodeBinaryOperator.INEQUALITY, inner_error, new CCodeConstant ("NULL"));
+			cfrag.append (new CCodeIfStatement (ccond, cerror_handler));
+		}
+	}
+
+	public override void visit_try_statement (TryStatement stmt) {
+		int this_try_id = next_try_id++;
+
+		var old_try = current_try;
+		var old_try_id = current_try_id;
+		var old_is_in_catch = is_in_catch;
+		current_try = stmt;
+		current_try_id = this_try_id;
+		is_in_catch = true;
+
+		foreach (CatchClause clause in stmt.get_catch_clauses ()) {
+			clause.clabel_name = "__catch%d_%s".printf (this_try_id, clause.error_type.get_lower_case_cname ());
+		}
+
+		if (stmt.finally_body != null) {
+			stmt.finally_body.accept (codegen);
+		}
+
+		is_in_catch = false;
+		stmt.body.accept (codegen);
+		is_in_catch = true;
+
+		foreach (CatchClause clause in stmt.get_catch_clauses ()) {
+			clause.accept (codegen);
+		}
+
+		current_try = old_try;
+		current_try_id = old_try_id;
+		is_in_catch = old_is_in_catch;
+
+		var cfrag = new CCodeFragment ();
+		cfrag.append (stmt.body.ccodenode);
+
+		foreach (CatchClause clause in stmt.get_catch_clauses ()) {
+			cfrag.append (new CCodeGotoStatement ("__finally%d".printf (this_try_id)));
+			cfrag.append (clause.ccodenode);
+		}
+
+		cfrag.append (new CCodeLabel ("__finally%d".printf (this_try_id)));
+		if (stmt.finally_body != null) {
+			cfrag.append (stmt.finally_body.ccodenode);
+		}
+
+		// check for errors not handled by this try statement
+		// may be handled by outer try statements or propagated
+		add_simple_check (stmt, cfrag, !stmt.after_try_block_reachable);
+
+		stmt.ccodenode = cfrag;
+	}
+
+	public override void visit_catch_clause (CatchClause clause) {
+		if (clause.error_variable != null) {
+			clause.error_variable.active = true;
+		}
+
+		current_method_inner_error = true;
+
+		generate_type_declaration (clause.error_type, source_declarations);
+
+		clause.accept_children (codegen);
+
+		var cfrag = new CCodeFragment ();
+		cfrag.append (new CCodeLabel (clause.clabel_name));
+
+		var cblock = new CCodeBlock ();
+
+		string variable_name;
+		if (clause.variable_name != null) {
+			variable_name = get_variable_cname (clause.variable_name);
+		} else {
+			variable_name = "__err";
+		}
+
+		if (clause.variable_name != null) {
+			var cdecl = new CCodeDeclaration (clause.error_type.get_cname ());
+			cdecl.add_declarator (new CCodeVariableDeclarator (variable_name, get_variable_cexpression ("_inner_error_")));
+			cblock.add_statement (cdecl);
+		} else {
+			// error object is not used within catch statement, clear it
+			var cclear = new CCodeFunctionCall (new CCodeIdentifier ("dova_object_unref"));
+			cclear.add_argument (get_variable_cexpression ("_inner_error_"));
+			cblock.add_statement (new CCodeExpressionStatement (cclear));
+		}
+		cblock.add_statement (new CCodeExpressionStatement (new CCodeAssignment (get_variable_cexpression ("_inner_error_"), new CCodeConstant ("NULL"))));
+
+		cblock.add_statement (clause.body.ccodenode);
+
+		cfrag.append (cblock);
+
+		clause.ccodenode = cfrag;
+	}
+
+	public override void append_local_free (Symbol sym, CCodeFragment cfrag, bool stop_at_loop = false) {
+		var finally_block = (Block) null;
+		if (sym.parent_node is TryStatement) {
+			finally_block = (sym.parent_node as TryStatement).finally_body;
+		} else if (sym.parent_node is CatchClause) {
+			finally_block = (sym.parent_node.parent_node as TryStatement).finally_body;
+		}
+
+		if (finally_block != null) {
+			cfrag.append (finally_block.ccodenode);
+		}
+
+		base.append_local_free (sym, cfrag, stop_at_loop);
+	}
+}
+
+// vim:sw=8 noet
diff --git a/codegen/valadovamethodcallmodule.vala b/codegen/valadovamethodcallmodule.vala
index aca9570..53817ff 100644
--- a/codegen/valadovamethodcallmodule.vala
+++ b/codegen/valadovamethodcallmodule.vala
@@ -224,6 +224,13 @@ internal class Vala.DovaMethodCallModule : DovaAssignmentModule {
 			ccall_expr = ccomma;
 		}
 
+		if (expr.tree_can_fail) {
+			// method can fail
+			current_method_inner_error = true;
+			// add &inner_error before the ellipsis arguments
+			ccall.add_argument (new CCodeUnaryExpression (CCodeUnaryOperator.ADDRESS_OF, get_variable_cexpression ("_inner_error_")));
+		}
+
 		expr.ccodenode = ccall_expr;
 	}
 }
diff --git a/codegen/valadovaobjectmodule.vala b/codegen/valadovaobjectmodule.vala
index 276cbf5..30eb4da 100644
--- a/codegen/valadovaobjectmodule.vala
+++ b/codegen/valadovaobjectmodule.vala
@@ -1060,6 +1060,8 @@ internal class Vala.DovaObjectModule : DovaArrayModule {
 
 		m.accept_children (codegen);
 
+		bool inner_error = current_method_inner_error;
+
 		current_symbol = old_symbol;
 		current_method_inner_error = old_method_inner_error;
 		next_temp_var_id = old_next_temp_var_id;
@@ -1125,6 +1127,12 @@ internal class Vala.DovaObjectModule : DovaArrayModule {
 					function.block.add_statement (new CCodeReturnStatement (new CCodeIdentifier ("result")));
 				}
 
+				if (inner_error) {
+					var cdecl = new CCodeDeclaration ("DovaError *");
+					cdecl.add_declarator (new CCodeVariableDeclarator ("_inner_error_", new CCodeConstant ("NULL")));
+					cinit.append (cdecl);
+				}
+
 				var st = m.parent_symbol as Struct;
 				if (m is CreationMethod && st != null && (st.is_boolean_type () || st.is_integer_type () || st.is_floating_type ())) {
 					var cdecl = new CCodeDeclaration (st.get_cname ());
@@ -1491,6 +1499,17 @@ internal class Vala.DovaObjectModule : DovaArrayModule {
 
 			generate_type_declaration (m.return_type, decl_space);
 		}
+
+		if (m.get_error_types ().size > 0 || (m.base_method != null && m.base_method.get_error_types ().size > 0)) {
+			var cparam = new CCodeFormalParameter ("error", "DovaError**");
+			func.add_parameter (cparam);
+			if (vdeclarator != null) {
+				vdeclarator.add_parameter (cparam);
+			}
+			if (vcall != null) {
+				vcall.add_argument (new CCodeIdentifier ("error"));
+			}
+		}
 	}
 
 	public override void visit_element_access (ElementAccess expr) {
diff --git a/vala/valacatchclause.vala b/vala/valacatchclause.vala
index 4ac98be..efe71d0 100644
--- a/vala/valacatchclause.vala
+++ b/vala/valacatchclause.vala
@@ -124,7 +124,12 @@ public class Vala.CatchClause : CodeNode {
 
 			error_variable.checked = true;
 		} else {
-			error_type = new ErrorType (null, null, source_reference);
+			// generic catch clause
+			if (analyzer.context.profile == Profile.GOBJECT) {
+				error_type = new ErrorType (null, null, source_reference);
+			} else {
+				error_type = analyzer.error_type;
+			}
 		}
 
 		error_type.check (analyzer);
diff --git a/vala/valaflowanalyzer.vala b/vala/valaflowanalyzer.vala
index d95ef5f..37fc7ea 100644
--- a/vala/valaflowanalyzer.vala
+++ b/vala/valaflowanalyzer.vala
@@ -33,6 +33,7 @@ public class Vala.FlowAnalyzer : CodeVisitor {
 		public bool is_error_target { get; set; }
 		public ErrorDomain? error_domain { get; set; }
 		public ErrorCode? error_code { get; set; }
+		public Class? error_class { get; set; }
 		public bool is_finally_clause { get; set; }
 		public BasicBlock basic_block { get; set; }
 		public BasicBlock? last_block { get; set; }
@@ -53,11 +54,12 @@ public class Vala.FlowAnalyzer : CodeVisitor {
 			is_return_target = true;
 		}
 
-		public JumpTarget.error_target (BasicBlock basic_block, CatchClause catch_clause, ErrorDomain? error_domain, ErrorCode? error_code) {
+		public JumpTarget.error_target (BasicBlock basic_block, CatchClause catch_clause, ErrorDomain? error_domain, ErrorCode? error_code, Class? error_class) {
 			this.basic_block = basic_block;
 			this.catch_clause = catch_clause;
 			this.error_domain = error_domain;
 			this.error_code = error_code;
+			this.error_class = error_class;
 			is_error_target = true;
 		}
 
@@ -836,6 +838,7 @@ public class Vala.FlowAnalyzer : CodeVisitor {
 			// exceptional control flow
 			foreach (DataType error_data_type in node.get_error_types()) {
 				var error_type = error_data_type as ErrorType;
+				var error_class = error_data_type.data_type as Class;
 				current_block = last_block;
 				unreachable_reported = true;
 
@@ -847,20 +850,34 @@ public class Vala.FlowAnalyzer : CodeVisitor {
 						unreachable_reported = false;
 						break;
 					} else if (jump_target.is_error_target) {
-						if (jump_target.error_domain == null
-						    || (jump_target.error_domain == error_type.error_domain
-						        && (jump_target.error_code == null
-						            || jump_target.error_code == error_type.error_code))) {
+						if (context.profile == Profile.GOBJECT) {
+							if (jump_target.error_domain == null
+							    || (jump_target.error_domain == error_type.error_domain
+								&& (jump_target.error_code == null
+								    || jump_target.error_code == error_type.error_code))) {
+								// error can always be caught by this catch clause
+								// following catch clauses cannot be reached by this error
+								current_block.connect (jump_target.basic_block);
+								current_block = null;
+								unreachable_reported = false;
+								break;
+							} else if (error_type.error_domain == null
+								   || (error_type.error_domain == jump_target.error_domain
+								       && (error_type.error_code == null
+								           || error_type.error_code == jump_target.error_code))) {
+								// error might be caught by this catch clause
+								// unknown at compile time
+								// following catch clauses might still be reached by this error
+								current_block.connect (jump_target.basic_block);
+							}
+						} else if (jump_target.error_class == null || jump_target.error_class == error_class) {
 							// error can always be caught by this catch clause
 							// following catch clauses cannot be reached by this error
 							current_block.connect (jump_target.basic_block);
 							current_block = null;
 							unreachable_reported = false;
 							break;
-						} else if (error_type.error_domain == null
-						           || (error_type.error_domain == jump_target.error_domain
-						               && (error_type.error_code == null
-						                   || error_type.error_code == jump_target.error_code))) {
+						} else if (jump_target.error_class.is_subtype_of (error_class)) {
 							// error might be caught by this catch clause
 							// unknown at compile time
 							// following catch clauses might still be reached by this error
@@ -934,10 +951,15 @@ public class Vala.FlowAnalyzer : CodeVisitor {
 		for (int i = catch_clauses.size - 1; i >= 0; i--) {
 			var catch_clause = catch_clauses[i];
 			if (catch_clause.error_type != null) {
-				var error_type = catch_clause.error_type as ErrorType;
-				jump_stack.add (new JumpTarget.error_target (new BasicBlock (), catch_clause, catch_clause.error_type.data_type as ErrorDomain, error_type.error_code));
+				if (context.profile == Profile.GOBJECT) {
+					var error_type = catch_clause.error_type as ErrorType;
+					jump_stack.add (new JumpTarget.error_target (new BasicBlock (), catch_clause, catch_clause.error_type.data_type as ErrorDomain, error_type.error_code, null));
+				} else {
+					var error_class = catch_clause.error_type.data_type as Class;
+					jump_stack.add (new JumpTarget.error_target (new BasicBlock (), catch_clause, null, null, error_class));
+				}
 			} else {
-				jump_stack.add (new JumpTarget.error_target (new BasicBlock (), catch_clause, null, null));
+				jump_stack.add (new JumpTarget.error_target (new BasicBlock (), catch_clause, null, null, null));
 			}
 		}
 
@@ -968,8 +990,14 @@ public class Vala.FlowAnalyzer : CodeVisitor {
 					break;
 				}
 
-				if (prev_target.error_domain == jump_target.error_domain &&
-				  prev_target.error_code == jump_target.error_code) {
+				if (context.profile == Profile.GOBJECT) {
+					if (prev_target.error_domain == jump_target.error_domain &&
+					    prev_target.error_code == jump_target.error_code) {
+						Report.error (stmt.source_reference, "double catch clause of same error detected");
+						stmt.error = true;
+						return;
+					}
+				} else if (prev_target.error_class == jump_target.error_class) {
 					Report.error (stmt.source_reference, "double catch clause of same error detected");
 					stmt.error = true;
 					return;
diff --git a/vala/valamethod.vala b/vala/valamethod.vala
index 3619638..61a5aa1 100644
--- a/vala/valamethod.vala
+++ b/vala/valamethod.vala
@@ -919,7 +919,8 @@ public class Vala.Method : Member {
 						can_propagate_error = true;
 					}
 				}
-				if (!can_propagate_error && !((ErrorType) body_error_type).dynamic_error) {
+				bool is_dynamic_error = body_error_type is ErrorType && ((ErrorType) body_error_type).dynamic_error;
+				if (!can_propagate_error && !is_dynamic_error) {
 					Report.warning (body_error_type.source_reference, "unhandled error `%s'".printf (body_error_type.to_string()));
 				}
 			}
diff --git a/vala/valasemanticanalyzer.vala b/vala/valasemanticanalyzer.vala
index 4981738..70685dc 100644
--- a/vala/valasemanticanalyzer.vala
+++ b/vala/valasemanticanalyzer.vala
@@ -155,6 +155,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
 	public Class gerror_type;
 	public DataType list_type;
 	public DataType tuple_type;
+	public DataType error_type;
 
 	public int next_lambda_id = 0;
 
@@ -224,6 +225,7 @@ public class Vala.SemanticAnalyzer : CodeVisitor {
 			type_type = new ObjectType ((Class) dova_ns.scope.lookup ("Type"));
 			list_type = new ObjectType ((Class) dova_ns.scope.lookup ("List"));
 			tuple_type = new ObjectType ((Class) dova_ns.scope.lookup ("Tuple"));
+			error_type = new ObjectType ((Class) dova_ns.scope.lookup ("Error"));
 		}
 
 		current_symbol = root_symbol;
diff --git a/vala/valathrowstatement.vala b/vala/valathrowstatement.vala
index 2a90ba3..e3d5da9 100644
--- a/vala/valathrowstatement.vala
+++ b/vala/valathrowstatement.vala
@@ -1,6 +1,6 @@
 /* valathrowstatement.vala
  *
- * Copyright (C) 2007-2009  Jürg Billeter
+ * Copyright (C) 2007-2010  Jürg Billeter
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -94,7 +94,7 @@ public class Vala.ThrowStatement : CodeNode, Statement {
 				return false;
 			}
 
-			if (!(error_expression.value_type is ErrorType)) {
+			if (analyzer.context.profile == Profile.GOBJECT && !(error_expression.value_type is ErrorType)) {
 				Report.error (error_expression.source_reference, "`%s' is not an error type".printf (error_expression.value_type.to_string ()));
 				error = true;
 				return false;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]