[m-rev.] for review: try goals

Peter Wang novalazy at gmail.com
Tue Feb 17 17:50:15 AEDT 2009


Branches: main

Add support for `try' goals, syntactic sugar on top of the exception handling
predicates in the standard library.  The syntax is documented in the reference
manual.  Currently one of the proposed try parameters, io(!IO), is implemented,
and the user must import the `exception' and `univ' modules explicitly.

Try goals are implemented by *two* source-to-source transformations, one at the
parse tree level, then a later one on the HLDS.  The reason for this is given
in try_expand.m.


library/ops.m:
	Add three new operators: try, catch, catch_any.

compiler/prog_item.m:
	Add representations of try goals to parse tree.

compiler/prog_io_goal.m:
	Parse try goals.

	Unrelated: fix spelling of "parameter".

compiler/add_clause.m:
	Perform the initial transformation on try goals.

compiler/hlds_goal.m:
	Add try goals to HLDS, as shorthand goals.

compiler/try_expand.m:
	New module to perform the latter transformation on try goals.

compiler/check_hlds.m:
	Import the new module.

compiler/mercury_compile.m:
	Call the try_expand pass near the end of the front-end pass.

compiler/det_analysis.m:
	Conform to addition of try goals in HLDS.  Make it not wrap a commit
	scope around the intermediate try goal (as it might if there are no
	outputs) as it is inappropriate.

compiler/prog_util.m:
	Conform to parse tree changes.

compiler/assertion.m:
compiler/build_mode_constraints.m:
compiler/cse_detection.m:
compiler/delay_partial_inst.m:
compiler/dependency_graph.m:
compiler/det_report.m:
compiler/equiv_type_hlds.m:
compiler/format_call.m:
compiler/goal_form.m:
compiler/goal_path.m:
compiler/goal_util.m:
compiler/hlds_out.m:
compiler/intermod.m:
compiler/lambda.m:
compiler/make_hlds_warn.m:
compiler/mercury_to_mercury.m:
compiler/mode_util.m:
compiler/modes.m:
compiler/module_imports.m:
compiler/module_qual.m:
compiler/ordering_mode_constraints.m:
compiler/polymorphism.m:
compiler/post_typecheck.m:
compiler/prop_mode_constraints.m:
compiler/purity.m:
compiler/quantification.m:
compiler/simplify.m:
compiler/stm_expand.m:
compiler/stratify.m:
compiler/structure_reuse.lfu.m:
compiler/switch_detection.m:
compiler/typecheck.m:
compiler/unique_modes.m:
compiler/unused_imports.m:
	Conform to changes in HLDS.

library/exception.m:
	Add two helper predicates for the try goal transformations.

doc/reference_manual.texi:
NEWS:
	Document and announce the new language feature.

compiler/notes/compiler_design.html:
	Note new compiler module.

tests/hard_coded/Mmakefile:
tests/hard_coded/try_syntax_1.exp:
tests/hard_coded/try_syntax_1.m:
tests/hard_coded/try_syntax_2.exp:
tests/hard_coded/try_syntax_2.m:
tests/hard_coded/try_syntax_3.exp:
tests/hard_coded/try_syntax_3.m:
tests/hard_coded/try_syntax_4.exp:
tests/hard_coded/try_syntax_4.m:
tests/hard_coded/try_syntax_5.exp:
tests/hard_coded/try_syntax_5.m:
tests/hard_coded/try_syntax_6.exp:
tests/hard_coded/try_syntax_6.m:
tests/invalid/Mmakefile:
tests/invalid/try_bad_params.err_exp:
tests/invalid/try_bad_params.m:
tests/invalid/try_io_else.err_exp:
tests/invalid/try_io_else.m:
	Add test cases.

tests/invalid/trace_goal_env.err_exp:
	Update test case for fixed spelling for "parameter".

vim/syntax/mercury.vim:
	Add try, catch, catch_any as keywords.

diff --git a/NEWS b/NEWS
index 7906955..efc2d6a 100644
--- a/NEWS
+++ b/NEWS
@@ -27,6 +27,7 @@ Changes to the Mercury language:
 * We now support currying of multi-moded predicates or functions when the
   mode to curry can be determined from the insts of the higher-order
   arguments.
+* We now support `try' goals for catching exceptions.

 Changes to the Mercury standard library:

@@ -426,6 +427,32 @@ Changes to the Mercury language:
 	...
 	Z = apply(AnyFunc, W)			% W = Z + A

+* Try goals provide syntactic sugar for catching exceptions. An example is:
+
+	:- pred p_carefully(io::di, io::uo) is det.
+
+	p_carefully(!IO) :-
+		(try [io(!IO)] (
+			io.write_string("Calling p\n", !IO),
+			p(Output, !IO)
+		)
+		then
+			io.write_string("p returned: ", !IO),
+			io.write(Output, !IO),
+			io.nl(!IO)
+		catch S ->
+			io.write_string("p threw a string: ", !IO),
+			io.write_string(S, !IO),
+			io.nl(!IO)
+		catch 42 ->
+			io.write_string("p threw 42\n", !IO)
+		catch_any Other ->
+			io.write_string("p threw something: ", !IO),
+			io.write(Other, !IO),
+			% Rethrow the object.
+			throw(X)
+		).
+
 Changes to the Mercury compiler:

 * The option `--trail-segments', or grade component `.trseg', causes
diff --git a/compiler/add_clause.m b/compiler/add_clause.m
index bce5fef..d51ef39 100644
--- a/compiler/add_clause.m
+++ b/compiler/add_clause.m
@@ -809,6 +809,34 @@ transform_goal_2(
     Reason = trace_goal(MaybeCompileTime, MaybeRunTime, MaybeIOHLDS,
         MutableHLDSs, Vars),
     goal_info_init(GoalInfo).
+transform_goal_2(
+        try_expr(MaybeIO0, Goal0, Then0, MaybeElse0, Catches0, MaybeCatchAny0),
+        Context, Renaming, TryGoal, NumAdded,
+        !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
+    (
+        MaybeIO0 = yes(IOStateVar0),
+        (
+            MaybeElse0 = no,
+            rename_var(need_not_rename, Renaming, IOStateVar0, IOStateVar),
+            transform_try_expr_with_io(IOStateVar0, IOStateVar, Goal0, Then0,
+                Catches0, MaybeCatchAny0, Context, Renaming, TryGoal,
+                NumAdded, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs)
+        ;
+            MaybeElse0 = yes(_),
+            Pieces = [words("Error: a `try' goal with an `io' parameter"),
+                words("cannot have an else part."), nl],
+            Msg = simple_msg(Context, [always(Pieces)]),
+            Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
+            !:Specs = [Spec | !.Specs],
+            TryGoal = true_goal,
+            NumAdded = 0
+        )
+    ;
+        MaybeIO0 = no,
+        transform_try_expr_without_io(Goal0, Then0, MaybeElse0, Catches0,
+            MaybeCatchAny0, Context, Renaming, TryGoal, NumAdded,
+            !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs)
+    ).
 transform_goal_2(if_then_else_expr(Vars0, StateVars0, Cond0, Then0, Else0),
         Context, Renaming,
         hlds_goal(if_then_else(Vars, Cond, Then, Else), GoalInfo),
@@ -1369,6 +1397,294 @@ transform_orelse_goals([Goal | Goals],
Renaming, [DisjInfo | DisjInfos],
         !NumAdded, !VarSet, !ModuleInfo, !QualInfo, SInfo0, !Specs).

 %----------------------------------------------------------------------------%
+%
+% Try goals
+%
+
+    % Transform a try_expr which needs to perform I/O.  The end result looks
+    % like:
+    %
+    %   magic_exception_result(TryResult),
+    %   (
+    %       TryResult = succeeded({}),
+    %       some [] (
+    %           !:IO = !.IO,
+    %           Goal
+    %       ),
+    %       some [] ( Then )
+    %   ;
+    %       TryResult = exception(Excp),
+    %       ExcpHandling
+    %   )
+    %
+    % Unlike in the non-I/O case, we have to transform the three pieces Goal,
+    % Then, ExcpHandling separately then stitch them together into HLDS goals.
+    % This is because we need to find out the variable for !.IO at the end of
+    % Goal, before entering Then.  The variable will be used in the later
+    % post-transformation.
+    %
+:- pred transform_try_expr_with_io(svar::in, svar::in, goal::in, goal::in,
+    list(catch_expr)::in, maybe(catch_any_expr)::in,
+    prog_context::in, prog_var_renaming::in, hlds_goal::out, int::out,
+    prog_varset::in, prog_varset::out, module_info::in, module_info::out,
+    qual_info::in, qual_info::out, svar_info::in, svar_info::out,
+    list(error_spec)::in, list(error_spec)::out) is det.
+
+transform_try_expr_with_io(IOStateVarUnrenamed, IOStateVar, Goal0, Then0,
+        Catches0, MaybeCatchAny0, Context, Renaming, TryGoal, NumAdded,
+        !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
+    varset.new_named_var(!.VarSet, "TryResult", ResultVar, !:VarSet),
+    varset.new_var(!.VarSet, ExcpVar, !:VarSet),
+
+    ResultVarTerm = variable(ResultVar, Context),
+    ExcpVarTerm = variable(ExcpVar, Context),
+    NullTupleTerm = functor(atom("{}"), [], Context),
+
+    goal_info_init(Context, GoalInfo),
+
+    % Make the call to magic_exception_result.
+    CallMagic0 = call_expr(magic_exception_result_sym_name, [ResultVarTerm],
+        purity_pure) - Context,
+    transform_goal(CallMagic0, Renaming, CallMagic, NumAddedA,
+        !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
+
+    % Get the variable for !.IO before the (eventual) try_io call.
+    svar_dot(Context, IOStateVar, IOStateVarBefore, !VarSet, !SInfo, !Specs),
+
+    SInfoBeforeDisjunction = !.SInfo,
+
+    % Build "TryResult = succeeded({})".
+    ResultIsSucceededUnify0 =
+        unify_expr(
+            ResultVarTerm,
+            functor(atom("succeeded"), [NullTupleTerm], Context),
+            purity_pure
+        ) - Context,
+    transform_goal(ResultIsSucceededUnify0, Renaming, ResultIsSucceededUnify,
+        NumAddedB, !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
+
+    % Build "some [] ( !:IO = !.IO, Goal )".
+    %
+    % The explicit unification avoids a degenerate case where Goal doesn't bind
+    % the final !:IO variable, which would lead to trouble later when we move
+    % Goal into its own lambda.
+    IOUnify = unify_expr(
+        functor(atom("!:"), [variable(IOStateVarUnrenamed, Context)], Context),
+        functor(atom("!."), [variable(IOStateVarUnrenamed, Context)], Context),
+        purity_pure
+    ) - Context,
+    ScopedGoal0 = some_expr([], conj_expr(IOUnify, Goal0) - Context) - Context,
+    transform_goal(ScopedGoal0, Renaming, ScopedGoal, NumAddedC,
+        !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
+
+    % Remember the variable for !.IO after the (eventual) try_io Goal.
+    svar_dot(Context, IOStateVar, IOStateVarAfter, !VarSet, !SInfo, !Specs),
+
+    % Build "some [] ( Then )".
+    ScopedThen0 = some_expr([], Then0) - Context,
+    transform_goal(ScopedThen0, Renaming, ScopedThen, NumAddedD,
+        !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
+
+    % Build:
+    %
+    %   TryResult = succeeded({}),
+    %   some [] ( !:IO = !.IO, Goal ),
+    %   some [] ( Then )
+    %
+    conj_list_to_goal([ResultIsSucceededUnify, ScopedGoal, ScopedThen],
+        GoalInfo, ResultIsSucceededDisjunct),
+
+    SInfoAfterResultIsSucceededDisjunct = !.SInfo,
+    !:SInfo = SInfoBeforeDisjunction,
+
+    % Build the disjunct for "TryResult = exception(Excp), ...".
+    make_exception_handling_disjunct(ResultVarTerm, ExcpVarTerm, Catches0,
+        MaybeCatchAny0, Context, ResultIsExceptionDisjunct0),
+    transform_goal(ResultIsExceptionDisjunct0, Renaming,
+        ResultIsExceptionDisjunct, NumAddedE,
+        !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
+
+    SInfoAfterResultIsExceptionDisjunct = !.SInfo,
+
+    % Get the disjuncts.
+    DisjunctSInfos = [
+        {ResultIsSucceededDisjunct, SInfoAfterResultIsSucceededDisjunct},
+        {ResultIsExceptionDisjunct, SInfoAfterResultIsExceptionDisjunct}
+    ],
+    svar_finish_disjunction(Context, !.VarSet, DisjunctSInfos,
+        Disjuncts, !:SInfo),
+    disj_list_to_goal(Disjuncts, GoalInfo, Disjunction),
+
+    % Build the call to magic_exception_result followed by the disjunction.
+    conj_list_to_goal([CallMagic, Disjunction], GoalInfo,
+        CallMagicThenDisjunction),
+
+    IOStateVars = try_io_state_vars(IOStateVarBefore, IOStateVarAfter),
+    GoalExpr = shorthand(try_goal(yes(IOStateVars), ResultVar,
+        CallMagicThenDisjunction)),
+    TryGoal = hlds_goal(GoalExpr, GoalInfo),
+
+    NumAdded = NumAddedA + NumAddedB + NumAddedC + NumAddedD + NumAddedE.
+
+    % Transform a try_expr which does not need I/O.
+    %
+    % If the try goal has an else part, the end result looks like:
+    %
+    %   magic_exception_result(TryResult),
+    %   (
+    %       TryResult = succeeded({}),
+    %       ( Goal ->
+    %           Then
+    %       ;
+    %           Else
+    %       )
+    %   ;
+    %       TryResult = exception(Excp),
+    %       ExcpHandling
+    %   )
+    %
+    % If the try goal does not have an else part, the end result looks like:
+    %
+    %   magic_exception_result(TryResult),
+    %   (
+    %       TryResult = succeeded({}),
+    %       some [] ( Goal ),
+    %       some [] ( Then )
+    %   ;
+    %       TryResult = exception(Excp),
+    %       ExcpHandling
+    %   )
+    %
+:- pred transform_try_expr_without_io(goal::in, goal::in, maybe(goal)::in,
+    list(catch_expr)::in, maybe(catch_any_expr)::in,
+    prog_context::in, prog_var_renaming::in, hlds_goal::out, int::out,
+    prog_varset::in, prog_varset::out, module_info::in, module_info::out,
+    qual_info::in, qual_info::out, svar_info::in, svar_info::out,
+    list(error_spec)::in, list(error_spec)::out) is det.
+
+transform_try_expr_without_io(Goal0, Then0, MaybeElse0, Catches0,
+        MaybeCatchAny0, Context, Renaming, TryGoal, NumAdded,
+        !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs) :-
+    varset.new_named_var(!.VarSet, "TryResult", ResultVar, !:VarSet),
+    varset.new_var(!.VarSet, ExcpVar, !:VarSet),
+
+    ResultVarTerm = variable(ResultVar, Context),
+    ExcpVarTerm = variable(ExcpVar, Context),
+    NullTupleTerm = functor(atom("{}"), [], Context),
+
+    goal_info_init(Context, GoalInfo),
+
+    % Build the call to magic_exception_result.
+    CallMagic0 = call_expr(magic_exception_result_sym_name, [ResultVarTerm],
+        purity_pure) - Context,
+
+    % Build "TryResult = succeeded({}), ..." disjunct.
+    ResultIsSucceededUnify0 =
+        unify_expr(
+            ResultVarTerm,
+            functor(atom("succeeded"), [NullTupleTerm], Context),
+            purity_pure
+        ) - Context,
+    (
+        MaybeElse0 = yes(Else0),
+        SucceededSubGoal =
+            if_then_else_expr([], [], Goal0, Then0, Else0) - Context
+    ;
+        MaybeElse0 = no,
+        SucceededSubGoal =
+            conj_expr(
+                some_expr([], Goal0) - Context,
+                some_expr([], Then0) - Context
+            ) - Context
+    ),
+    ResultIsSucceededDisjunct0 =
+        conj_expr(ResultIsSucceededUnify0, SucceededSubGoal) - Context,
+
+    % Build the disjunct for "TryResult = exception(Excp), ...".
+    make_exception_handling_disjunct(ResultVarTerm, ExcpVarTerm, Catches0,
+        MaybeCatchAny0, Context, ResultIsExceptionDisjunct0),
+
+    % Build the call followed by the disjunction.
+    CallMagicThenDisjunction0 =
+        conj_expr(
+            CallMagic0,
+            disj_expr(
+                ResultIsSucceededDisjunct0,
+                ResultIsExceptionDisjunct0
+            ) - Context
+        ) - Context,
+    transform_goal(CallMagicThenDisjunction0, Renaming,
+        CallMagicThenDisjunction, NumAdded,
+        !VarSet, !ModuleInfo, !QualInfo, !SInfo, !Specs),
+
+    GoalExpr = shorthand(try_goal(no, ResultVar, CallMagicThenDisjunction)),
+    TryGoal = hlds_goal(GoalExpr, GoalInfo).
+
+:- pred make_exception_handling_disjunct(prog_term::in, prog_term::in,
+    list(catch_expr)::in, maybe(catch_any_expr)::in, prog_context::in,
+    goal::out) is det.
+
+make_exception_handling_disjunct(ResultVarTerm, ExcpVarTerm, Catches,
+        MaybeCatchAny, Context, Goal) :-
+    ResultIsExceptionUnify =
+        unify_expr(
+            ResultVarTerm,
+            functor(atom("exception"), [ExcpVarTerm], Context),
+            purity_pure
+        ) - Context,
+    make_catch_ite_chain(ResultVarTerm, ExcpVarTerm, Catches, MaybeCatchAny,
+        CatchChain),
+    Goal = conj_expr(ResultIsExceptionUnify, CatchChain) - Context.
+
+:- pred make_catch_ite_chain(prog_term::in, prog_term::in,
+    list(catch_expr)::in, maybe(catch_any_expr)::in, goal::out) is det.
+
+make_catch_ite_chain(ResultVarTerm, ExcpVarTerm, Catches, MaybeCatchAny,
+        Goal) :-
+    (
+        Catches = [catch_expr(FirstPattern, FirstGoal) | RestCatches],
+        make_catch_ite_chain(ResultVarTerm, ExcpVarTerm, RestCatches,
+            MaybeCatchAny, ElseGoal),
+        make_catch_pattern_unify_goal(FirstPattern, ExcpVarTerm,
+            FirstPatternGoal),
+        Goal = if_then_else_expr([], [], FirstPatternGoal, FirstGoal,
+            ElseGoal) - get_term_context(FirstPattern)
+    ;
+        Catches = [],
+        (
+            MaybeCatchAny = yes(catch_any_expr(CatchAnyVar, CatchAnyGoal)),
+            % With a catch_any part, end the if-then-else chain with:
+            %   CatchAnyVar = univ_value(Excp),
+            %   CatchAnyGoal
+            CatchAnyGoal = _ - Context,
+            GetUnivValue = unify_expr(
+                variable(CatchAnyVar, Context),
+                functor(atom("univ_value"), [ExcpVarTerm], Context),
+                purity_pure) - Context,
+            Goal = conj_expr(GetUnivValue, CatchAnyGoal) - Context
+        ;
+            MaybeCatchAny = no,
+            % Without a catch_any part, end the if-then-else chain
+            % by rethrowing the exception.
+            Goal = call_expr(string_to_sym_name("exception.rethrow"),
+                [ResultVarTerm], purity_pure) - get_term_context(ExcpVarTerm)
+        )
+    ).
+
+:- pred make_catch_pattern_unify_goal(prog_term::in, prog_term::in,
+    goal::out) is det.
+
+make_catch_pattern_unify_goal(CatchPatternTerm, ExcpVarTerm, Goal) :-
+    GoalExpr = call_expr(string_to_sym_name("univ_to_type"),
+        [ExcpVarTerm, CatchPatternTerm], purity_pure),
+    Goal = GoalExpr - get_term_context(CatchPatternTerm).
+
+:- func magic_exception_result_sym_name = sym_name.
+
+magic_exception_result_sym_name =
+    qualified(mercury_exception_module, "magic_exception_result").
+
+%----------------------------------------------------------------------------%

 :- func this_file = string.

diff --git a/compiler/assertion.m b/compiler/assertion.m
index c5ebff5..6c9ab8d 100644
--- a/compiler/assertion.m
+++ b/compiler/assertion.m
@@ -715,6 +715,10 @@ normalise_goal_expr(GoalExpr0, GoalExpr) :-
             ShortHand = atomic_goal(GoalType, Outer, Inner, Vars, MainGoal,
                 OrElseAlternatives)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            normalise_goal(SubGoal0, SubGoal),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
+        ;
             ShortHand0 = bi_implication(LHS0, RHS0),
             normalise_goal(LHS0, LHS),
             normalise_goal(RHS0, RHS),
diff --git a/compiler/build_mode_constraints.m
b/compiler/build_mode_constraints.m
index cd071ae..6262bdb 100644
--- a/compiler/build_mode_constraints.m
+++ b/compiler/build_mode_constraints.m
@@ -341,6 +341,9 @@ add_mc_vars_for_goal(PredId, ProgVarset,
hlds_goal(GoalExpr, GoalInfo),
         ;
             ShortHand = bi_implication(_, _),
             unexpected(this_file, "add_mc_vars_for_goal: bi_implication")
+        ;
+            ShortHand = try_goal(_, _, _),
+            sorry(this_file, "add_mc_vars_for_goal: try_goal")
         )
     ).

@@ -673,6 +676,9 @@ add_goal_expr_constraints(ModuleInfo, ProgVarset,
PredId, GoalExpr,
             Shorthand = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file, "shorthand goal")
+        ;
+            Shorthand = try_goal(_, _, _),
+            sorry(this_file, "NYI: try_goal")
         )
     ).

diff --git a/compiler/check_hlds.m b/compiler/check_hlds.m
index 5d4a7c7..5e03b97 100644
--- a/compiler/check_hlds.m
+++ b/compiler/check_hlds.m
@@ -72,6 +72,9 @@
 % Stratification.
 :- include_module stratify.

+% Expand try goals
+:- include_module try_expand.
+
 % Warnings about simple code
 :- include_module common.
 :- include_module format_call.
diff --git a/compiler/cse_detection.m b/compiler/cse_detection.m
index 30376b9..27ca967 100644
--- a/compiler/cse_detection.m
+++ b/compiler/cse_detection.m
@@ -337,6 +337,12 @@ detect_cse_in_goal_expr(GoalExpr0, GoalExpr,
!CseInfo, GoalInfo, InstMap0,
             ShortHand0 = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file, "detect_cse_in_goal_expr: bi_implication")
+        ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            % XXX not sure about this as SubGoal0 is not in its final form.
+            % Also, mightn't the try "Goal" part get hoisted out?
+            detect_cse_in_goal(SubGoal0, SubGoal, !CseInfo, InstMap0, Redo),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
         ),
         GoalExpr = shorthand(ShortHand)
     ).
diff --git a/compiler/delay_partial_inst.m b/compiler/delay_partial_inst.m
index e0d7920..fcf265b 100644
--- a/compiler/delay_partial_inst.m
+++ b/compiler/delay_partial_inst.m
@@ -443,6 +443,13 @@ delay_partial_inst_in_goal(InstMap0, Goal0, Goal,
!ConstructMap, !DelayInfo) :-
             GoalExpr = shorthand(ShortHand),
             Goal = hlds_goal(GoalExpr, GoalInfo0)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            delay_partial_inst_in_goal(InstMap0, SubGoal0, SubGoal,
+                !ConstructMap, !DelayInfo),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal),
+            GoalExpr = shorthand(ShortHand),
+            Goal = hlds_goal(GoalExpr, GoalInfo0)
+        ;
             ShortHand0 = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file,
diff --git a/compiler/dependency_graph.m b/compiler/dependency_graph.m
index 07f1c65..3459715 100644
--- a/compiler/dependency_graph.m
+++ b/compiler/dependency_graph.m
@@ -427,6 +427,9 @@ add_dependency_arcs_in_goal(Caller, Goal, !DepGraph) :-
             add_dependency_arcs_in_goal(Caller, MainGoal, !DepGraph),
             add_dependency_arcs_in_list(Caller, OrElseGoals, !DepGraph)
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            add_dependency_arcs_in_goal(Caller, SubGoal, !DepGraph)
+        ;
             ShortHand = bi_implication(LHS, RHS),
             add_dependency_arcs_in_list(Caller, [LHS, RHS], !DepGraph)
         )
diff --git a/compiler/det_analysis.m b/compiler/det_analysis.m
index d20eaa3..7a4b373 100644
--- a/compiler/det_analysis.m
+++ b/compiler/det_analysis.m
@@ -386,10 +386,10 @@ get_exported_proc_context([Proc | Procs],
PredId, ProcId, Context) :-

 %-----------------------------------------------------------------------------%

-det_infer_goal(hlds_goal(GoalExpr0, GoalInfo0), hlds_goal(GoalExpr, GoalInfo),
-        InstMap0, !.SolnContext, RightFailingContexts,
+det_infer_goal(Goal0, Goal, InstMap0, !.SolnContext, RightFailingContexts,
         MaybePromiseEqvSolutionSets, Detism, GoalFailingContexts,
         !DetInfo, !Specs) :-
+    Goal0 = hlds_goal(_, GoalInfo0),
     NonLocalVars = goal_info_get_nonlocals(GoalInfo0),
     InstmapDelta = goal_info_get_instmap_delta(GoalInfo0),

@@ -410,6 +410,23 @@ det_infer_goal(hlds_goal(GoalExpr0, GoalInfo0),
hlds_goal(GoalExpr, GoalInfo),
     ;
         AddPruning = no
     ),
+
+    det_infer_goal_1(Goal0, Goal, InstMap0, !.SolnContext,
RightFailingContexts,
+        MaybePromiseEqvSolutionSets, AddPruning, Detism, GoalFailingContexts,
+        !DetInfo, !Specs).
+
+:- pred det_infer_goal_1(hlds_goal::in, hlds_goal::out, instmap::in,
+    soln_context::in, list(failing_context)::in, maybe(pess_info)::in,
+    bool::in, determinism::out, list(failing_context)::out,
+    det_info::in, det_info::out, list(error_spec)::in, list(error_spec)::out)
+    is det.
+
+det_infer_goal_1(hlds_goal(GoalExpr0, GoalInfo0), hlds_goal(GoalExpr,
GoalInfo),
+        InstMap0, !.SolnContext, RightFailingContexts,
+        MaybePromiseEqvSolutionSets, AddPruning, Detism, GoalFailingContexts,
+        !DetInfo, !Specs) :-
+    InstmapDelta = goal_info_get_instmap_delta(GoalInfo0),
+
     (
         GoalExpr0 = scope(ScopeReason, _),
         (
@@ -638,6 +655,15 @@ det_infer_goal_2(GoalExpr0, GoalExpr, GoalInfo,
InstMap0, SolnContext,
             ShortHand = atomic_goal(GoalType, Inner, Outer, Vars, MainGoal,
                 OrElseGoals)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, TryGoal0),
+            % Don't allow det_infer_goal_1 to insert a commit scope around the
+            % code that's standing in place for the code we'll actually create
+            % for a try goal.
+            det_infer_goal_1(TryGoal0, TryGoal, InstMap0, SolnContext,
+                RightFailingContexts, MaybePromiseEqvSolutionSets, no, Detism,
+                GoalFailingContexts, !DetInfo, !Specs),
+            ShortHand = try_goal(MaybeIO, ResultVar, TryGoal)
+        ;
             ShortHand0 = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file, "det_infer_goal_2: bi_implication")
diff --git a/compiler/det_report.m b/compiler/det_report.m
index 6019399..ff0f856 100644
--- a/compiler/det_report.m
+++ b/compiler/det_report.m
@@ -695,6 +695,10 @@ det_diagnose_goal_expr(GoalExpr, GoalInfo,
InstMap0, Desired, Actual,
                 SwitchContexts, !DetInfo, OrElseMsgs),
             Msgs = MainMsgs ++ OrElseMsgs
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            det_diagnose_goal(SubGoal, InstMap0, Desired, SwitchContexts,
+                !DetInfo, Msgs)
+        ;
             ShortHand = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file, "det_diagnose_goal_expr: bi_implication")
diff --git a/compiler/equiv_type_hlds.m b/compiler/equiv_type_hlds.m
index 5629d50..1fa1300 100644
--- a/compiler/equiv_type_hlds.m
+++ b/compiler/equiv_type_hlds.m
@@ -959,6 +959,11 @@ replace_in_goal_expr(EqvMap, GoalExpr0, GoalExpr,
Changed, !Info) :-
             GoalExpr = GoalExpr0
         )
     ;
+        ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+        replace_in_goal(EqvMap, SubGoal0, SubGoal, Changed, !Info),
+        ShortHand = try_goal(MaybeIO, ResultVar, SubGoal),
+        GoalExpr = shorthand(ShortHand)
+    ;
         ShortHand0 = bi_implication(_, _),
         unexpected(this_file, "replace_in_goal_expr: bi_implication")
     ).
diff --git a/compiler/format_call.m b/compiler/format_call.m
index b1352ee..3b1b5c8 100644
--- a/compiler/format_call.m
+++ b/compiler/format_call.m
@@ -485,6 +485,11 @@ traverse_conj([Goal | Goals], CurId,
!FormatCallSites, !Counter,
                 !FormatCallSites, !Counter, !ConjMaps, !PredMap, !RelevantVars,
                 ModuleInfo)
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            traverse_goal(SubGoal, SubGoalId, !FormatCallSites,
+                !Counter, !ConjMaps, !PredMap, !RelevantVars, ModuleInfo),
+            svmap.det_insert(SubGoalId, CurId, !PredMap)
+        ;
             ShortHand = bi_implication(_, _),
             % These should have been expanded by now.
             unexpected(this_file, "traverse_conj: bi_implication")
diff --git a/compiler/goal_form.m b/compiler/goal_form.m
index 472ffe5..b314546 100644
--- a/compiler/goal_form.m
+++ b/compiler/goal_form.m
@@ -279,6 +279,9 @@ goal_can_throw_2(GoalExpr, _GoalInfo, Result,
!ModuleInfo) :-
             % it is pretty safe to say that any goal inside an atomic goal
             % can throw an exception.
             Result = can_throw
+        ;
+            ShortHand = try_goal(_, _, _),
+            Result = can_throw
         )
     ).

@@ -455,6 +458,9 @@ goal_can_loop_func(MaybeModuleInfo, Goal) = CanLoop :-
             OrElseCanLoop = goal_list_can_loop(MaybeModuleInfo, OrElseGoals),
             CanLoop = MainGoalCanLoop `or` OrElseCanLoop
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            CanLoop = goal_can_loop_func(MaybeModuleInfo, SubGoal)
+        ;
             ShortHand = bi_implication(_, _),
             unexpected(this_file, "goal_can_loop: bi_implication")
         )
@@ -580,6 +586,9 @@ goal_expr_can_throw(MaybeModuleInfo, GoalExpr) = CanThrow :-
             ShortHand = atomic_goal(_, _, _, _, _, _),
             CanThrow = yes
         ;
+            ShortHand = try_goal(_, _, _),
+            CanThrow = yes
+        ;
             ShortHand = bi_implication(_, _),
             unexpected(this_file, "goal_expr_can_throw: bi_implication")
         )
@@ -746,7 +755,9 @@ goal_may_allocate_heap_2(GoalExpr, May) :-
     ;
         GoalExpr = shorthand(ShortHand),
         (
-            ShortHand = atomic_goal(_, _, _, _, _, _),
+            ( ShortHand = atomic_goal(_, _, _, _, _, _)
+            ; ShortHand = try_goal(_, _, _)
+            ),
             May = yes
         ;
             ShortHand = bi_implication(GoalA, GoalB),
@@ -912,6 +923,9 @@ count_recursive_calls(Goal, PredId, ProcId, Min, Max) :-
             count_recursive_calls_disj([MainGoal | OrElseGoals],
                 PredId, ProcId, Min, Max)
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            count_recursive_calls(SubGoal, PredId, ProcId, Min, Max)
+        ;
             ShortHand = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file, "count_recursive_calls: bi_implication")
diff --git a/compiler/goal_path.m b/compiler/goal_path.m
index dd375b3..8f2acfb 100644
--- a/compiler/goal_path.m
+++ b/compiler/goal_path.m
@@ -224,9 +224,13 @@ fill_expr_slots(GoalInfo, Path0, SlotInfo,
GoalExpr0, GoalExpr) :-
             ShortHand = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
                 MainGoal, OrElseGoals)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            fill_goal_slots(Path0, SlotInfo, SubGoal0, SubGoal),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
+        ;
             ShortHand0 = bi_implication(_, _),
             % These should have been expanded out by now.
-            unexpected(this_file, "fill_expr_slots: unexpected shorthand")
+            unexpected(this_file, "fill_expr_slots: unexpected bi_implication")
         ),
         GoalExpr = shorthand(ShortHand)
     ).
diff --git a/compiler/goal_util.m b/compiler/goal_util.m
index f071a52..26088e6 100644
--- a/compiler/goal_util.m
+++ b/compiler/goal_util.m
@@ -597,6 +597,10 @@ goal_vars_2(Goal, !Set) :-
             goal_vars_2(MainGoal, !Set),
             goals_goal_vars(OrElseGoals, !Set)
         ;
+            Shorthand = try_goal(_, _, SubGoal),
+            % The IO and Result variables would be in SubGoal.
+            goal_vars_2(SubGoal, !Set)
+        ;
             Shorthand = bi_implication(LeftGoal, RightGoal),
             goal_vars_2(LeftGoal, !Set),
             goal_vars_2(RightGoal, !Set)
@@ -740,6 +744,11 @@ attach_features_to_goal_expr(Features, InFromGroundTerm,
             ShortHand = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
                 MainGoal, OrElseGoals)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            attach_features_to_all_goals(Features, InFromGroundTerm,
+                SubGoal0, SubGoal),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
+        ;
             ShortHand0 = bi_implication(GoalA0, GoalB0),
             attach_features_to_all_goals(Features, InFromGroundTerm,
                 GoalA0, GoalA),
@@ -847,7 +856,9 @@ proc_body_is_leaf(hlds_goal(GoalExpr, _)) = IsLeaf :-
     ;
         GoalExpr = shorthand(ShortHand),
         (
-            ShortHand = atomic_goal(_, _, _, _, _, _),
+            ( ShortHand = atomic_goal(_, _, _, _, _, _)
+            ; ShortHand = try_goal(_, _, _)
+            ),
             IsLeaf = is_not_leaf
         ;
             ShortHand = bi_implication(GoalA, GoalB),
@@ -985,6 +996,11 @@ goal_expr_size(GoalExpr, Size) :-
             goals_size(OrElseGoals, Size2),
             Size = Size1 + Size2 + 1
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            % Hopefully this size isn't too important as the SubGoal is not yet
+            % in the final form.
+            goal_size(SubGoal, Size)
+        ;
             ShortHand = bi_implication(GoalA, GoalB),
             goal_size(GoalA, Size1),
             goal_size(GoalB, Size2),
@@ -1184,8 +1200,11 @@ goal_calls_proc_in_list_2(hlds_goal(GoalExpr,
_GoalInfo), PredProcIds,
             goal_list_calls_proc_in_list_2(OrElseGoals, PredProcIds,
                 !CalledSet)
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            goal_calls_proc_in_list_2(SubGoal, PredProcIds, !CalledSet)
+        ;
             ShortHand = bi_implication(_, _),
-            unexpected(this_file, "goal__calls_proc_in_list_2: bi_implication")
+            unexpected(this_file, "goal_calls_proc_in_list_2: bi_implication")
         )
     ).

@@ -1828,6 +1847,12 @@ maybe_strip_equality_pretest(Goal0) = Goal :-
             GoalExpr = shorthand(ShortHand),
             Goal = hlds_goal(GoalExpr, GoalInfo0)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            SubGoal = maybe_strip_equality_pretest(SubGoal0),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal),
+            GoalExpr = shorthand(ShortHand),
+            Goal = hlds_goal(GoalExpr, GoalInfo0)
+        ;
             ShortHand0 = bi_implication(_, _),
             unexpected(this_file,
                 "maybe_strip_equality_pretest: bi_implication")
diff --git a/compiler/hlds_goal.m b/compiler/hlds_goal.m
index bc8a398..785775b 100644
--- a/compiler/hlds_goal.m
+++ b/compiler/hlds_goal.m
@@ -278,6 +278,24 @@
                 % Any later or_else alternative goals.
                 orelse_alternatives :: list(hlds_goal)

+            )
+
+    ;       try_goal(
+                % A try goal.
+
+                % The variables holding the initial and final I/O states for
+                % the goal to be executed in the `try' proper, i.e. not
+                % inclusive of the I/O states that may be in the arms following
+                % the try.  Will be `no' if no `io(_)' component was specified.
+                try_maybe_io        :: maybe(try_io_state_vars),
+
+                % The variable that will hold the result of the `try' or
+                % `try_io' call.
+                try_result_var      :: prog_var,
+
+                % A "pre-transformed" version of the entire try goal.
+                % See try_expand.m for details.
+                try_goal            :: hlds_goal
             ).

 :- type atomic_interface_vars
@@ -286,6 +304,12 @@
                 atomic_final    :: prog_var
             ).

+:- type try_io_state_vars
+    --->    try_io_state_vars(
+                try_io_initial  :: prog_var,
+                try_io_final    :: prog_var
+            ).
+
     % If an atomic goal has type unknown_atomic_goal_type, then the conversion
     % predicates to and from the inner variables have not been added yet to the
     % main and orelse goals. If the type is top_level_atomic_goal or
@@ -295,6 +319,12 @@
     ;       top_level_atomic_goal
     ;       nested_atomic_goal.

+:- type catch
+    --->    catch(
+                catch_expr  :: hlds_goal_expr,
+                catch_goal  :: hlds_goal
+            ).
+
     % Each scope that is created from the expansion of a ground term above
     % a certain size is classified into one of these three categories.
     % The categories are for scopes that (a) construct a ground term, (b)
@@ -2415,6 +2445,20 @@ rename_vars_in_goal_expr(Must, Subn, Expr0, Expr) :-
             Shorthand = atomic_goal(GoalType, Outer, Inner,
                 MaybeOutputVars, MainGoal, OrElseGoals)
         ;
+            Shorthand0 = try_goal(MaybeIO0, ResultVar0, SubGoal0),
+            (
+                MaybeIO0 = yes(try_io_state_vars(IOVarInitial0, IOVarFinal0)),
+                rename_var(Must, Subn, IOVarInitial0, IOVarInitial),
+                rename_var(Must, Subn, IOVarFinal0, IOVarFinal),
+                MaybeIO = yes(try_io_state_vars(IOVarInitial, IOVarFinal))
+            ;
+                MaybeIO0 = no,
+                MaybeIO = no
+            ),
+            rename_var(Must, Subn, ResultVar0, ResultVar),
+            rename_vars_in_goal(Must, Subn, SubGoal0, SubGoal),
+            Shorthand = try_goal(MaybeIO, ResultVar, SubGoal)
+        ;
             Shorthand0 = bi_implication(LeftGoal0, RightGoal0),
             rename_vars_in_goal(Must, Subn, LeftGoal0, LeftGoal),
             rename_vars_in_goal(Must, Subn, RightGoal0, RightGoal),
@@ -2840,6 +2884,9 @@ goal_has_foreign(Goal) = HasForeign :-
             ShortHand = atomic_goal(_, _, _, _, _, _),
             HasForeign = yes
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            HasForeign = goal_has_foreign(SubGoal)
+        ;
             ShortHand = bi_implication(GoalA, GoalB),
             HasForeign = bool.or(goal_has_foreign(GoalA),
                 goal_has_foreign(GoalB))
@@ -2898,6 +2945,7 @@ goal_expr_has_subgoals(GoalExpr) = HasSubGoals :-
     ;
         GoalExpr = shorthand(ShortHand),
         ( ShortHand = atomic_goal(_, _, _, _, _, _)
+        ; ShortHand = try_goal(_, _, _)
         ; ShortHand = bi_implication(_, _)
         ),
         HasSubGoals = has_subgoals
@@ -3010,6 +3058,10 @@ set_goal_contexts(Context, Goal0, Goal) :-
             ShortHand = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
                 MainGoal, OrElseGoals)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            set_goal_contexts(Context, SubGoal0, SubGoal),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
+        ;
             ShortHand0 = bi_implication(LHS0, RHS0),
             set_goal_contexts(Context, LHS0, LHS),
             set_goal_contexts(Context, RHS0, RHS),
diff --git a/compiler/hlds_out.m b/compiler/hlds_out.m
index 3c1b02f..bb36bf4 100644
--- a/compiler/hlds_out.m
+++ b/compiler/hlds_out.m
@@ -831,8 +831,8 @@ maybe_write_pred(Indent, ModuleInfo, PredTable,
PredId, !IO) :-
         )
     ).

-    :- pred write_pred(int::in, module_info::in, pred_id::in, pred_info::in,
-        io::di, io::uo) is det.
+:- pred write_pred(int::in, module_info::in, pred_id::in, pred_info::in,
+    io::di, io::uo) is det.

 write_pred(Indent, ModuleInfo, PredId, PredInfo, !IO) :-
     Module = pred_info_module(PredInfo),
@@ -2233,6 +2233,24 @@ write_goal_2_shorthand(ShortHand, ModuleInfo,
VarSet, AppendVarNums,
         io.write_string(")", !IO),
         io.write_string(Follow, !IO)
     ;
+        ShortHand = try_goal(MaybeIO, _, SubGoal),
+        write_indent(Indent, !IO),
+        io.write_string("( % try\n", !IO),
+        (
+            MaybeIO = yes(try_io_state_vars(IOVarA, IOVarB)),
+            write_indent(Indent + 1, !IO),
+            io.write_string("% io(", !IO),
+            mercury_output_vars(VarSet, AppendVarNums, [IOVarA, IOVarB], !IO),
+            io.write_string(")\n", !IO)
+        ;
+            MaybeIO = no
+        ),
+        write_goal_a(SubGoal, ModuleInfo, VarSet, AppendVarNums,
+            Indent + 1, "\n", TypeQual, !IO),
+        write_indent(Indent, !IO),
+        io.write_string(")", !IO),
+        io.write_string(Follow, !IO)
+    ;
         ShortHand = bi_implication(GoalA, GoalB),
         write_indent(Indent, !IO),
         io.write_string("( % bi-implication\n", !IO),
diff --git a/compiler/intermod.m b/compiler/intermod.m
index 5b119d9..fe7091e 100644
--- a/compiler/intermod.m
+++ b/compiler/intermod.m
@@ -566,6 +566,10 @@ intermod_traverse_goal_expr(GoalExpr0, GoalExpr,
DoWrite, !Info) :-
             ShortHand = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
                 MainGoal, OrElseGoals)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            intermod_traverse_goal(SubGoal0, SubGoal, DoWrite, !Info),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
+        ;
             ShortHand0 = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file,
diff --git a/compiler/lambda.m b/compiler/lambda.m
index 00398af..14ef500 100644
--- a/compiler/lambda.m
+++ b/compiler/lambda.m
@@ -270,13 +270,17 @@ lambda_process_goal(Goal0, Goal, !Info) :-
             lambda_process_goal(MainGoal0, MainGoal, !Info),
             lambda_process_goal_list(OrElseGoals0, OrElseGoals, !Info),
             ShortHand = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
-                MainGoal, OrElseGoals),
-            GoalExpr = shorthand(ShortHand)
+                MainGoal, OrElseGoals)
+        ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            lambda_process_goal(SubGoal0, SubGoal, !Info),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
         ;
             ShortHand0 = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file, "lambda_process_goal_2: bi_implication")
-        )
+        ),
+        GoalExpr = shorthand(ShortHand)
     ),
     Goal = hlds_goal(GoalExpr, GoalInfo).

diff --git a/compiler/make_hlds_warn.m b/compiler/make_hlds_warn.m
index 626b195..7ce45d0 100644
--- a/compiler/make_hlds_warn.m
+++ b/compiler/make_hlds_warn.m
@@ -221,6 +221,10 @@ warn_singletons_in_goal(Goal, QuantVars, VarSet,
PredCallId, ModuleInfo,
             warn_singletons_in_goal_list(OrElseGoals, InsideQuantVars, VarSet,
                 PredCallId, ModuleInfo, !Specs)
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            warn_singletons_in_goal(SubGoal, QuantVars, VarSet,
+                PredCallId, ModuleInfo, !Specs)
+        ;
             ShortHand = bi_implication(GoalA, GoalB),
             warn_singletons_in_goal_list([GoalA, GoalB], QuantVars, VarSet,
                 PredCallId, ModuleInfo, !Specs)
diff --git a/compiler/mercury_compile.m b/compiler/mercury_compile.m
index 47e8347..8edec17 100644
--- a/compiler/mercury_compile.m
+++ b/compiler/mercury_compile.m
@@ -64,6 +64,7 @@
 :- import_module check_hlds.det_analysis.
 :- import_module check_hlds.unique_modes.
 :- import_module check_hlds.stratify.
+:- import_module check_hlds.try_expand.
 :- import_module check_hlds.simplify.

     % High-level HLDS transformations.
@@ -2603,6 +2604,9 @@ frontend_pass_by_phases(!HLDS, FoundError,
!DumpInfo, !IO) :-
         check_stratification(Verbose, Stats, !HLDS, FoundStratError, !IO),
         maybe_dump_hlds(!.HLDS, 60, "stratification", !DumpInfo, !IO),

+        process_try_goals(Verbose, Stats, !HLDS, FoundTryError, !IO),
+        maybe_dump_hlds(!.HLDS, 62, "try", !DumpInfo, !IO),
+
         simplify(yes, frontend, Verbose, Stats, !HLDS, SimplifySpecs, !IO),
         maybe_dump_hlds(!.HLDS, 65, "frontend_simplify", !DumpInfo, !IO),

@@ -2618,6 +2622,7 @@ frontend_pass_by_phases(!HLDS, FoundError,
!DumpInfo, !IO) :-
             FoundModeError = no,
             FoundUniqError = no,
             FoundStratError = no,
+            FoundTryError = no,
             NumErrors1 = 0,
             NumErrors2 = 0,
             % Strictly speaking, we shouldn't need to check the exit status.
@@ -3412,6 +3417,24 @@ check_stratification(Verbose, Stats, !HLDS,
FoundError, !IO) :-
         FoundError = no
     ).

+:- pred process_try_goals(bool::in, bool::in,
+    module_info::in, module_info::out, bool::out, io::di, io::uo) is det.
+
+process_try_goals(Verbose, Stats, !HLDS, FoundError, !IO) :-
+    maybe_write_string(Verbose, "% Transforming try goals...\n", !IO),
+    module_info_get_num_errors(!.HLDS, NumErrors0),
+    expand_try_goals(!HLDS, !IO),
+    module_info_get_num_errors(!.HLDS, NumErrors),
+    maybe_write_string(Verbose, "% done.\n", !IO),
+    ( NumErrors \= NumErrors0 ->
+        FoundError = yes,
+        maybe_write_string(Verbose, "% Program contains error(s).\n", !IO),
+        io.set_exit_status(1, !IO)
+    ;
+        FoundError = no
+    ),
+    maybe_report_stats(Stats, !IO).
+
 :- pred maybe_warn_dead_procs(bool::in, bool::in,
     module_info::in, module_info::out, io::di, io::uo) is det.

diff --git a/compiler/mercury_to_mercury.m b/compiler/mercury_to_mercury.m
index 1e77897..9d6bfb0 100644
--- a/compiler/mercury_to_mercury.m
+++ b/compiler/mercury_to_mercury.m
@@ -3013,6 +3013,50 @@ mercury_output_goal_2(Expr, VarSet, Indent, !IO) :-
     mercury_output_newline(Indent, !IO),
     io.write_string(")", !IO).

+mercury_output_goal_2(Expr, VarSet, Indent, !IO) :-
+    Expr = try_expr(MaybeIO, Goal, Then, MaybeElse, Catches, MaybeCatchAny),
+    io.write_string("(try [", !IO),
+    (
+        MaybeIO = yes(IOStateVar),
+        io.write_string("io(!", !IO),
+        mercury_output_var(VarSet, no, IOStateVar, !IO),
+        io.write_string(")", !IO)
+    ;
+        MaybeIO = no
+    ),
+    io.write_string("] (", !IO),
+    Indent1 = Indent + 1,
+    mercury_output_newline(Indent1, !IO),
+    mercury_output_goal(Goal, VarSet, Indent1, !IO),
+    mercury_output_newline(Indent, !IO),
+    io.write_string(")", !IO),
+    mercury_output_newline(Indent, !IO),
+    io.write_string("then", !IO),
+    mercury_output_newline(Indent1, !IO),
+    mercury_output_goal(Then, VarSet, Indent1, !IO),
+    mercury_output_newline(Indent, !IO),
+    (
+        MaybeElse = yes(Else),
+        io.write_string("else", !IO),
+        mercury_output_newline(Indent1, !IO),
+        mercury_output_goal(Else, VarSet, Indent1, !IO)
+    ;
+        MaybeElse = no
+    ),
+    list.foldl(mercury_output_catch(VarSet, Indent), Catches, !IO),
+    (
+        MaybeCatchAny = yes(catch_any_expr(CatchAnyVar, CatchAnyGoal)),
+        io.write_string("catch_any ", !IO),
+        mercury_output_var(VarSet, no, CatchAnyVar, !IO),
+        io.write_string(" ->", !IO),
+        mercury_output_newline(Indent1, !IO),
+        mercury_output_goal(CatchAnyGoal, VarSet, Indent1, !IO)
+    ;
+        MaybeCatchAny = no
+    ),
+    mercury_output_newline(Indent, !IO),
+    io.write_string(")", !IO).
+
 mercury_output_goal_2(if_then_else_expr(Vars, StateVars, Cond, Then, Else),
         VarSet, Indent, !IO) :-
     io.write_string("(if", !IO),
@@ -3290,8 +3334,6 @@ mercury_output_some(Vars, StateVars, VarSet, !IO) :-
         true
     ).

-%-----------------------------------------------------------------------------%
-
 :- pred mercury_output_orelse_goals(goals::in, prog_varset::in, int::in,
     io::di, io::uo) is det.

@@ -3313,6 +3355,17 @@ mercury_output_orelse_goals(Goals, VarSet,
Indent, !IO) :-
         )
     ).

+:- pred mercury_output_catch(prog_varset::in, int::in, catch_expr::in,
+    io::di, io::uo) is det.
+
+mercury_output_catch(VarSet, Indent, catch_expr(Pattern, Goal), !IO) :-
+    io.write_string("catch ", !IO),
+    mercury_output_term(VarSet, no, Pattern, !IO),
+    io.write_string(" ->", !IO),
+    mercury_output_newline(Indent + 1, !IO),
+    mercury_output_goal(Goal, VarSet, Indent + 1, !IO),
+    mercury_output_newline(Indent, !IO).
+
 %-----------------------------------------------------------------------------%

 mercury_output_pragma_foreign_decl(Lang, IsLocal, ForeignDeclString, !IO) :-
diff --git a/compiler/mode_util.m b/compiler/mode_util.m
index 0030cdf..6391943 100644
--- a/compiler/mode_util.m
+++ b/compiler/mode_util.m
@@ -1209,6 +1209,11 @@ recompute_instmap_delta_2(RecomputeAtomic,
GoalExpr0, GoalExpr, GoalInfo,
             ShortHand = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
                 MainGoal, OrElseGoals)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            recompute_instmap_delta_1(RecomputeAtomic, SubGoal0, SubGoal,
+                VarTypes, InstMap0, InstMapDelta, !RI),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
+        ;
             ShortHand0 = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file,
diff --git a/compiler/modes.m b/compiler/modes.m
index 3a65452..2942946 100644
--- a/compiler/modes.m
+++ b/compiler/modes.m
@@ -2078,6 +2078,13 @@ modecheck_goal_shorthand(ShortHand0, GoalInfo0,
GoalExpr, !ModeInfo, !IO) :-
         GoalExpr = shorthand(ShortHand),
         mode_checkpoint(exit, "atomic", !ModeInfo, !IO)
     ;
+        ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+        mode_checkpoint(enter, "try", !ModeInfo, !IO),
+        modecheck_goal(SubGoal0, SubGoal, !ModeInfo, !IO),
+        ShortHand = try_goal(MaybeIO, ResultVar, SubGoal),
+        GoalExpr = shorthand(ShortHand),
+        mode_checkpoint(exit, "try", !ModeInfo, !IO)
+    ;
         ShortHand0 = bi_implication(_, _),
         % These should have been expanded out by now.
         unexpected(this_file, "modecheck_goal_shorthand: bi_implication")
diff --git a/compiler/module_imports.m b/compiler/module_imports.m
index 5e779f0..463dea5 100644
--- a/compiler/module_imports.m
+++ b/compiler/module_imports.m
@@ -614,6 +614,13 @@ goal_contains_stm_atomic(GoalExpr - _Context) =
ContainsAtomic :-
         ),
         ContainsAtomic = goal_contains_stm_atomic(SubGoal)
     ;
+        GoalExpr = try_expr(_, SubGoal, Then, MaybeElse, Catches, CatchAny),
+        ContainsAtomic = maybe_goals_contain_stm_atomic([
+            yes(SubGoal), yes(Then), MaybeElse,
+            maybe_catch_any_expr_goal(CatchAny) |
+            list.map(yes_catch_expr_goal, Catches)
+        ])
+    ;
         ( GoalExpr = implies_expr(SubGoalA, SubGoalB)
         ; GoalExpr = equivalent_expr(SubGoalA, SubGoalB)
         ),
@@ -653,6 +660,28 @@ three_goals_contain_stm_atomic(GoalA, GoalB,
GoalC) = ContainsAtomic :-
         ContainsAtomic = two_goals_contain_stm_atomic(GoalB, GoalC)
     ).

+:- func maybe_goals_contain_stm_atomic(list(maybe(goal))) = bool.
+
+maybe_goals_contain_stm_atomic([]) = no.
+maybe_goals_contain_stm_atomic([MaybeGoal | MaybeGoals]) = ContainsAtomic :-
+    (
+        MaybeGoal = yes(Goal),
+        goal_contains_stm_atomic(Goal) = yes
+    ->
+        ContainsAtomic = yes
+    ;
+        ContainsAtomic = maybe_goals_contain_stm_atomic(MaybeGoals)
+    ).
+
+:- func yes_catch_expr_goal(catch_expr) = maybe(goal).
+
+yes_catch_expr_goal(Catch) = yes(Catch ^ catch_goal).
+
+:- func maybe_catch_any_expr_goal(maybe(catch_any_expr)) = maybe(goal).
+
+maybe_catch_any_expr_goal(yes(catch_any_expr(_, Goal))) = yes(Goal).
+maybe_catch_any_expr_goal(no) = no.
+
 %-----------------------------------------------------------------------------%

 get_fact_table_dependencies(Items, Deps) :-
diff --git a/compiler/module_qual.m b/compiler/module_qual.m
index 44e0001..a294307 100644
--- a/compiler/module_qual.m
+++ b/compiler/module_qual.m
@@ -539,6 +539,26 @@
process_assert(promise_equivalent_solution_arbitrary_expr(_V, _D, _C,
G) - _,
     process_assert(G, Symbols, Success).
 process_assert(trace_expr(_C, _R, _I, _M, G) - _, Symbols, Success) :-
     process_assert(G, Symbols, Success).
+process_assert(try_expr(_, Goal, Then, MaybeElse, Catches, MaybeCatchAny) - _,
+        Symbols, Success) :-
+    process_assert(Goal, SymbolsGoal, SuccessGoal),
+    process_assert(Then, SymbolsThen, SuccessThen),
+    maybe_process_assert(MaybeElse, SymbolsElse, SuccessElse),
+    list.map2(process_assert_catch, Catches, SymbolsCatches, SuccessCatches),
+    (
+        MaybeCatchAny = yes(catch_any_expr(_, CatchAnyGoal)),
+        process_assert(CatchAnyGoal, SymbolsCatchAny, SuccessCatchAny)
+    ;
+        MaybeCatchAny = no,
+        SymbolsCatchAny = [],
+        SuccessCatchAny = no
+    ),
+    SymbolsLists = [SymbolsGoal, SymbolsThen, SymbolsElse, SymbolsCatchAny |
+        SymbolsCatches],
+    list.condense(SymbolsLists, Symbols),
+    SuccessLists = [SuccessGoal, SuccessThen, SuccessElse, SuccessCatchAny |
+        SuccessCatches],
+    bool.and_list(SuccessLists, Success).
 process_assert(atomic_expr(_, _, _, MainGoal, OrElseGoals) - _, Symbols,
         Success) :-
     process_assert(MainGoal, SymbolsMainGoal, SuccessMainGoal),
@@ -604,6 +624,29 @@ process_assert(unify_expr(LHS0, RHS0, _Purity) -
_, Symbols, Success) :-
         Success = no
     ).

+:- pred maybe_process_assert(maybe(goal)::in, list(sym_name)::out, bool::out)
+    is det.
+
+maybe_process_assert(no, [], yes).
+maybe_process_assert(yes(Goal), Symbols, Success) :-
+    process_assert(Goal, Symbols, Success).
+
+:- pred process_assert_catch(catch_expr::in, list(sym_name)::out, bool::out)
+    is det.
+
+process_assert_catch(catch_expr(Pattern0, Goal), Symbols, Success) :-
+    term.coerce(Pattern0, Pattern),
+    (
+        term_qualified_symbols(Pattern, SymbolsPattern),
+        process_assert(Goal, SymbolsGoal, yes)
+    ->
+        list.append(SymbolsPattern, SymbolsGoal, Symbols),
+        Success = yes
+    ;
+        Symbols = [],
+        Success = no
+    ).
+
     % process_assert(G, SNs, B)
     %
     % Performs process_assert on a list of goals.
diff --git a/compiler/notes/compiler_design.html
b/compiler/notes/compiler_design.html
index 9ff9e01..946a3bc 100644
--- a/compiler/notes/compiler_design.html
+++ b/compiler/notes/compiler_design.html
@@ -899,6 +899,13 @@ so that the compiler does the right thing for
options such as
 	through negation.
 	<p>

+<dt> try goal expansion
+
+	<dd>
+	try_expand.m expands `try' goals into calls to predicates in the
+	`exception' module instead.
+	<p>
+
 <dt> simplification (simplify.m)

 	<dd>
diff --git a/compiler/ordering_mode_constraints.m
b/compiler/ordering_mode_constraints.m
index 34cb176..3bf48ba 100644
--- a/compiler/ordering_mode_constraints.m
+++ b/compiler/ordering_mode_constraints.m
@@ -402,6 +402,7 @@ goal_expr_reordering(PredId, VarMap, Bindings,
GoalExpr0, GoalExpr) :-
     ;
         GoalExpr0 = shorthand(_),
         % XXX We need to handle atomic goals.
+        % XXX We need to handle try goals.
         unexpected(this_file, "goal_expr_reordering: NYI: shorthand")
     ).

@@ -804,6 +805,9 @@ dump_goal_goal_paths(Indent, Goal, !IO) :-
             Goals = [MainGoal | OrElseGoals],
             list.foldl(dump_goal_goal_paths(SubGoalIndent), Goals, !IO)
         ;
+            ShortHand = try_goal(_, _, _),
+            unexpected(this_file, "try_goal")
+        ;
             ShortHand = bi_implication(_, _),
             unexpected(this_file, "bi_implication")
         )
diff --git a/compiler/polymorphism.m b/compiler/polymorphism.m
index 797d006..cfe6c82 100644
--- a/compiler/polymorphism.m
+++ b/compiler/polymorphism.m
@@ -1185,13 +1185,17 @@ polymorphism_process_goal_expr(GoalExpr0,
GoalInfo0, Goal, !Info) :-
             polymorphism_process_goal(MainGoal0, MainGoal, !Info),
             polymorphism_process_goal_list(OrElseGoals0, OrElseGoals, !Info),
             ShortHand = atomic_goal(GoalType, Outer, Inner, Vars,
-                MainGoal, OrElseGoals),
-            GoalExpr = shorthand(ShortHand),
-            Goal = hlds_goal(GoalExpr, GoalInfo0)
+                MainGoal, OrElseGoals)
+        ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            polymorphism_process_goal(SubGoal0, SubGoal, !Info),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
         ;
             ShortHand0 = bi_implication(_, _),
             unexpected(this_file, "process_goal_expr: bi_implication")
-        )
+        ),
+        GoalExpr = shorthand(ShortHand),
+        Goal = hlds_goal(GoalExpr, GoalInfo0)
     ).

     % type_info_vars prepends a comma separated list of variables
diff --git a/compiler/post_typecheck.m b/compiler/post_typecheck.m
index 5969620..a2d44a2 100644
--- a/compiler/post_typecheck.m
+++ b/compiler/post_typecheck.m
@@ -647,6 +647,9 @@ in_interface_check(ModuleInfo, PredInfo, Goal, !Specs) :-
             in_interface_check(ModuleInfo, PredInfo, MainGoal, !Specs),
             in_interface_check_list(ModuleInfo, PredInfo, OrElseGoals, !Specs)
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            in_interface_check(ModuleInfo, PredInfo, SubGoal, !Specs)
+        ;
             ShortHand = bi_implication(LHS, RHS),
             in_interface_check(ModuleInfo, PredInfo, LHS, !Specs),
             in_interface_check(ModuleInfo, PredInfo, RHS, !Specs)
diff --git a/compiler/prog_io_goal.m b/compiler/prog_io_goal.m
index 2abd288..29b8d35 100644
--- a/compiler/prog_io_goal.m
+++ b/compiler/prog_io_goal.m
@@ -226,24 +226,59 @@ parse_goal_2(";", [ATerm, BTerm], Context,
ContextPieces, MaybeGoal,
     ).
 parse_goal_2("else", [IfTerm, CTerm], Context, ContextPieces, MaybeGoal,
         !VarSet) :-
-    IfTerm = term.functor(term.atom("if"),
-        [term.functor(term.atom("then"), [ATerm, BTerm], _)], _),
-    parse_some_vars_goal(ATerm, ContextPieces, MaybeAGoal, !VarSet),
-    parse_goal(BTerm, ContextPieces, MaybeBGoal, !VarSet),
-    parse_goal(CTerm, ContextPieces, MaybeCGoal, !VarSet),
     (
-        MaybeAGoal = ok3(Vars, StateVars, AGoal),
-        MaybeBGoal = ok1(BGoal),
-        MaybeCGoal = ok1(CGoal)
+        IfTerm = term.functor(term.atom("if"),
+            [term.functor(term.atom("then"), [ATerm, BTerm], _)], _)
     ->
-        Goal = if_then_else_expr(Vars, StateVars, AGoal, BGoal, CGoal)
-            - Context,
-        MaybeGoal = ok1(Goal)
+        parse_some_vars_goal(ATerm, ContextPieces, MaybeAGoal, !VarSet),
+        parse_goal(BTerm, ContextPieces, MaybeBGoal, !VarSet),
+        parse_goal(CTerm, ContextPieces, MaybeCGoal, !VarSet),
+        (
+            MaybeAGoal = ok3(Vars, StateVars, AGoal),
+            MaybeBGoal = ok1(BGoal),
+            MaybeCGoal = ok1(CGoal)
+        ->
+            Goal = if_then_else_expr(Vars, StateVars, AGoal, BGoal, CGoal)
+                - Context,
+            MaybeGoal = ok1(Goal)
+        ;
+            ASpecs = get_any_errors3(MaybeAGoal),
+            BSpecs = get_any_errors1(MaybeBGoal),
+            CSpecs = get_any_errors1(MaybeCGoal),
+            MaybeGoal = error1(ASpecs ++ BSpecs ++ CSpecs)
+        )
     ;
-        ASpecs = get_any_errors3(MaybeAGoal),
-        BSpecs = get_any_errors1(MaybeBGoal),
-        CSpecs = get_any_errors1(MaybeCGoal),
-        MaybeGoal = error1(ASpecs ++ BSpecs ++ CSpecs)
+        % `else' can also be part of a `try' goal.
+        parse_else_then_try_term(
+            term.functor(term.atom("else"), [IfTerm, CTerm], Context), [], no,
+            Context, ContextPieces, MaybeGoal, !VarSet)
+    ).
+parse_goal_2("then", [TryTerm, ThenTerm], Context, ContextPieces,
+        MaybeGoal, !VarSet) :-
+    parse_then_try_term(
+        term.functor(atom("then"), [TryTerm, ThenTerm], Context), no, [], no,
+        Context, ContextPieces, MaybeGoal, !VarSet).
+parse_goal_2("catch", [ElseThenTryTerm, CatchTerm], Context, ContextPieces,
+        MaybeGoal, !VarSet) :-
+    parse_catch_then_try_term(
+        term.functor(atom("catch"), [ElseThenTryTerm, CatchTerm], Context), no,
+        Context, ContextPieces, MaybeGoal, !VarSet).
+parse_goal_2("catch_any", [TermA, ArrowTerm], Context, ContextPieces,
+        MaybeGoal, !VarSet) :-
+    parse_catch_any_term(ArrowTerm, Context, ContextPieces, MaybeCatchAnyExpr,
+        !VarSet),
+    (
+        MaybeCatchAnyExpr = ok1(CatchAnyExpr),
+        ( TermA = term.functor(atom("catch"), _, _) ->
+            parse_catch_then_try_term(TermA, yes(CatchAnyExpr),
+                Context, ContextPieces, MaybeGoal, !VarSet)
+        ;
+            parse_else_then_try_term(TermA, [], yes(CatchAnyExpr),
+                Context, ContextPieces, MaybeGoal, !VarSet)
+        )
+    ;
+        MaybeCatchAnyExpr = error1(Specs),
+        MaybeGoal = error1(Specs)
     ).
 parse_goal_2("not", [ATerm], Context, ContextPieces, MaybeGoal, !VarSet) :-
     parse_goal(ATerm, ContextPieces, MaybeAGoal, !VarSet),
@@ -729,7 +764,7 @@ parse_trace_params(VarSet, Context, Term,
MaybeComponentsContexts) :-
         )
     ;
         TermStr = describe_error_term(VarSet, Term),
-        Pieces = [words("Error: invalid trace goal paramater"),
+        Pieces = [words("Error: invalid trace goal parameter"),
             quote(TermStr), suffix("."), nl],
         Spec = error_spec(severity_error, phase_term_to_parse_tree,
             [simple_msg(get_term_context(Term), [always(Pieces)])]),
@@ -879,7 +914,7 @@ parse_trace_component(VarSet, _ErrorTerm, Term,
MaybeComponentContext) :-
             )
         ;
             TermStr = describe_error_term(VarSet, Term),
-            Pieces = [words("Error: invalid trace goal paramater"),
+            Pieces = [words("Error: invalid trace goal parameter"),
                 quote(TermStr), suffix("."), nl],
             Spec = error_spec(severity_error, phase_term_to_parse_tree,
                 [simple_msg(Context, [always(Pieces)])]),
@@ -887,7 +922,7 @@ parse_trace_component(VarSet, _ErrorTerm, Term,
MaybeComponentContext) :-
         )
     ;
         TermStr = describe_error_term(VarSet, Term),
-        Pieces = [words("Error: invalid trace goal paramater"),
+        Pieces = [words("Error: invalid trace goal parameter"),
             quote(TermStr), suffix("."), nl],
         Spec = error_spec(severity_error, phase_term_to_parse_tree,
             [simple_msg(get_term_context(Term), [always(Pieces)])]),
@@ -957,7 +992,7 @@ parse_trace_compiletime(VarSet, Term, MaybeCompiletime) :-
                     Compiletime = trace_flag(FlagName),
                     MaybeCompiletime = ok1(Compiletime)
                 ;
-                    Pieces = [words("Error: compile_time paramater"),
+                    Pieces = [words("Error: compile_time parameter"),
                         quote("flag"),
                         words("takes a string as argument."), nl],
                     Spec = error_spec(severity_error,
@@ -966,7 +1001,7 @@ parse_trace_compiletime(VarSet, Term, MaybeCompiletime) :-
                     MaybeCompiletime = error1([Spec])
                 )
             ;
-                Pieces = [words("Error: compile_time paramater"),
+                Pieces = [words("Error: compile_time parameter"),
                     quote("flag"), words("takes just one argument."), nl],
                 Spec = error_spec(severity_error, phase_term_to_parse_tree,
                     [simple_msg(TermContext, [always(Pieces)])]),
@@ -981,7 +1016,7 @@ parse_trace_compiletime(VarSet, Term, MaybeCompiletime) :-
                     Compiletime = trace_grade(trace_grade_debug),
                     MaybeCompiletime = ok1(Compiletime)
                 ;
-                    Pieces = [words("compile_time paramater"),
+                    Pieces = [words("compile_time parameter"),
                         quote("grade"), words("takes just"),
                         quote("debug"), words("as argument (for now)."),
                         nl],
@@ -991,7 +1026,7 @@ parse_trace_compiletime(VarSet, Term, MaybeCompiletime) :-
                     MaybeCompiletime = error1([Spec])
                 )
             ;
-                Pieces = [words("Error: compile_time paramater"),
+                Pieces = [words("Error: compile_time parameter"),
                     quote("grade"), words("takes just one argument."), nl],
                 Spec = error_spec(severity_error, phase_term_to_parse_tree,
                     [simple_msg(TermContext, [always(Pieces)])]),
@@ -1012,7 +1047,7 @@ parse_trace_compiletime(VarSet, Term, MaybeCompiletime) :-
                     Compiletime = trace_trace_level(Level),
                     MaybeCompiletime = ok1(Compiletime)
                 ;
-                    Pieces = [words("Error: compile_time paramater"),
+                    Pieces = [words("Error: compile_time parameter"),
                         quote("tracelevel"), words("takes just"),
                         quote("shallow"), words("or"), quote("deep"),
                         words("as argument."), nl],
@@ -1022,7 +1057,7 @@ parse_trace_compiletime(VarSet, Term, MaybeCompiletime) :-
                     MaybeCompiletime = error1([Spec])
                 )
             ;
-                Pieces = [words("Error: compile_time paramater"),
+                Pieces = [words("Error: compile_time parameter"),
                     quote("tracelevel"),
                     words("takes just one argument."), nl],
                 Spec = error_spec(severity_error, phase_term_to_parse_tree,
@@ -1031,7 +1066,7 @@ parse_trace_compiletime(VarSet, Term, MaybeCompiletime) :-
             )
         ;
             TermStr = describe_error_term(VarSet, Term),
-            Pieces = [words("Error: invalid compile_time paramater"),
+            Pieces = [words("Error: invalid compile_time parameter"),
                 quote(TermStr), suffix("."), nl],
             Spec = error_spec(severity_error, phase_term_to_parse_tree,
                 [simple_msg(TermContext, [always(Pieces)])]),
@@ -1039,7 +1074,7 @@ parse_trace_compiletime(VarSet, Term, MaybeCompiletime) :-
         )
     ;
         TermStr = describe_error_term(VarSet, Term),
-        Pieces = [words("Error: invalid compile_time paramater"),
+        Pieces = [words("Error: invalid compile_time parameter"),
             quote(TermStr), suffix("."), nl],
         Spec = error_spec(severity_error, phase_term_to_parse_tree,
             [simple_msg(get_term_context(Term), [always(Pieces)])]),
@@ -1068,7 +1103,7 @@ parse_trace_runtime(VarSet, Term, MaybeRuntime) :-
                     Runtime = trace_envvar(EnvVarName),
                     MaybeRuntime = ok1(Runtime)
                 ;
-                    Pieces = [words("Error: run_time paramater"), quote("env"),
+                    Pieces = [words("Error: run_time parameter"), quote("env"),
                         words("takes an identifier as argument."), nl],
                     Spec = error_spec(severity_error,
                         phase_term_to_parse_tree,
@@ -1077,7 +1112,7 @@ parse_trace_runtime(VarSet, Term, MaybeRuntime) :-
                     MaybeRuntime = error1([Spec])
                 )
             ;
-                Pieces = [words("Error: run_time paramater"), quote("env"),
+                Pieces = [words("Error: run_time parameter"), quote("env"),
                     words("takes just one argument."), nl],
                 Spec = error_spec(severity_error, phase_term_to_parse_tree,
                     [simple_msg(TermContext, [always(Pieces)])]),
@@ -1085,7 +1120,7 @@ parse_trace_runtime(VarSet, Term, MaybeRuntime) :-
             )
         ;
             TermStr = describe_error_term(VarSet, Term),
-            Pieces = [words("Error: invalid run_time paramater"),
+            Pieces = [words("Error: invalid run_time parameter"),
                 quote(TermStr), suffix("."), nl],
             Spec = error_spec(severity_error, phase_term_to_parse_tree,
                 [simple_msg(TermContext, [always(Pieces)])]),
@@ -1093,7 +1128,7 @@ parse_trace_runtime(VarSet, Term, MaybeRuntime) :-
         )
     ;
         TermStr = describe_error_term(VarSet, Term),
-        Pieces = [words("Error: invalid run_time paramater"),
+        Pieces = [words("Error: invalid run_time parameter"),
             quote(TermStr), suffix("."), nl],
         Spec = error_spec(severity_error, phase_term_to_parse_tree,
             [simple_msg(get_term_context(Term), [always(Pieces)])]),
@@ -1185,6 +1220,280 @@ convert_trace_params_2([Component - Context |
ComponentsContexts],

 %-----------------------------------------------------------------------------%

+:- pred parse_catch_any_term(term::in, term.context::in,
+    list(format_component)::in, maybe1(catch_any_expr)::out,
+    prog_varset::in, prog_varset::out) is semidet.
+
+parse_catch_any_term(ArrowTerm, _Context, ContextPieces, MaybeCatchAny,
+        !VarSet) :-
+    ArrowTerm = term.functor(atom("->"), [VarTerm0, GoalTerm], TermContext),
+    ( VarTerm0 = term.variable(Var0, _) ->
+        term.coerce_var(Var0, Var),
+        parse_goal(GoalTerm, ContextPieces, MaybeGoal, !VarSet),
+        (
+            MaybeGoal = ok1(Goal),
+            CatchAny = catch_any_expr(Var, Goal),
+            MaybeCatchAny = ok1(CatchAny)
+        ;
+            MaybeGoal = error1(Error),
+            MaybeCatchAny = error1(Error)
+        )
+    ;
+        Pieces = [words("Error: the argument of catch_any"),
+            words("should be a variable."), nl],
+        Spec = error_spec(severity_error, phase_term_to_parse_tree,
+            [simple_msg(TermContext, [always(Pieces)])]),
+        MaybeCatchAny = error1([Spec])
+    ).
+
+:- pred parse_catch_then_try_term(term::in, maybe(catch_any_expr)::in,
+    term.context::in, list(format_component)::in, maybe1(goal)::out,
+    prog_varset::in, prog_varset::out) is semidet.
+
+parse_catch_then_try_term(CatchElseThenTryTerm, MaybeCatchAnyExpr,
+        Context, ContextPieces, MaybeGoal, !VarSet) :-
+    CatchElseThenTryTerm = term.functor(atom("catch"), [TermA, TermB], _),
+    parse_sub_catch_terms(TermB, Context, ContextPieces, MaybeCatches,
+         !VarSet),
+    (
+        MaybeCatches = ok1(Catches),
+        parse_else_then_try_term(TermA, Catches, MaybeCatchAnyExpr,
+            Context, ContextPieces, MaybeGoal, !VarSet)
+    ;
+        MaybeCatches = error1(Error),
+        MaybeGoal = error1(Error)
+    ).
+
+:- pred parse_sub_catch_terms(term::in, term.context::in,
+    list(format_component)::in, maybe1(list(catch_expr))::out,
+    prog_varset::in, prog_varset::out) is semidet.
+
+parse_sub_catch_terms(Term, Context, ContextPieces, MaybeCatches, !VarSet) :-
+    ( Term = functor(atom("catch"), [CatchArrowTerm, SubTerm], _) ->
+        parse_catch_arrow_term(CatchArrowTerm, Context, ContextPieces,
+            MaybeCatch, !VarSet),
+        (
+            MaybeCatch = ok1(Catch),
+            parse_sub_catch_terms(SubTerm, Context, ContextPieces,
+                MaybeCatches0, !VarSet),
+            (
+                MaybeCatches0 = ok1(Catches0),
+                MaybeCatches = ok1([Catch | Catches0])
+            ;
+                MaybeCatches0 = error1(Error),
+                MaybeCatches = error1(Error)
+            )
+        ;
+            MaybeCatch = error1(Error),
+            MaybeCatches = error1(Error)
+        )
+    ;
+        parse_catch_arrow_term(Term, Context, ContextPieces, MaybeCatch,
+            !VarSet),
+        (
+            MaybeCatch = ok1(Catch),
+            MaybeCatches = ok1([Catch])
+        ;
+            MaybeCatch = error1(Error),
+            MaybeCatches = error1(Error)
+        )
+    ).
+
+:- pred parse_catch_arrow_term(term::in, term.context::in,
+    list(format_component)::in, maybe1(catch_expr)::out,
+    prog_varset::in, prog_varset::out) is semidet.
+
+parse_catch_arrow_term(CatchArrowTerm, _Context, ContextPieces, MaybeCatch,
+        !VarSet) :-
+    CatchArrowTerm = term.functor(atom("->"), [PatternTerm0, GoalTerm], _),
+    term.coerce(PatternTerm0, PatternTerm),
+    parse_goal(GoalTerm, ContextPieces, MaybeGoal, !VarSet),
+    (
+        MaybeGoal = ok1(Goal),
+        Catch = catch_expr(PatternTerm, Goal),
+        MaybeCatch = ok1(Catch)
+    ;
+        MaybeGoal = error1(Error),
+        MaybeCatch = error1(Error)
+    ).
+
+:- pred parse_else_then_try_term(term::in, list(catch_expr)::in,
+    maybe(catch_any_expr)::in, term.context::in, list(format_component)::in,
+    maybe1(goal)::out, prog_varset::in, prog_varset::out) is semidet.
+
+parse_else_then_try_term(Term, CatchExprs, MaybeCatchAnyExpr,
+        Context, ContextPieces, MaybeGoal, !VarSet) :-
+    % `else' part may or may not exist in `try' goals.
+    ( Term = term.functor(term.atom("else"), [ThenTerm, ElseTerm], _) ->
+        parse_goal(ElseTerm, ContextPieces, MaybeElseGoal0, !VarSet),
+        (
+            MaybeElseGoal0 = ok1(ElseGoal),
+            parse_then_try_term(ThenTerm, yes(ElseGoal), CatchExprs,
+                MaybeCatchAnyExpr, Context, ContextPieces, MaybeGoal, !VarSet)
+        ;
+            MaybeElseGoal0 = error1(Specs),
+            MaybeGoal = error1(Specs)
+        )
+    ;
+        parse_then_try_term(Term, no, CatchExprs, MaybeCatchAnyExpr,
+            Context, ContextPieces, MaybeGoal, !VarSet)
+    ).
+
+:- pred parse_then_try_term(term::in, maybe(goal)::in, list(catch_expr)::in,
+    maybe(catch_any_expr)::in, term.context::in, list(format_component)::in,
+    maybe1(goal)::out, prog_varset::in, prog_varset::out) is semidet.
+
+parse_then_try_term(ThenTryTerm, MaybeElse, CatchExprs, MaybeCatchAnyExpr,
+        Context, ContextPieces, MaybeGoal, !VarSet) :-
+    ThenTryTerm = term.functor(term.atom("then"), [TryTerm, ThenTerm], _),
+    TryTerm = term.functor(term.atom("try"), [ParamsTerm, TryGoalTerm], _),
+
+    varset.coerce(!.VarSet, GenericVarSet),
+    parse_try_params(GenericVarSet, Context, ParamsTerm, MaybeParams),
+    parse_goal(TryGoalTerm, ContextPieces, MaybeTryGoal, !VarSet),
+    parse_goal(ThenTerm, ContextPieces, MaybeThenGoal, !VarSet),
+    (
+        MaybeParams = ok1(Params),
+        MaybeTryGoal = ok1(TryGoal),
+        MaybeThenGoal = ok1(ThenGoal)
+    ->
+        convert_try_params(Params, MaybeComponents),
+        (
+            MaybeComponents = ok1(MaybeIO),
+            GoalExpr = try_expr(MaybeIO, TryGoal, ThenGoal, MaybeElse,
+                CatchExprs, MaybeCatchAnyExpr),
+            MaybeGoal = ok1(GoalExpr - Context)
+        ;
+            MaybeComponents = error1(Specs),
+            MaybeGoal = error1(Specs)
+        )
+    ;
+        ParamsSpecs = get_any_errors1(MaybeParams),
+        TryGoalSpecs = get_any_errors1(MaybeTryGoal),
+        ThenGoalSpecs = get_any_errors1(MaybeThenGoal),
+        MaybeGoal = error1(ParamsSpecs ++ TryGoalSpecs ++ ThenGoalSpecs)
+    ).
+
+:- type try_component
+    --->    try_component_maybe_io(prog_var).
+
+:- pred parse_try_params(varset::in, context::in, term::in,
+    maybe1(assoc_list(try_component, term.context))::out) is det.
+
+parse_try_params(VarSet, Context, Term, MaybeComponentsContexts) :-
+    ( Term = term.functor(term.atom("[]"), [], _) ->
+        MaybeComponentsContexts = ok1([])
+    ; Term = term.functor(term.atom("[|]"), [HeadTerm, TailTerm], _) ->
+        parse_try_component(VarSet, Term, HeadTerm,
+            MaybeHeadComponentContext),
+        parse_try_params(VarSet, Context, TailTerm,
+            MaybeTailComponentsContexts),
+        (
+            MaybeHeadComponentContext = ok1(HeadComponentContext),
+            MaybeTailComponentsContexts = ok1(TailComponentsContexts)
+        ->
+            MaybeComponentsContexts =
+                ok1([HeadComponentContext | TailComponentsContexts])
+        ;
+            HeadSpecs = get_any_errors1(MaybeHeadComponentContext),
+            TailSpecs = get_any_errors1(MaybeTailComponentsContexts),
+            MaybeComponentsContexts = error1(HeadSpecs ++ TailSpecs)
+        )
+    ;
+        TermStr = describe_error_term(VarSet, Term),
+        Pieces = [words("Error: invalid try goal parameter"),
+            quote(TermStr), suffix("."), nl],
+        Spec = error_spec(severity_error, phase_term_to_parse_tree,
+            [simple_msg(get_term_context(Term), [always(Pieces)])]),
+        MaybeComponentsContexts = error1([Spec])
+    ).
+
+:- pred parse_try_component(varset::in, term::in, term::in,
+    maybe1(pair(try_component, term.context))::out) is det.
+
+parse_try_component(VarSet, _ErrorTerm, Term, MaybeComponentContext) :-
+    (
+        Term = term.functor(Functor, SubTerms, Context),
+        Functor = term.atom(Atom)
+    ->
+        ( Atom = "io" ->
+            ( SubTerms = [SubTerm] ->
+                (
+                    SubTerm = term.functor(term.atom("!"),
+                        [term.variable(Var, _)], _)
+                ->
+                    term.coerce_var(Var, ProgVar),
+                    Component = try_component_maybe_io(ProgVar),
+                    MaybeComponentContext = ok1(Component - Context)
+                ;
+                    Pieces = [words("Error: the argument of"), fixed(Atom),
+                        words("should be a state variable."), nl],
+                    Spec = error_spec(severity_error,
+                        phase_term_to_parse_tree,
+                        [simple_msg(get_term_context(SubTerm),
+                            [always(Pieces)])]),
+                    MaybeComponentContext = error1([Spec])
+                )
+            ;
+                Pieces = [words("Error:"), fixed(Atom),
+                    words("takes exactly one argument,"),
+                    words("which should be a state variable name."), nl],
+                Spec = error_spec(severity_error, phase_term_to_parse_tree,
+                    [simple_msg(Context, [always(Pieces)])]),
+                MaybeComponentContext = error1([Spec])
+            )
+        ;
+            TermStr = describe_error_term(VarSet, Term),
+            Pieces = [words("Error: invalid try goal parameter"),
+                quote(TermStr), suffix("."), nl],
+            Spec = error_spec(severity_error, phase_term_to_parse_tree,
+                [simple_msg(Context, [always(Pieces)])]),
+            MaybeComponentContext = error1([Spec])
+        )
+    ;
+        TermStr = describe_error_term(VarSet, Term),
+        Pieces = [words("Error: invalid try goal parameter"),
+            quote(TermStr), suffix("."), nl],
+        Spec = error_spec(severity_error, phase_term_to_parse_tree,
+            [simple_msg(get_term_context(Term), [always(Pieces)])]),
+        MaybeComponentContext = error1([Spec])
+    ).
+
+:- pred convert_try_params(assoc_list(try_component, term.context)::in,
+    maybe1(maybe(prog_var))::out) is det.
+
+convert_try_params(Components, MaybeParams) :-
+    convert_try_params_2(Components, no, [], MaybeParams).
+
+:- pred convert_try_params_2(assoc_list(try_component, term.context)::in,
+    maybe(prog_var)::in, list(error_spec)::in,
+    maybe1(maybe(prog_var))::out) is det.
+
+convert_try_params_2([], MaybeIO, Specs, MaybeParams) :-
+    (
+        Specs = [],
+        MaybeParams = ok1(MaybeIO)
+    ;
+        Specs = [_ | _],
+        MaybeParams = error1(Specs)
+    ).
+convert_try_params_2([Component - Context | ComponentsContexts],
+        !.MaybeIO, !.Specs, MaybeParams) :-
+    Component = try_component_maybe_io(IOStateVar),
+    (
+        !.MaybeIO = no,
+        !:MaybeIO = yes(IOStateVar)
+    ;
+        !.MaybeIO = yes(_),
+        Pieces = [words("Duplicate io try parameter."), nl],
+        Spec = error_spec(severity_error, phase_term_to_parse_tree,
+            [simple_msg(Context, [always(Pieces)])]),
+        !:Specs = [Spec | !.Specs]
+    ),
+    convert_try_params_2(ComponentsContexts, !.MaybeIO, !.Specs, MaybeParams).
+
+%-----------------------------------------------------------------------------%
+
 :- type atomic_component
     --->    atomic_component_inner(atomic_component_state)
     ;       atomic_component_outer(atomic_component_state)
diff --git a/compiler/prog_item.m b/compiler/prog_item.m
index 5c3161e..79218f4 100644
--- a/compiler/prog_item.m
+++ b/compiler/prog_item.m
@@ -818,6 +818,14 @@
                 aexpr_main_goal     :: goal,
                 aexpr_orelse_goals  :: goals
             )
+    ;       try_expr(
+                tryexpr_maybe_io        :: maybe(prog_var),
+                tryexpr_goal            :: goal,
+                tryexpr_then            :: goal,
+                tryexpr_maybe_else      :: maybe(goal),
+                tryexpr_catches         :: list(catch_expr),
+                tryexpr_maybe_catch_any :: maybe(catch_any_expr)
+            )

     % implications
     ;       implies_expr(goal, goal)
@@ -835,6 +843,18 @@
     ;       call_expr(sym_name, list(prog_term), purity)
     ;       unify_expr(prog_term, prog_term, purity).

+:- type catch_expr
+    --->    catch_expr(
+                catch_pattern   :: prog_term,
+                catch_goal      :: goal
+            ).
+
+:- type catch_any_expr
+    --->    catch_any_expr(
+                catch_any_var   :: prog_var,
+                catch_any_goal  :: goal
+            ).
+
 %-----------------------------------------------------------------------------%
 %
 % Module system
diff --git a/compiler/prog_util.m b/compiler/prog_util.m
index 2fb83e4..73456f3 100644
--- a/compiler/prog_util.m
+++ b/compiler/prog_util.m
@@ -381,6 +381,31 @@ rename_in_goal_expr(OldVar, NewVar,
     rename_in_goal(OldVar, NewVar, MainExpr0, MainExpr),
     list.map(rename_in_goal(OldVar, NewVar), OrElseExpr0, OrElseExpr).
 rename_in_goal_expr(OldVar, NewVar,
+        try_expr(MaybeIO0, SubGoal0, Then0, MaybeElse0, Catches0,
+            MaybeCatchAny0),
+        try_expr(MaybeIO, SubGoal, Then, MaybeElse, Catches, MaybeCatchAny)) :-
+    rename_in_maybe_var(OldVar, NewVar, MaybeIO0, MaybeIO),
+    rename_in_goal(OldVar, NewVar, SubGoal0, SubGoal),
+    rename_in_goal(OldVar, NewVar, Then0, Then),
+    (
+        MaybeElse0 = yes(Else0),
+        rename_in_goal(OldVar, NewVar, Else0, Else),
+        MaybeElse = yes(Else)
+    ;
+        MaybeElse0 = no,
+        MaybeElse = no
+    ),
+    list.map(rename_in_catch_expr(OldVar, NewVar), Catches0, Catches),
+    (
+        MaybeCatchAny0 = yes(catch_any_expr(CatchAnyVar0, CatchAnyGoal0)),
+        rename_in_var(OldVar, NewVar, CatchAnyVar0, CatchAnyVar),
+        rename_in_goal(OldVar, NewVar, CatchAnyGoal0, CatchAnyGoal),
+        MaybeCatchAny = yes(catch_any_expr(CatchAnyVar, CatchAnyGoal))
+    ;
+        MaybeCatchAny0 = no,
+        MaybeCatchAny = no
+    ).
+rename_in_goal_expr(OldVar, NewVar,
         implies_expr(GoalA0, GoalB0),
         implies_expr(GoalA, GoalB)) :-
     rename_in_goal(OldVar, NewVar, GoalA0, GoalA),
@@ -453,6 +478,28 @@ rename_in_var(OldVar, NewVar, Var0, Var) :-
         Var = Var0
     ).

+:- pred rename_in_maybe_var(prog_var::in, prog_var::in,
+    maybe(prog_var)::in, maybe(prog_var)::out) is det.
+
+rename_in_maybe_var(OldVar, NewVar, MaybeVar0, MaybeVar) :-
+    (
+        MaybeVar0 = yes(Var0),
+        rename_in_var(OldVar, NewVar, Var0, Var),
+        MaybeVar = yes(Var)
+    ;
+        MaybeVar0 = no,
+        MaybeVar = no
+    ).
+
+:- pred rename_in_catch_expr(prog_var::in, prog_var::in,
+    catch_expr::in, catch_expr::out) is det.
+
+rename_in_catch_expr(OldVar, NewVar, Catch0, Catch) :-
+    Catch0 = catch_expr(Term0, Goal0),
+    term.substitute(Term0, OldVar, term.variable(NewVar, context_init), Term),
+    rename_in_goal(OldVar, NewVar, Goal0, Goal),
+    Catch = catch_expr(Term, Goal).
+
 %-----------------------------------------------------------------------------%

 make_pred_name_with_context(ModuleName, Prefix,
diff --git a/compiler/prop_mode_constraints.m b/compiler/prop_mode_constraints.m
index 9b6ffa6..32fc9ee 100644
--- a/compiler/prop_mode_constraints.m
+++ b/compiler/prop_mode_constraints.m
@@ -372,6 +372,12 @@
ensure_unique_arguments_in_goal(hlds_goal(!.GoalExpr, !.GoalInfo),
                 MainGoal, OrElseGoals),
             !:GoalExpr = shorthand(ShortHand)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            ensure_unique_arguments_in_goal(SubGoal0, SubGoal, !SeenSoFar,
+                !Varset, !Vartypes),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal),
+            !:GoalExpr = shorthand(ShortHand)
+        ;
             ShortHand0 = bi_implication(_, _),
             unexpected(this_file, "bi_implication")
         )
diff --git a/compiler/purity.m b/compiler/purity.m
index a7e2324..62b786e 100644
--- a/compiler/purity.m
+++ b/compiler/purity.m
@@ -838,6 +838,12 @@ compute_expr_purity(GoalExpr0, GoalExpr,
GoalInfo, Purity, ContainsTrace,
                 MainGoal, OrElseGoals),
             GoalExpr = shorthand(ShortHand)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            compute_goal_purity(SubGoal0, SubGoal, Purity, ContainsTrace,
+                !Info),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal),
+            GoalExpr = shorthand(ShortHand)
+        ;
             ShortHand0 = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file, "compute_expr_purity: bi_implication")
diff --git a/compiler/quantification.m b/compiler/quantification.m
index c0cea0e..aca8495 100644
--- a/compiler/quantification.m
+++ b/compiler/quantification.m
@@ -593,6 +593,12 @@ implicitly_quantify_goal_quant_info_2(GoalExpr0,
GoalExpr, GoalInfo0,
                 MainGoal, OrElseGoals),
             GoalExpr = shorthand(ShortHand)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            implicitly_quantify_goal_quant_info(SubGoal0, SubGoal,
+                NonLocalsToRecompute, !Info),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal),
+            GoalExpr = shorthand(ShortHand)
+        ;
             ShortHand0 = bi_implication(LHS, RHS),
             implicitly_quantify_goal_quant_info_bi_implication(LHS, RHS,
                 GoalExpr, GoalInfo0, NonLocalsToRecompute, !Info)
@@ -1416,6 +1422,12 @@ goal_expr_vars_2(NonLocalsToRecompute,
GoalExpr, !Set, !LambdaSet) :-
             disj_vars(NonLocalsToRecompute, [MainGoal | OrElseGoals],
                 !Set, !LambdaSet)
         ;
+            ShortHand = try_goal(_MaybeIO, _ResultVar, SubGoal),
+            % IO state variables and ResultVar are already in SubGoal.
+            SubGoal = hlds_goal(SubGoalExpr, _SubGoalInfo),
+            goal_expr_vars_2(NonLocalsToRecompute, SubGoalExpr, !Set,
+                !LambdaSet)
+        ;
             ShortHand = bi_implication(LHS, RHS),
             conj_vars(NonLocalsToRecompute, [LHS, RHS], !Set, !LambdaSet)
         )
diff --git a/compiler/simplify.m b/compiler/simplify.m
index 9ab4431..6eae747 100644
--- a/compiler/simplify.m
+++ b/compiler/simplify.m
@@ -917,6 +917,10 @@ simplify_goal_expr(!GoalExpr, !GoalInfo, !Info) :-
                 MaybeOutputVars, MainGoal, OrElseGoals, !:GoalExpr, !GoalInfo,
                 !Info)
         ;
+            ShortHand0 = try_goal(_, _, _),
+            % These should have been expanded out by now.
+            unexpected(this_file, "simplify_goal_2: try_goal")
+        ;
             ShortHand0 = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file, "simplify_goal_2: bi_implication")
@@ -1663,6 +1667,9 @@ warn_switch_for_ite_cond(ModuleInfo, VarTypes,
Cond, !CondCanSwitch) :-
             ShortHand = atomic_goal(_, _, _, _, _, _),
             !:CondCanSwitch = cond_cannot_switch
         ;
+            ShortHand = try_goal(_, _, _),
+            !:CondCanSwitch = cond_cannot_switch
+        ;
             ShortHand = bi_implication(_, _),
             unexpected(this_file, "warn_ite_instead_of_switch: shorthand")
         )
@@ -3452,6 +3459,11 @@ goal_contains_trace(hlds_goal(GoalExpr0, GoalInfo0),
             ContainsTrace = worst_contains_trace(MainContainsTrace,
                 OrElseContainsTrace)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            goal_contains_trace(SubGoal0, SubGoal, ContainsTrace),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal),
+            GoalExpr = shorthand(ShortHand)
+        ;
             ShortHand0 = bi_implication(_, _),
             unexpected(this_file, "goal_contains_trace: bi_implication")
         )
@@ -3898,6 +3910,9 @@ will_flush(shorthand(ShortHand), _) = WillFlush :-
         ShortHand = atomic_goal(_, _, _, _, _MainGoal, _OrElseGoals),
         WillFlush = yes
     ;
+        ShortHand = try_goal(_, _, _),
+        WillFlush = yes
+    ;
         ShortHand = bi_implication(_, _),
         % These should have been expanded out by now.
         unexpected(this_file, "will_flush: bi_implication")
diff --git a/compiler/stm_expand.m b/compiler/stm_expand.m
index 5768375..009bf9d 100644
--- a/compiler/stm_expand.m
+++ b/compiler/stm_expand.m
@@ -382,6 +382,9 @@ stm_process_goal(Instmap, Goal0, Goal, !Info) :-
             stm_create_actual_goal(GoalType, Instmap, FinalInstmap,
                 Outer, Inner, MainGoal, OrElseGoals, Goal, !Info)
         ;
+            ShortHand0 = try_goal(_, _, _),
+            unexpected(this_file, "stm_process_goal: try_goal")
+        ;
             ShortHand0 = bi_implication(_, _),
             unexpected(this_file, "stm_process_goal: bi_implication")
         )
diff --git a/compiler/stratify.m b/compiler/stratify.m
index e765b7e..6e8fc40 100644
--- a/compiler/stratify.m
+++ b/compiler/stratify.m
@@ -231,6 +231,10 @@ first_order_check_goal(Goal, Negated, WholeScc,
ThisPredProcId, Error,
             first_order_check_goals(OrElseGoals, Negated, WholeScc,
                 ThisPredProcId, Error, !ModuleInfo, !IO)
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            first_order_check_goal(SubGoal, Negated, WholeScc,
+                ThisPredProcId, Error, !ModuleInfo, !IO)
+        ;
             ShortHand = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file, "first_order_check_goal: bi_implication")
@@ -395,6 +399,10 @@ higher_order_check_goal(Goal, Negated, WholeScc,
ThisPredProcId,
             higher_order_check_goals(OrElseGoals, Negated, WholeScc,
                 ThisPredProcId, HighOrderLoops, Error, !ModuleInfo, !IO)
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            higher_order_check_goal(SubGoal, Negated, WholeScc,
+                ThisPredProcId, HighOrderLoops, Error, !ModuleInfo, !IO)
+        ;
             ShortHand = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file, "higher_order_check_goal: bi_implication")
@@ -818,6 +826,9 @@ stratify_analyze_goal(Goal, !Calls, !HasAT, !CallsHO) :-
             stratify_analyze_goal(MainGoal, !Calls, !HasAT, !CallsHO),
             stratify_analyze_goals(OrElseGoals, !Calls, !HasAT, !CallsHO)
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            stratify_analyze_goal(SubGoal, !Calls, !HasAT, !CallsHO)
+        ;
             ShortHand = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file, "stratify_analyze_goal: bi_implication")
@@ -933,6 +944,9 @@ get_called_procs(Goal, !Calls) :-
             get_called_procs(MainGoal, !Calls),
             get_called_procs_goals(OrElseGoals, !Calls)
         ;
+            ShortHand = try_goal(_, _, SubGoal),
+            get_called_procs(SubGoal, !Calls)
+        ;
             ShortHand = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file, "get_called_procs: bi_implication")
diff --git a/compiler/structure_reuse.lfu.m b/compiler/structure_reuse.lfu.m
index 8882b6f..dc52fe1 100644
--- a/compiler/structure_reuse.lfu.m
+++ b/compiler/structure_reuse.lfu.m
@@ -301,6 +301,9 @@ add_vars_to_lfu_in_goal_expr(ForceInUse, Expr0, Expr) :-
             add_vars_to_lfu_in_goal(ForceInUse, LeftGoal0, LeftGoal),
             add_vars_to_lfu_in_goal(ForceInUse, RightGoal0, RightGoal),
             Shorthand = bi_implication(LeftGoal, RightGoal)
+        ;
+            Shorthand0 = try_goal(_, _, _),
+            unexpected(this_file, "add_vars_to_lfu_in_goal_expr: try_goal")
         ),
         Expr = shorthand(Shorthand)
     ).
diff --git a/compiler/switch_detection.m b/compiler/switch_detection.m
index 4f2cf2a..6a5cf2b 100644
--- a/compiler/switch_detection.m
+++ b/compiler/switch_detection.m
@@ -317,13 +317,18 @@ detect_switches_in_goal_expr(VarTypes,
AllowMulti, InstMap0,
             detect_switches_in_orelse(VarTypes, AllowMulti, InstMap0,
                 OrElseGoals0, OrElseGoals, !ModuleInfo, !Requant),
             ShortHand = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
-                MainGoal, OrElseGoals),
-            GoalExpr = shorthand(ShortHand)
+                MainGoal, OrElseGoals)
+        ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            detect_switches_in_goal(VarTypes, AllowMulti, InstMap0,
+                SubGoal0, SubGoal, !ModuleInfo, !Requant),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
         ;
             ShortHand0 = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file, "detect_switches_in_goal_2: bi_implication")
-        )
+        ),
+        GoalExpr = shorthand(ShortHand)
     ).

 %-----------------------------------------------------------------------------%
@@ -981,6 +986,10 @@ find_bind_var_2(Var, ProcessUnify, Goal0, Goal,
!Subst, !Result, !Info,
             Goal = Goal0,
             FoundDeconstruct = given_up_search
         ;
+            ShortHand0 = try_goal(_, _, _),
+            Goal = Goal0,
+            FoundDeconstruct = given_up_search
+        ;
             ShortHand0 = bi_implication(_, _),
             unexpected(this_file, "find_bind_var_2: bi_implication")
         )
diff --git a/compiler/try_expand.m b/compiler/try_expand.m
new file mode 100644
index 0000000..2831312
--- /dev/null
+++ b/compiler/try_expand.m
@@ -0,0 +1,859 @@
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et
+%-----------------------------------------------------------------------------%
+% Copyright (C) 2009 The University of Melbourne.
+% This file may only be copied under the terms of the GNU General
+% Public Licence - see the file COPYING in the Mercury distribution.
+%-----------------------------------------------------------------------------%
+%
+% File: try_expand.m
+% Author: wangp.
+%
+% Try goals are implemented by replacing them with calls to predicates in the
+% `exception' module.  For example, goal such as:
+%
+%      (try [] p(X, Y)
+%       then q(X, Y)
+%       else r
+%       catch ...
+%      )
+%
+% is expanded to:
+%
+%      exception.try(
+%          (pred(OutputTuple::out) is semidet :-
+%              p(X, Y),
+%              OutputTuple = {X, Y}
+%          ), TryResult),
+%      (
+%          TryResult = succeeded({X, Y}),
+%          q(X, Y)
+%      ;
+%          TryResult = failed,
+%          r
+%      ;
+%          TryResult = exception(Excp),
+%          ...exception handling...
+%      )
+%
+% The transformation requires us to know which variables are bound in the
+% higher order term that is passed to `try', as well as the determinism, so we
+% can't transform them immediately when converting from the parse tree to HLDS
+% representations.  But try goals have very complex control flows and we don't
+% really want to write and maintain mode, determinism, and other analyses to
+% work on them directly either.
+%
+% Instead, we cheat a little and "pre-transform" try goals very early on (when
+% converting from a parse tree to an HLDS) into something that resembles
+% somewhat the final forms, e.g.
+%
+%      magic_exception_result(TryResult),  % out(cannot_fail)
+%      (
+%          TryResult = succeeded({}),
+%          ( p(X, Y) ->
+%              q(X, Y)
+%          ;
+%              r
+%          )
+%      ;
+%          TryResult = exception(Excp),
+%          ...exception handling...
+%      )
+%
+% We let the semantic checks work on these pre-transformed goals.  Afterwards
+% we pick out the various pieces and construct the proper, final goals.
+%
+%-----------------------------------------------------------------------------%
+%
+% PRE-TRANSFORMATION (implemented in add_clause.m)
+%
+% 1. try goal without I/O but with an else part
+%
+%      magic_exception_result(TryResult),  % out(cannot_fail)
+%      (
+%          TryResult = succeeded({}),
+%          ( <Goal> ->
+%              <Then>
+%          ;
+%              <Else>
+%          )
+%      ;
+%          TryResult = exception(Excp),
+%          <ExcpHandling>
+%      )
+%
+%   As intended, variables bound in <Goal> are only in scope within <Then>,
+%   not <Else> nor <ExcpHandling>.
+%
+% 2. try goal without I/O and without an else part, or
+% 3. try goal with I/O
+%
+%       magic_exception_result(TryResult),  % out(cannot_fail)
+%       (
+%           TryResult = succeeded({}),
+%           some [] <Goal>,
+%           some [] <Then>
+%       ;
+%           TryResult = exception(Excp),
+%           <ExcpHandling>
+%       )
+%
+%   The `some' scopes there so that we can distinguish between <Goal> and
+%   <Then> later. (They act as barrier scopes, except we can't introduce
+%   barrier scopes then.  We depend on the early analyses not to move things
+%   in and out of `some' scopes.)
+%
+%-----------------------------------------------------------------------------%
+%
+% POST-TRANSFORMATION (implemented in this module)
+%
+% 1. try goal without I/O, and can fail
+%
+%      try((pred(OutputTuple::out) is semidet :-
+%              <Goal>,
+%              OutputTuple = { <BoundVars> }
+%          ), TryResult),
+%      (
+%          TryResult = succeeded({ <BoundVars> }),
+%          <Then>
+%      ;
+%          TryResult = failed,
+%          <Else>
+%      ;
+%          TryResult = exception(Excp),
+%          <ExcpHandling>
+%      )
+%
+% 2. try goal without I/O, and cannot fail
+%
+%      try((pred(OutputTuple::out) is det :-
+%              <Goal>,
+%              OutputTuple = { <BoundVars> }
+%          ), TryResult),
+%      (
+%          TryResult = succeeded({ <BoundVars> }),
+%          <Then>
+%      ;
+%          TryResult = exception(Excp),
+%          <ExcpHandling>
+%      )
+%
+% 3. try goal with I/O
+%
+%      try_io((pred(OutputTuple::out, !.IO::di, !:IO::uo) is det :-
+%              <Goal>,
+%              OutputTuple = { <BoundVars> }
+%          ), TryResult, !IO),
+%      (
+%          TryResult = succeeded({ <BoundVars> }),
+%          <Then>
+%      ;
+%          TryResult = exception(Excp),
+%          <ExcpHandling>
+%      )
+%
+%   We have to rename an io.state variable in ExcpHandling so that the sequence
+%   begins with the output I/O state of the `try_io' call.
+%
+% The <ExcpHandling> parts can be passed through from the pre-transformation.
+% If a `catch_any' is present the exception handling looks like this:
+%
+%      ( univ_to_type(Excp, <CatchPattern1>) ->
+%          <CatchGoal1>
+%      ; univ_to_type(Excp, <CatchPattern2>) ->
+%          <CatchGoal2>
+%      ;
+%          CatchAnyVar = univ_value(Excp),
+%          <CatchAnyGoal>
+%      )
+%
+% Otherwise, if `catch_any' is not present:
+%
+%      ( univ_to_type(Excp, <CatchPattern1>) ->
+%          <CatchGoal1>
+%      ; univ_to_type(Excp, <CatchPattern2>) ->
+%          <CatchGoal2>
+%      ;
+%          rethrow(TryResult)
+%      )
+%
+%-----------------------------------------------------------------------------%
+
+:- module check_hlds.try_expand.
+:- interface.
+
+:- import_module hlds.
+:- import_module hlds.hlds_module.
+
+:- import_module io.
+
+%-----------------------------------------------------------------------------%
+
+:- pred expand_try_goals(module_info::in, module_info::out, io::di, io::uo)
+    is det.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module check_hlds.det_analysis.
+:- import_module check_hlds.det_report.
+:- import_module check_hlds.modes.
+:- import_module check_hlds.polymorphism.
+:- import_module hlds.goal_util.
+:- import_module hlds.hlds_goal.
+:- import_module hlds.hlds_pred.
+:- import_module hlds.instmap.
+:- import_module hlds.passes_aux.
+:- import_module hlds.pred_table.
+:- import_module hlds.quantification.
+:- import_module libs.
+:- import_module libs.compiler_util.
+:- import_module mdbcomp.
+:- import_module mdbcomp.prim_data.
+:- import_module parse_tree.
+:- import_module parse_tree.error_util.
+:- import_module parse_tree.prog_data.
+:- import_module parse_tree.prog_mode.
+:- import_module parse_tree.prog_type.
+
+:- import_module bool.
+:- import_module int.
+:- import_module list.
+:- import_module map.
+:- import_module maybe.
+:- import_module pair.
+:- import_module set.
+:- import_module string.
+:- import_module term.
+
+%-----------------------------------------------------------------------------%
+
+expand_try_goals(!ModuleInfo, !IO) :-
+    some [!Globals] (
+        module_info_get_globals(!.ModuleInfo, !:Globals),
+        disable_det_warnings(OptionsToRestore, !Globals),
+        module_info_set_globals(!.Globals, !ModuleInfo),
+
+        module_info_predids(PredIds, !ModuleInfo),
+        list.foldl2(expand_try_goals_in_pred, PredIds, !ModuleInfo, !IO),
+
+        module_info_get_globals(!.ModuleInfo, !:Globals),
+        restore_det_warnings(OptionsToRestore, !Globals),
+        module_info_set_globals(!.Globals, !ModuleInfo)
+    ).
+
+:- pred expand_try_goals_in_pred(pred_id::in,
+    module_info::in, module_info::out, io::di, io::uo) is det.
+
+expand_try_goals_in_pred(PredId, !ModuleInfo, !IO) :-
+    module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
+    ProcIds = pred_info_non_imported_procids(PredInfo),
+    list.foldl2(expand_try_goals_in_proc(PredId), ProcIds, !ModuleInfo, !IO).
+
+:- pred expand_try_goals_in_proc(pred_id::in, proc_id::in,
+    module_info::in, module_info::out, io::di, io::uo) is det.
+
+expand_try_goals_in_proc(PredId, ProcId, !ModuleInfo, !IO) :-
+    some [!PredInfo, !ProcInfo] (
+        module_info_pred_proc_info(!.ModuleInfo, PredId, ProcId,
+            !:PredInfo, !:ProcInfo),
+        proc_info_get_goal(!.ProcInfo, Goal0),
+        proc_info_get_initial_instmap(!.ProcInfo, !.ModuleInfo, InitInstmap),
+
+        Info0 = trys_info(!.ModuleInfo, !.PredInfo, !.ProcInfo, no),
+        expand_try_goals_in_goal(InitInstmap, Goal0, Goal, Info0, Info),
+        Info = trys_info(!:ModuleInfo, !:PredInfo, !:ProcInfo, Changed),
+
+        (
+            Changed = yes,
+            update_changed_proc(Goal, PredId, ProcId, !.PredInfo, !.ProcInfo,
+                !ModuleInfo, !IO),
+            module_info_clobber_dependency_info(!ModuleInfo)
+        ;
+            Changed = no
+        )
+    ).
+
+:- pred update_changed_proc(hlds_goal::in, pred_id::in, proc_id::in,
+    pred_info::in, proc_info::in, module_info::in, module_info::out,
+    io::di, io::uo) is det.
+
+update_changed_proc(Goal, PredId, ProcId, PredInfo, !.ProcInfo, !ModuleInfo,
+        !IO) :-
+    proc_info_set_goal(Goal, !ProcInfo),
+    requantify_proc(!ProcInfo),
+    module_info_set_pred_proc_info(PredId, ProcId, PredInfo, !.ProcInfo,
+        !ModuleInfo),
+
+    modecheck_proc(ProcId, PredId, !ModuleInfo, ErrorSpecs, _Changed, !IO),
+    module_info_get_globals(!.ModuleInfo, Globals),
+    write_error_specs(ErrorSpecs, Globals, 0, _NumWarnings, 0, NumErrors, !IO),
+    module_info_incr_num_errors(NumErrors, !ModuleInfo),
+    ( NumErrors > 0 ->
+        % In some cases we may detect mode errors after expanding try goals
+        % which were missed before, so don't abort the compiler (but we'll stop
+        % compiling not long after this pass).
+        true
+    ;
+        determinism_check_proc(ProcId, PredId, !ModuleInfo, DetSpecs),
+        (
+            DetSpecs = []
+        ;
+            DetSpecs = [_ | _],
+            unexpected(this_file, "determinism check fails when repeated")
+        )
+    ).
+
+%-----------------------------------------------------------------------------%
+
+:- type trys_info
+    --->    trys_info(
+                ti_module_info  :: module_info,
+                ti_pred_info    :: pred_info,
+                ti_proc_info    :: proc_info,
+                ti_changed      :: bool
+            ).
+
+:- pred expand_try_goals_in_goal(instmap::in, hlds_goal::in, hlds_goal::out,
+    trys_info::in, trys_info::out) is det.
+
+expand_try_goals_in_goal(Instmap, Goal0, Goal, !Info) :-
+    Goal0 = hlds_goal(GoalExpr0, GoalInfo0),
+    (
+        GoalExpr0 = unify(_, _, _, _, _),
+        Goal = Goal0
+    ;
+        GoalExpr0 = conj(ConjType, Conjuncts0),
+        expand_try_goals_in_conj(Instmap, Conjuncts0, Conjuncts, !Info),
+        GoalExpr = conj(ConjType, Conjuncts),
+        Goal = hlds_goal(GoalExpr, GoalInfo0)
+    ;
+        GoalExpr0 = disj(Disjuncts0),
+        expand_try_goals_in_disj(Instmap, Disjuncts0, Disjuncts, !Info),
+        GoalExpr = disj(Disjuncts),
+        Goal = hlds_goal(GoalExpr, GoalInfo0)
+    ;
+        GoalExpr0 = negation(SubGoal0),
+        expand_try_goals_in_goal(Instmap, SubGoal0, SubGoal, !Info),
+        GoalExpr = negation(SubGoal),
+        Goal = hlds_goal(GoalExpr, GoalInfo0)
+    ;
+        GoalExpr0 = switch(Var, CanFail, Cases0),
+        expand_try_goals_in_cases(Instmap, Cases0, Cases, !Info),
+        GoalExpr = switch(Var, CanFail, Cases),
+        Goal = hlds_goal(GoalExpr, GoalInfo0)
+    ;
+        GoalExpr0 = scope(Reason, InnerGoal0),
+        (
+            Reason = from_ground_term(_, from_ground_term_construct),
+            % There can be no try goals inside this scope.
+            Goal = Goal0
+        ;
+            ( Reason = exist_quant(_)
+            ; Reason = promise_solutions(_, _)
+            ; Reason = promise_purity(_, _)
+            ; Reason = commit(_)
+            ; Reason = barrier(_)
+            ; Reason = from_ground_term(_, from_ground_term_deconstruct)
+            ; Reason = from_ground_term(_, from_ground_term_other)
+            ; Reason = trace_goal(_, _, _, _, _)
+            ),
+            expand_try_goals_in_goal(Instmap, InnerGoal0, InnerGoal, !Info),
+            GoalExpr = scope(Reason, InnerGoal),
+            Goal = hlds_goal(GoalExpr, GoalInfo0)
+        )
+    ;
+        GoalExpr0 = if_then_else(Vars, Cond0, Then0, Else0),
+        expand_try_goals_in_if_then_else(Instmap, Cond0, Cond, Then0, Then,
+            Else0, Else, !Info),
+        GoalExpr = if_then_else(Vars, Cond, Then, Else),
+        Goal = hlds_goal(GoalExpr, GoalInfo0)
+    ;
+        ( GoalExpr0 = generic_call(_, _, _, _)
+        ; GoalExpr0 = plain_call(_, _, _, _, _, _)
+        ; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _)
+        ),
+        Goal = Goal0
+    ;
+        % This should be expanded out at this stage
+        GoalExpr0 = shorthand(ShortHand0),
+        (
+            ShortHand0 = try_goal(_, _, _),
+            expand_try_goal(Instmap, ShortHand0, Goal, !Info)
+        ;
+            ShortHand0 = atomic_goal(_, _, _, _, _, _),
+            unexpected(this_file, "expand_try_goals_in_goal: atomic_goal")
+        ;
+            ShortHand0 = bi_implication(_, _),
+            unexpected(this_file, "expand_try_goals_in_goal: bi_implication")
+        )
+    ).
+
+:- pred expand_try_goals_in_conj(instmap::in,
+    list(hlds_goal)::in, list(hlds_goal)::out,
+    trys_info::in, trys_info::out) is det.
+
+expand_try_goals_in_conj(_Instmap0, [], [], !Info).
+expand_try_goals_in_conj(Instmap0, [Goal0 | Goals0], [Goal | Goals], !Info) :-
+    expand_try_goals_in_goal(Instmap0, Goal0, Goal, !Info),
+    Goal0 = hlds_goal(_, GoalInfo),
+    InstmapDelta = goal_info_get_instmap_delta(GoalInfo),
+    apply_instmap_delta(Instmap0, InstmapDelta, Instmap),
+    expand_try_goals_in_conj(Instmap, Goals0, Goals, !Info).
+
+:- pred expand_try_goals_in_disj(instmap::in,
+    list(hlds_goal)::in, list(hlds_goal)::out,
+    trys_info::in, trys_info::out) is det.
+
+expand_try_goals_in_disj(Instmap0, Goals0, Goals, !Info) :-
+    list.map_foldl(expand_try_goals_in_goal(Instmap0), Goals0, Goals, !Info).
+
+:- pred expand_try_goals_in_cases(instmap::in, list(case)::in, list(case)::out,
+    trys_info::in, trys_info::out) is det.
+
+expand_try_goals_in_cases(_Instmap0, [], [], !Info).
+expand_try_goals_in_cases(Instmap0, [Case0 | Cases0], [Case | Cases], !Info) :-
+    Case0 = case(MainConsId, OtherConsIds, Goal0),
+    expand_try_goals_in_goal(Instmap0, Goal0, Goal, !Info),
+    expand_try_goals_in_cases(Instmap0, Cases0, Cases, !Info),
+    Case = case(MainConsId, OtherConsIds, Goal).
+
+:- pred expand_try_goals_in_if_then_else(instmap::in,
+    hlds_goal::in, hlds_goal::out, hlds_goal::in,
+    hlds_goal::out, hlds_goal::in, hlds_goal::out,
+    trys_info::in, trys_info::out) is det.
+
+expand_try_goals_in_if_then_else(Instmap0, Cond0, Cond, Then0, Then,
+        Else0, Else, !Info) :-
+    expand_try_goals_in_goal(Instmap0, Cond0, Cond, !Info),
+
+    Cond0 = hlds_goal(_, CondInfo),
+    CondInstmapDelta = goal_info_get_instmap_delta(CondInfo),
+    apply_instmap_delta(Instmap0, CondInstmapDelta, InstmapAfterCond),
+    expand_try_goals_in_goal(InstmapAfterCond, Then0, Then, !Info),
+
+    expand_try_goals_in_goal(Instmap0, Else0, Else, !Info).
+
+%-----------------------------------------------------------------------------%
+
+:- inst try_goal
+    --->    try_goal(ground, ground, ground).
+
+:- pred expand_try_goal(instmap::in, shorthand_goal_expr::in(try_goal),
+    hlds_goal::out, trys_info::in, trys_info::out) is det.
+
+expand_try_goal(Instmap, TryGoal, FinalGoal, !Info) :-
+    TryGoal = try_goal(MaybeIO, ResultVar, IntermediateGoal),
+    extract_intermediate_goal_parts(!.Info ^ ti_module_info, ResultVar,
+        IntermediateGoal, Goal0, Then0, MaybeElse0, ExcpHandling0),
+
+    % Handle nested try goals.
+    expand_try_goals_in_goal(Instmap, Goal0, Goal1, !Info),
+    update_instmap(Goal0, Instmap, InstmapAfterGoal),
+    expand_try_goals_in_goal(InstmapAfterGoal, Then0, Then1, !Info),
+    (
+        MaybeElse0 = yes(Else0),
+        expand_try_goals_in_goal(Instmap, Else0, Else1, !Info),
+        MaybeElse1 = yes(Else1)
+    ;
+        MaybeElse0 = no,
+        MaybeElse1 = no
+    ),
+    expand_try_goals_in_goal(Instmap, ExcpHandling0, ExcpHandling1, !Info),
+
+    % Find the output variables.  Note we use Goal0, not Goal1, as any nested
+    % tries would have been transformed will mess up the calculation.
+    bound_nonlocals_in_goal(!.Info ^ ti_module_info, Instmap, Goal0,
+        GoalOutputVarsSet0),
+    (
+        MaybeIO = yes(try_io_state_vars(_IOStateVarInitial, IOStateVarFinal)),
+        set.delete(GoalOutputVarsSet0, IOStateVarFinal, GoalOutputVarsSet)
+    ;
+        MaybeIO = no,
+        GoalOutputVarsSet = GoalOutputVarsSet0
+    ),
+
+    some [!ModuleInfo, !PredInfo, !ProcInfo, !VarTypes] (
+        !.Info = trys_info(!:ModuleInfo, !:PredInfo, !:ProcInfo, _),
+        expand_try_goal_2(MaybeIO, ResultVar, Goal1, Then1, MaybeElse1,
+            ExcpHandling1, GoalOutputVarsSet, FinalGoal, !PredInfo, !ProcInfo,
+            !ModuleInfo),
+        !:Info = trys_info(!.ModuleInfo, !.PredInfo, !.ProcInfo, yes)
+    ).
+
+:- pred expand_try_goal_2(maybe(try_io_state_vars)::in, prog_var::in,
+    hlds_goal::in, hlds_goal::in, maybe(hlds_goal)::in, hlds_goal::in,
+    set(prog_var)::in, hlds_goal::out, pred_info::in, pred_info::out,
+    proc_info::in, proc_info::out, module_info::in, module_info::out) is det.
+
+expand_try_goal_2(MaybeIO, ResultVar, Goal1, Then1, MaybeElse1, ExcpHandling1,
+        GoalOutputVarsSet, FinalGoal, !PredInfo, !ProcInfo, !ModuleInfo) :-
+    some [!VarTypes] (
+        % Get the type of the output tuple.
+        proc_info_get_vartypes(!.ProcInfo, !:VarTypes),
+        GoalOutputVars = set.to_sorted_list(GoalOutputVarsSet),
+        map.apply_to_list(GoalOutputVars, !.VarTypes, GoalOutputVarTypes),
+        OutputTupleType = tuple_type(GoalOutputVarTypes, kind_star),
+
+        % Fix the type of the result of the try call, now that we know what it
+        % should be.
+        RealResultVarType = defined_type(
+            qualified(mercury_exception_module, "exception_result"),
+            [OutputTupleType], kind_star),
+        map.det_update(!.VarTypes, ResultVar, RealResultVarType, !:VarTypes),
+        proc_info_set_vartypes(!.VarTypes, !ProcInfo)
+    ),
+
+    make_try_lambda(Goal1, GoalOutputVarsSet, OutputTupleType, MaybeIO,
+        LambdaVar, AssignLambdaVar, !ProcInfo),
+
+    (
+        MaybeIO = yes(try_io_state_vars(GoalInitialIOVar, GoalFinalIOVar)),
+
+        % We need to rearrange I/O state variables a bit.
+        %
+        % Let Goal take the I/O state from GoalInitialIOVar to GoalFinalIOVar.
+        % The input to try_io will be GoalInitialIOVar.
+        % Let the output of try_io to be TryIOOutputVar.
+        %
+        % Due to the pre-transformation, ExcpHandling also takes the I/O state
+        % from GoalInitialIOVar to GoalFinalIOVar.  We need to rename
+        % GoalInitialIOVar to TryIOOutputVar as the exception handling code
+        % follows the try_io call.
+        %
+        % We cannot let TryIOOutputVar be GoalFinalIOVar, as the latter may
+        % already appear somewhere in ExcpHandling.  TryIOOutputVar must be a
+        % new variable.
+        %
+        % The Then part starts the I/O state sequence from GoalFinalIOVar, so
+        % we need to unify "GoalFinalIOVar = TryIOOutputVar".  We don't use
+        % renaming in this case because GoalFinalIOVar might not even occur in
+        % the Then part; then renaming would lead to a mode error.
+
+        proc_info_create_var_from_type(io_state_type, yes("TryIOOutput"),
+            TryIOOutputVar, !ProcInfo),
+        make_try_call("try_io", LambdaVar, ResultVar,
+            [GoalInitialIOVar, TryIOOutputVar], OutputTupleType,
+            CallTryGoal, !PredInfo, !ProcInfo, !ModuleInfo),
+
+        create_pure_atomic_complicated_unification(GoalFinalIOVar,
+            rhs_var(TryIOOutputVar), term.context_init,
+            umc_implicit("try_expand"), [], UnifyThenInitialIOVar),
+        conjoin_goals(UnifyThenInitialIOVar, Then1, Then),
+
+        RenamingExcp = map.from_assoc_list([GoalInitialIOVar -
TryIOOutputVar]),
+        rename_some_vars_in_goal(RenamingExcp, ExcpHandling1, ExcpHandling)
+    ;
+        MaybeIO = no,
+        make_try_call("try", LambdaVar, ResultVar, [], OutputTupleType,
+            CallTryGoal, !PredInfo, !ProcInfo, !ModuleInfo),
+        Then = Then1,
+        ExcpHandling = ExcpHandling1
+    ),
+
+    goal_info_init(GoalInfo),
+
+    % The `succeeded' case.
+    proc_info_create_var_from_type(OutputTupleType, yes("OutputTuple"),
+        TupleVar, !ProcInfo),
+    deconstruct_functor(ResultVar, succeeded_cons_id, [TupleVar],
+        DeconstructSucceeded),
+    deconstruct_tuple(TupleVar, GoalOutputVars, DeconstructOutputs),
+    conj_list_to_goal([DeconstructSucceeded, DeconstructOutputs, Then],
+        GoalInfo, DeconstructsThen),
+    SucceededCase = case(succeeded_cons_id, [], DeconstructsThen),
+
+    % The `exception' case.
+    ExceptionCase = case(exception_cons_id, [], ExcpHandling),
+
+    % The `failed' case.
+    (
+        MaybeElse1 = yes(Else1),
+        FailedCase = case(failed_cons_id, [], Else1),
+        MaybeFailedCase = [FailedCase]
+    ;
+        MaybeElse1 = no,
+        MaybeFailedCase = []
+    ),
+
+    Cases = [SucceededCase, ExceptionCase | MaybeFailedCase],
+    ResultSwitch = hlds_goal(switch(ResultVar, cannot_fail, Cases), GoalInfo),
+    conj_list_to_goal([AssignLambdaVar, CallTryGoal, ResultSwitch], GoalInfo,
+        FinalGoal).
+
+    % Pick out the parts of the original try goal from a pre-transformed goal.
+    %
+:- pred extract_intermediate_goal_parts(module_info::in, prog_var::in,
+    hlds_goal::in, hlds_goal::out, hlds_goal::out, maybe(hlds_goal)::out,
+    hlds_goal::out) is det.
+
+extract_intermediate_goal_parts(ModuleInfo, ResultVar, IntermediateGoal,
+        Goal, Then, MaybeElse, ExcpHandling) :-
+    (
+        extract_intermediate_goal_parts_2(ModuleInfo, ResultVar,
+            IntermediateGoal, GoalPrime, ThenPrime, MaybeElsePrime,
+            ExcpHandlingPrime)
+    ->
+        Goal = GoalPrime,
+        Then = ThenPrime,
+        MaybeElse = MaybeElsePrime,
+        ExcpHandling = ExcpHandlingPrime
+    ;
+        unexpected(this_file, "find_subparts: unexpected goal form")
+    ).
+
+:- pred extract_intermediate_goal_parts_2(module_info::in, prog_var::in,
+    hlds_goal::in, hlds_goal::out, hlds_goal::out, maybe(hlds_goal)::out,
+    hlds_goal::out) is semidet.
+
+extract_intermediate_goal_parts_2(ModuleInfo, ResultVar, IntermediateGoal,
+        Goal, Then, MaybeElse, ExcpHandling) :-
+    IntermediateGoal = hlds_goal(conj(plain_conj, Conjuncts), _),
+    Conjuncts = [
+        hlds_goal(MagicCall, _),
+        hlds_goal(Switch, _)
+    ],
+    MagicCall = plain_call(_, _, [ResultVar], _, _, _),
+    Switch = switch(ResultVar, cannot_fail, Cases),
+
+    lookup_case_goal(Cases, succeeded_cons_id, SucceededGoal),
+    extract_from_succeeded_goal(ModuleInfo, SucceededGoal, Goal, Then,
+        MaybeElse),
+
+    lookup_case_goal(Cases, exception_cons_id, ExcpHandling).
+
+    % There are two forms we could extract when TryResult has the
+    % functor exception.succeeded/1.
+    %
+    %      TryResult = exception.succeeded(V),
+    %      V = {},
+    %      ( Goal ->
+    %          Then
+    %      ;
+    %          Else
+    %      ),
+    %      Rest
+    %
+    % or:
+    %
+    %      TryResult = exception.succeeded(V),
+    %      V = {},
+    %      some [] Goal,
+    %      some [] Then,
+    %      Rest
+    %
+:- pred extract_from_succeeded_goal(module_info::in, hlds_goal::in,
+    hlds_goal::out, hlds_goal::out, maybe(hlds_goal)::out) is semidet.
+
+extract_from_succeeded_goal(ModuleInfo, SucceededGoal, Goal, Then,
+        MaybeElse) :-
+    SucceededGoal = hlds_goal(conj(plain_conj, Conjuncts0), _),
+    Conjuncts0 = [DeconstructResult, TestNullTuple | Conjuncts1],
+    DeconstructResult = hlds_goal(unify(_ResultVar, _, _, _, _), _),
+    TestNullTuple = hlds_goal(unify(_, TestRHS, _, _, _), _),
+    TestRHS = rhs_functor(cons(unqualified("{}"), 0), no, []),
+
+    (
+        Conjuncts1 = [hlds_goal(IfThenElse, _) | Rest],
+        IfThenElse = if_then_else(_, GoalPrime, Then0, Else0)
+    ->
+        Goal = GoalPrime,
+
+        % If Goal is erroneous the Then part may have been optimised away to
+        % `true'.  However, we will be separating the Goal and Then parts in
+        % the final goal, so the knowledge that Goal won't succeed will be lost
+        % to the mode checker.  In that case we replace the Then goal by a call
+        % to an `erroneous' procedure.
+        Goal = hlds_goal(_, GoalInfo),
+        GoalDetism = goal_info_get_determinism(GoalInfo),
+        determinism_components(GoalDetism, _, GoalMaxSoln),
+        (
+            GoalMaxSoln = at_most_zero,
+            make_unreachable_call(ModuleInfo, Then)
+        ;
+            ( GoalMaxSoln = at_most_one
+            ; GoalMaxSoln = at_most_many_cc
+            ; GoalMaxSoln = at_most_many
+            ),
+            conjoin_goal_and_goal_list(Then0, Rest, Then)
+        ),
+
+        conjoin_goal_and_goal_list(Else0, Rest, Else),
+        MaybeElse = yes(Else)
+    ;
+        Conjuncts1 = [SomeGoal | AfterSomeGoal],
+        SomeGoal = hlds_goal(scope(exist_quant([]), Goal), _),
+        (
+            AfterSomeGoal = [SomeThen | Rest],
+            SomeThen = hlds_goal(scope(exist_quant([]), Then0), _)
+        ->
+            conjoin_goal_and_goal_list(Then0, Rest, Then),
+            MaybeElse = no
+        ;
+            % If "some [] Then" is missing then "some [] Goal" must be
+            % `erroneous'.  Make the Then part into a call to an erroneous
+            % procedure.
+            Goal = hlds_goal(_, GoalInfo),
+            GoalDetism = goal_info_get_determinism(GoalInfo),
+            determinism_components(GoalDetism, _, GoalMaxSoln),
+            (
+                GoalMaxSoln = at_most_zero,
+                make_unreachable_call(ModuleInfo, Then),
+                MaybeElse = no
+            ;
+                ( GoalMaxSoln = at_most_one
+                ; GoalMaxSoln = at_most_many_cc
+                ; GoalMaxSoln = at_most_many
+                ),
+                unexpected(this_file, "find_subparts_2: Goal not erroneous")
+            )
+        )
+    ).
+
+:- pred lookup_case_goal(list(case)::in, cons_id::in, hlds_goal::out) is det.
+
+lookup_case_goal([], ConsId, _) :-
+    unexpected(this_file, "lookup_case_goal: couldn't find " ++
string(ConsId)).
+lookup_case_goal([H | T], ConsId, Goal) :-
+    ( H = case(ConsId, [], GoalPrime) ->
+        Goal = GoalPrime
+    ;
+        lookup_case_goal(T, ConsId, Goal)
+    ).
+
+:- pred bound_nonlocals_in_goal(module_info::in, instmap::in, hlds_goal::in,
+    set(prog_var)::out) is det.
+
+bound_nonlocals_in_goal(ModuleInfo, InstMap, Goal, BoundNonLocals) :-
+    Goal = hlds_goal(_, GoalInfo),
+    NonLocals = goal_info_get_nonlocals(GoalInfo),
+    InstmapDelta = goal_info_get_instmap_delta(GoalInfo),
+    BoundNonLocals = set.filter(var_is_bound_in_instmap_delta(ModuleInfo,
+        InstMap, InstmapDelta), NonLocals).
+
+:- pred make_try_lambda(hlds_goal::in, set(prog_var)::in, mer_type::in,
+    maybe(try_io_state_vars)::in, prog_var::out, hlds_goal::out,
+    proc_info::in, proc_info::out) is det.
+
+make_try_lambda(Body0, OutputVarsSet, OutputTupleType, MaybeIO,
+        LambdaVar, AssignLambdaVar, !ProcInfo) :-
+    Body0 = hlds_goal(_, BodyInfo0),
+    NonLocals0 = goal_info_get_nonlocals(BodyInfo0),
+    set.difference(NonLocals0, OutputVarsSet, NonLocals1),
+
+    proc_info_create_var_from_type(OutputTupleType, yes("OutputTuple"),
+        OutputTupleVar, !ProcInfo),
+    (
+        MaybeIO = yes(try_io_state_vars(IOVarInitial, IOVarFinal)),
+        LambdaParams = [OutputTupleVar, IOVarInitial, IOVarFinal],
+        LambdaParamTypes = [OutputTupleType, io_state_type, io_state_type],
+        LambdaParamModes = [out_mode, di_mode, uo_mode],
+        set.delete(NonLocals1, IOVarFinal, NonLocals)
+    ;
+        MaybeIO = no,
+        LambdaParams = [OutputTupleVar],
+        LambdaParamTypes = [OutputTupleType],
+        LambdaParamModes = [out_mode],
+        NonLocals = NonLocals0
+    ),
+    LambdaType = higher_order_type(LambdaParamTypes, no, purity_pure,
+        lambda_normal),
+    proc_info_create_var_from_type(LambdaType, yes("TryLambda"), LambdaVar,
+        !ProcInfo),
+
+    % Add the construction of OutputTuple to the body.
+    construct_tuple(OutputTupleVar, set.to_sorted_list(OutputVarsSet),
+        MakeOutputTuple),
+    conjoin_goals(Body0, MakeOutputTuple, LambdaBody0),
+
+    % Rename away output variables in the lambda body.
+    proc_info_get_varset(!.ProcInfo, VarSet0),
+    proc_info_get_vartypes(!.ProcInfo, VarTypes0),
+    clone_variables(set.to_sorted_list(OutputVarsSet), VarSet0, VarTypes0,
+        VarSet0, VarSet, VarTypes0, VarTypes, map.init, Renaming),
+    proc_info_set_varset(VarSet, !ProcInfo),
+    proc_info_set_vartypes(VarTypes, !ProcInfo),
+    rename_some_vars_in_goal(Renaming, LambdaBody0, LambdaBody),
+
+    % Get the determinism of the lambda.
+    LambdaBody = hlds_goal(_, BodyGoalInfo),
+    BodyDetism = goal_info_get_determinism(BodyGoalInfo),
+    detism_to_try_lambda_detism(BodyDetism, LambdaDetism),
+
+    % Make the lambda assignment.
+    RHS = rhs_lambda_goal(purity_pure, ho_ground, pf_predicate,
+        lambda_normal, set.to_sorted_list(NonLocals),
+        LambdaParams, LambdaParamModes, LambdaDetism, LambdaBody),
+    create_pure_atomic_complicated_unification(LambdaVar, RHS,
+        term.context_init, umc_implicit("try_expand"), [], AssignLambdaVar).
+
+    % try* don't cover all possible determinisms so we have generate lambdas
+    % with less restrictive determinisms.
+    %
+:- pred detism_to_try_lambda_detism(determinism::in, determinism::out) is det.
+
+detism_to_try_lambda_detism(detism_det, detism_det).
+detism_to_try_lambda_detism(detism_semi, detism_semi).
+detism_to_try_lambda_detism(detism_multi, detism_cc_multi).
+detism_to_try_lambda_detism(detism_non, detism_cc_non).
+detism_to_try_lambda_detism(detism_cc_multi, detism_cc_multi).
+detism_to_try_lambda_detism(detism_cc_non, detism_cc_non).
+detism_to_try_lambda_detism(detism_erroneous, detism_det).
+detism_to_try_lambda_detism(detism_failure, detism_semi).
+
+:- pred make_try_call(string::in, prog_var::in, prog_var::in,
+    list(prog_var)::in, mer_type::in, hlds_goal::out,
+    pred_info::in, pred_info::out, proc_info::in, proc_info::out,
+    module_info::in, module_info::out) is det.
+
+make_try_call(PredName, LambdaVar, ResultVar, ExtraArgs, OutputTupleType,
+        OverallGoal, !PredInfo, !ProcInfo, !ModuleInfo) :-
+    create_poly_info(!.ModuleInfo, !.PredInfo, !.ProcInfo, PolyInfo0),
+    polymorphism_make_type_info_var(OutputTupleType, term.context_init,
+        TypeInfoVar, MakeTypeInfoGoals, PolyInfo0, PolyInfo),
+    poly_info_extract(PolyInfo, !PredInfo, !ProcInfo, !:ModuleInfo),
+
+    % The mode will be fixed up by a later analysis.
+    Mode = mode_no(0),
+    Args = [TypeInfoVar, LambdaVar, ResultVar] ++ ExtraArgs,
+    Features = [],
+    InstMapSrc = [],
+    generate_simple_call(mercury_exception_module, PredName,
+        pf_predicate, Mode, detism_cc_multi, purity_pure, Args, Features,
+        InstMapSrc, !.ModuleInfo, term.context_init, CallGoal),
+
+    goal_info_init(GoalInfo),
+    conj_list_to_goal(MakeTypeInfoGoals ++ [CallGoal], GoalInfo, OverallGoal).
+
+:- pred make_unreachable_call(module_info::in, hlds_goal::out) is det.
+
+make_unreachable_call(ModuleInfo, Goal) :-
+    generate_simple_call(mercury_exception_module, "unreachable",
+        pf_predicate, only_mode, detism_erroneous, purity_pure,
+        [], [], [], ModuleInfo, term.context_init, Goal).
+
+%-----------------------------------------------------------------------------%
+
+:- func succeeded_cons_id = cons_id.
+
+succeeded_cons_id = cons(qualified(mercury_exception_module, "succeeded"), 1).
+
+:- func failed_cons_id = cons_id.
+
+failed_cons_id = cons(qualified(mercury_exception_module, "failed"), 0).
+
+:- func exception_cons_id = cons_id.
+
+exception_cons_id = cons(qualified(mercury_exception_module, "exception"), 1).
+
+%-----------------------------------------------------------------------------%
+
+:- func this_file = string.
+
+this_file = "try_expand.m".
+
+%-----------------------------------------------------------------------------%
+:- end_module check_hlds.try_expand.
+%-----------------------------------------------------------------------------%
diff --git a/compiler/typecheck.m b/compiler/typecheck.m
index 09c1d97..58d548e 100644
--- a/compiler/typecheck.m
+++ b/compiler/typecheck.m
@@ -1276,6 +1276,21 @@ typecheck_goal_2(GoalExpr0, GoalExpr, GoalInfo, !Info) :-
                 "typecheck_goal_2: GoalType != unknown_atomic_goal_type"),
             ShortHand = atomic_goal(GoalType, Outer, Inner, MaybeOutputVars,
                 MainGoal, OrElseGoals)
+        ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            trace [io(!IO)] (
+                type_checkpoint("try_goal", !.Info, !IO)
+            ),
+            typecheck_goal(SubGoal0, SubGoal, !Info),
+            (
+                MaybeIO = yes(try_io_state_vars(InitialIO, FinalIO)),
+                ensure_vars_have_a_type([InitialIO, FinalIO], !Info),
+                typecheck_var_has_type(InitialIO, io_state_type, !Info),
+                typecheck_var_has_type(FinalIO, io_state_type, !Info)
+            ;
+                MaybeIO = no
+            ),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal)
         ),
         GoalExpr = shorthand(ShortHand)
     ).
diff --git a/compiler/unique_modes.m b/compiler/unique_modes.m
index 9cdc43c..f623a72 100644
--- a/compiler/unique_modes.m
+++ b/compiler/unique_modes.m
@@ -319,6 +319,13 @@ unique_modes_check_goal_expr(GoalExpr0,
GoalInfo0, GoalExpr, !ModeInfo, !IO) :-
                 MaybeOutputVars, MainGoal0, OrElseGoals0, GoalInfo0, GoalExpr,
                 !ModeInfo, !IO)
         ;
+            ShortHand0 = try_goal(MaybeIO, ResultVar, SubGoal0),
+            mode_checkpoint(enter, "try", !ModeInfo, !IO),
+            unique_modes_check_goal(SubGoal0, SubGoal, !ModeInfo, !IO),
+            ShortHand = try_goal(MaybeIO, ResultVar, SubGoal),
+            GoalExpr = shorthand(ShortHand),
+            mode_checkpoint(exit, "try", !ModeInfo, !IO)
+        ;
             ShortHand0 = bi_implication(_, _),
             % These should have been expanded out by now.
             unexpected(this_file,
diff --git a/compiler/unused_imports.m b/compiler/unused_imports.m
index 99ffa7f..0474fc5 100644
--- a/compiler/unused_imports.m
+++ b/compiler/unused_imports.m
@@ -432,6 +432,9 @@ hlds_goal_used_modules(Goal, !UsedModules) :-
             ShortHand = atomic_goal(_, _, _, _, MainGoal, OrElseGoals),
             hlds_goal_used_modules(MainGoal, !UsedModules),
             list.foldl(hlds_goal_used_modules, OrElseGoals, !UsedModules)
+        ;
+            ShortHand = try_goal(_, _, SubGoal),
+            hlds_goal_used_modules(SubGoal, !UsedModules)
         )
     ).

diff --git a/doc/reference_manual.texi b/doc/reference_manual.texi
index a027612..93ad1e6 100644
--- a/doc/reference_manual.texi
+++ b/doc/reference_manual.texi
@@ -105,6 +105,7 @@ into another language, under the above conditions
for modified versions.
 * Impurity::          Users can write impure Mercury code.
 * Trace goals::       Trace goals allow programmers to add debugging and
                       logging code to their programs.
+* Try goals::         Exception handling.
 * Pragmas::           Various compiler directives, used for example to
                       control optimization.
 * Implementation-dependent extensions::
@@ -867,6 +868,21 @@ but to be confined to the provision of extra information
 for the user of the program.
 See @ref{Trace goals} for the details.

+ at item @code{try @var{Params} @var{Goal} @dots{} catch @var{Term} ->
@var{CGoal} @dots{}}
+A try goal. Exceptions thrown during the execution of @var{Goal}
+may be caught and handled. A summary of the try goal syntax is:
+
+ at example
+    try @var{Params} @var{Goal}
+    then @var{ThenGoal}
+    else @var{ElseGoal}
+    catch @var{Term} -> @var{CatchGoal}
+    ...
+    catch_any @var{CatchAnyVar} -> @var{CatchAnyGoal}
+ at end example
+
+See @ref{Try goals} for the full details.
+
 @item @code{@var{Call}}
 Any goal which does not match any of the above forms
 must be a predicate call.
@@ -9459,6 +9475,100 @@ but one with an implicit promise_pure around
the clause in which they occur.
 @c The trace goal scope itself acts
 @c as a promise_pure scope for any impure code inside it.

+ at node Try goals
+ at chapter Try goals
+
+Mercury procedures may throw exceptions.  Exceptions may be caught using
+the predicates defined in the @samp{exception} library module,
+or using try goals.
+
+ at noindent
+A @samp{try} goal has the following form:
+
+ at example
+        try @var{Params} @var{Goal}
+        then @var{ThenGoal}
+        else @var{ElseGoal}
+        catch @var{Term} -> @var{CatchGoal}
+        ...
+        catch_any @var{CatchAnyVar} -> @var{CatchAnyGoal}
+ at end example
+
+ at var{CondGoal}, @var{ThenGoal}, @var{ElseGoal}, @var{CatchGoal},
+ at var{CatchAnyGoal} must be valid goals.
+
+ at var{Params} must be a valid list of zero or more try parameters.
+
+The try parameter @samp{io} takes a single argument, which must be the name
+of a state variable prefixed by @samp{!}; for example, @samp{io(!IO)}.
+The state variable must have the type @samp{io.state}, and be in scope
+of the try goal.  The state variable is threaded through @samp{Goal},
+so it may perform I/O but cannot fail.
+If no @samp{io} parameter exists, @samp{Goal} may not perform I/O and may
+fail.
+
+The ``then'' part is mandatory.
+The ``else'' part is mandatory if @var{Goal} may fail; otherwise it
+must be omitted.
+There may be zero or more ``catch'' branches.
+The ``catch_any'' part is optional.  @var{CatchAnyVar} must be a single
+variable.
+
+A try goal has determinism @samp{cc_multi}.
+ at c Exception: if all of the then/else/catch/catch_any parts only succeed
+ at c without binding non-local variables then the determinism is det.
+ at c In the implementation we may still infer cc_multi though.
+
+On entering a try goal, @var{Goal} is executed.  If it succeeds
+without throwing an exception, @var{ThenGoal} is executed.
+Any variables bound by @var{Goal} are visible in @var{ThenGoal} only.
+If @var{Goal} fails, then @var{ElseGoal} is executed.
+
+If @var{Goal} throws an exception, the exception object is unified
+with each the @var{Term}s in the ``catch'' branches in turn.
+On the first successful unification, the corresponding @var{CatchGoal} is
+executed (and other ``catch'' and ``catch_any'' branches ignored).
+Variables bound during the unification of the @var{Term} are in scope
+of the corresponding @var{CatchGoal}.
+
+If the exception object does not unify with any of the terms in ``catch''
+branches, and a ``catch_any'' branch is present, the exception is bound
+to @var{CatchAnyVar} and the @var{CatchAnyGoal} executed.
+ at var{CatchAnyVar} is visible in the @var{CatchAnyGoal} only, and
+is existentially typed.
+
+Finally, if the thrown object did not unify with any ``catch'' term,
+and there is no ``catch_any'' part, the exception is rethrown.
+
+ at noindent
+An example of a try goal that performs I/O is:
+
+ at example
+:- pred p_carefully(io::di, io::uo) is det.
+
+p_carefully(!IO) :-
+        (try [io(!IO)] (
+                io.write_string("Calling p\n", !IO),
+                p(Output, !IO)
+        )
+        then
+                io.write_string("p returned: ", !IO),
+                io.write(Output, !IO),
+                io.nl(!IO)
+        catch S ->
+                io.write_string("p threw a string: ", !IO),
+                io.write_string(S, !IO),
+                io.nl(!IO)
+        catch 42 ->
+                io.write_string("p threw 42\n", !IO)
+        catch_any Other ->
+                io.write_string("p threw something: ", !IO),
+                io.write(Other, !IO),
+                % Rethrow the object.
+                throw(X)
+        ).
+ at end example
+
 @node Pragmas
 @chapter Pragmas

diff --git a/library/exception.m b/library/exception.m
index d61867d..643fdff 100644
--- a/library/exception.m
+++ b/library/exception.m
@@ -242,6 +242,19 @@

 %-----------------------------------------------------------------------------%

+    % This is used in the implementation of `try' goals.  It should never be
+    % called.
+    %
+:- pred magic_exception_result(exception_result({})::out(cannot_fail))
+    is cc_multi.
+
+    % This is used in the implementation of `try' goals.  It should never be
+    % called.
+    %
+:- pred unreachable is erroneous.
+
+%-----------------------------------------------------------------------------%
+
 :- implementation.

 :- import_module solutions.
@@ -782,6 +795,14 @@ handle_stm_result(Result0, Result, !STM) :-

 %-----------------------------------------------------------------------------%

+magic_exception_result(succeeded({})).
+magic_exception_result(succeeded({})).  % force cc_multi
+
+unreachable :-
+    throw("unreachable code reached").
+
+%-----------------------------------------------------------------------------%
+
 :- pred throw_impl(univ::in) is erroneous.

 :- type handler(T) == pred(univ, T).
diff --git a/library/ops.m b/library/ops.m
index 723c94a..71f3837 100644
--- a/library/ops.m
+++ b/library/ops.m
@@ -384,6 +384,8 @@ ops.op_table(Op, Info, OtherInfos) :-
         ; Op = "if",    Info = op_info(prefix(x), 1160)
         ; Op = "then",  Info = op_info(infix(x, x), 1150)
         ; Op = "else",  Info = op_info(infix(x, y), 1170)
+        ; Op = "catch", Info = op_info(infix(x, y), 1180)
+        ; Op = "catch_any", Info = op_info(infix(x, y), 1190)
         ; Op = "not",   Info = op_info(prefix(y), 900)
         ; Op = "pred",  Info = op_info(prefix(x), 800)
         ),
@@ -435,6 +437,7 @@ ops.op_table(Op, Info, OtherInfos) :-
         ; Op = "promise_equivalent_solution_sets"
         ; Op = "trace"
         ; Op = "atomic"
+        ; Op = "try"
         ),
         Info = op_info(binary_prefix(x, y), 950),
         OtherInfos = []
diff --git a/tests/hard_coded/Mmakefile b/tests/hard_coded/Mmakefile
index 5c41548..4fb3466 100644
--- a/tests/hard_coded/Mmakefile
+++ b/tests/hard_coded/Mmakefile
@@ -244,6 +244,12 @@ ORDINARY_PROGS=	\
 	transform_value \
 	transitive_inst_type \
 	trigraphs \
+	try_syntax_1 \
+	try_syntax_2 \
+	try_syntax_3 \
+	try_syntax_4 \
+	try_syntax_5 \
+	try_syntax_6 \
 	tuple_test \
 	type_ctor_desc \
 	type_ctor_desc_manip \
@@ -744,6 +750,17 @@ mutable_excp.out: mutable_excp
 		mv $@.tmp $@; \
 	fi

+# try_syntax_3.out is expected to fail (it calls throw/1).
+#
+try_syntax_3.out: try_syntax_3
+	if MERCURY_SUPPRESS_STACK_TRACE=yes ./try_syntax_3 > $@.tmp 2>&1; \
+	then \
+		grep  . $@.tmp; \
+		exit 1; \
+	else \
+		mv $@.tmp $@; \
+	fi
+
 # For the constant_prop_1 test case, we test that constant propagation
 # has been achieved by grepping the generated target code for particular
 # patterns that will only arise if the Mercury compiler did the intended
diff --git a/tests/hard_coded/try_syntax_1.exp
b/tests/hard_coded/try_syntax_1.exp
new file mode 100644
index 0000000..ae5d675
--- /dev/null
+++ b/tests/hard_coded/try_syntax_1.exp
@@ -0,0 +1,5 @@
+Should succeed: sugar-coated
+Should fail: failed
+Should throw an int: caught int: 90210
+Should throw a string: caught string: up
+Should throw a primate: caught other type: stupid_monkey
diff --git a/tests/hard_coded/try_syntax_1.m b/tests/hard_coded/try_syntax_1.m
new file mode 100644
index 0000000..f5ff6e9
--- /dev/null
+++ b/tests/hard_coded/try_syntax_1.m
@@ -0,0 +1,91 @@
+%-----------------------------------------------------------------------------%
+% Try goal without I/O, can fail.
+
+:- module try_syntax_1.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is cc_multi.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module exception.
+:- import_module univ.
+:- import_module string.
+
+%-----------------------------------------------------------------------------%
+
+:- type action
+    --->    act_succeed
+    ;       act_fail
+    ;       act_throw_int
+    ;       act_throw_string
+    ;       act_throw_primate.
+
+:- type primate
+    --->    stupid_monkey
+    ;       stupid_human.
+
+main(!IO) :-
+    io.write_string("Should succeed: ", !IO),
+    run_test(act_succeed, !IO),
+
+    io.write_string("Should fail: ", !IO),
+    run_test(act_fail, !IO),
+
+    io.write_string("Should throw an int: ", !IO),
+    run_test(act_throw_int, !IO),
+
+    io.write_string("Should throw a string: ", !IO),
+    run_test(act_throw_string, !IO),
+
+    io.write_string("Should throw a primate: ", !IO),
+    run_test(act_throw_primate, !IO).
+
+:- pred run_test(action::in, io::di, io::uo) is cc_multi.
+
+run_test(Action, !IO) :-
+    Input = input,
+    (try [] (
+        Output = Input ++ "-coated",
+        (
+            Action = act_succeed
+        ;
+            Action = act_fail,
+            fail
+        ;
+            Action = act_throw_int,
+            throw(90210)
+        ;
+            Action = act_throw_string,
+            throw("up")
+        ;
+            Action = act_throw_primate,
+            throw(stupid_monkey)
+        )
+    )
+    then
+        X = Output : string
+    else
+        X = "failed"
+    catch E ->
+        X = "caught int: " ++ string.from_int(E)
+    catch E ->
+        X = "caught string: " ++ E
+    catch_any Other ->
+        X = "caught other type: " ++ string(Other)
+    ),
+    io.write_string(X, !IO),
+    io.nl(!IO).
+
+:- func input = string.
+:- pragma no_inline(input/0).
+
+input = "sugar".
+
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=8 sts=4 sw=4 et
diff --git a/tests/hard_coded/try_syntax_2.exp
b/tests/hard_coded/try_syntax_2.exp
new file mode 100644
index 0000000..7d095be
--- /dev/null
+++ b/tests/hard_coded/try_syntax_2.exp
@@ -0,0 +1,2 @@
+Should succeed: sugar-coated
+Should throw: caught string: up
diff --git a/tests/hard_coded/try_syntax_2.m b/tests/hard_coded/try_syntax_2.m
new file mode 100644
index 0000000..89e534f
--- /dev/null
+++ b/tests/hard_coded/try_syntax_2.m
@@ -0,0 +1,62 @@
+%-----------------------------------------------------------------------------%
+% Try goal without I/O, cannot fail.
+
+:- module try_syntax_2.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is cc_multi.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module exception.
+:- import_module univ.
+:- import_module string.
+
+%-----------------------------------------------------------------------------%
+
+:- type action
+    --->    act_succeed
+    ;       act_throw.
+
+main(!IO) :-
+    io.write_string("Should succeed: ", !IO),
+    run_test(act_succeed, !IO),
+
+    io.write_string("Should throw: ", !IO),
+    run_test(act_throw, !IO).
+
+:- pred run_test(action::in, io::di, io::uo) is cc_multi.
+
+run_test(Action, !IO) :-
+    Input = input,
+    (try [] (
+        Output = Input ++ "-coated",
+        (
+            Action = act_succeed
+        ;
+            Action = act_throw,
+            throw("up")
+        )
+    )
+    then
+        X = Output
+    catch E ->
+        X = "caught string: " ++ E
+    catch_any Other ->
+        X = "caught other type: " ++ string(Other)
+    ),
+    io.write_string(X, !IO),
+    io.nl(!IO).
+
+:- func input = string.
+:- pragma no_inline(input/0).
+
+input = "sugar".
+
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=8 sts=4 sw=4 et
diff --git a/tests/hard_coded/try_syntax_3.exp
b/tests/hard_coded/try_syntax_3.exp
new file mode 100644
index 0000000..1f116f0
--- /dev/null
+++ b/tests/hard_coded/try_syntax_3.exp
@@ -0,0 +1,2 @@
+Uncaught Mercury exception:
+"This should not be caught."
diff --git a/tests/hard_coded/try_syntax_3.m b/tests/hard_coded/try_syntax_3.m
new file mode 100644
index 0000000..59e424f
--- /dev/null
+++ b/tests/hard_coded/try_syntax_3.m
@@ -0,0 +1,41 @@
+%-----------------------------------------------------------------------------%
+% Try goal, rethrowing if no catch patterns match.
+%
+:- module try_syntax_3.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is cc_multi.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module exception.
+:- import_module univ.
+:- import_module string.
+
+%-----------------------------------------------------------------------------%
+
+main(!IO) :-
+    (try []
+        throw("This should not be caught.")
+    then
+        % unreachable
+        X = "WRONG"
+    else
+        % unreachable
+        X = "WRONG"
+    catch E ->
+        X = "caught int: " ++ string.from_int(E)
+    catch E @ "This should be caught." ->
+        X = "caught string: " ++ E
+    % If none of the catch pattern match then the exception will be rethown.
+    ),
+    io.write_string(X, !IO),
+    io.nl(!IO).
+
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=8 sts=4 sw=4 et
diff --git a/tests/hard_coded/try_syntax_4.exp
b/tests/hard_coded/try_syntax_4.exp
new file mode 100644
index 0000000..357cd27
--- /dev/null
+++ b/tests/hard_coded/try_syntax_4.exp
@@ -0,0 +1,10 @@
+Begin non-throwing test:
+checkpoint (a)
+checkpoint (b)
+checkpoint (c)
+some output
+
+Begin throwing test:
+checkpoint (a)
+checkpoint (d)
+caught int: 90210
diff --git a/tests/hard_coded/try_syntax_4.m b/tests/hard_coded/try_syntax_4.m
new file mode 100644
index 0000000..4a3170c
--- /dev/null
+++ b/tests/hard_coded/try_syntax_4.m
@@ -0,0 +1,75 @@
+%-----------------------------------------------------------------------------%
+% Try goal with I/O
+
+:- module try_syntax_4.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is cc_multi.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module exception.
+:- import_module univ.
+:- import_module string.
+
+%-----------------------------------------------------------------------------%
+
+:- type action
+    --->    act_succeed
+    ;       act_throw.
+
+main(!IO) :-
+    io.write_string("Begin non-throwing test:\n", !IO),
+    run_test(act_succeed, !IO),
+    io.nl(!IO),
+    io.write_string("Begin throwing test:\n", !IO),
+    run_test(act_throw, !IO).
+
+:- pred run_test(action::in, io::di, io::uo) is cc_multi.
+
+run_test(Action, !IO) :-
+    (try [io(!IO)] (
+        do_some_io("checkpoint (a)\n", !IO),
+        Output = "some output",
+        (
+            Action = act_succeed
+        ;
+            Action = act_throw,
+            throw(90210)
+        ),
+        do_some_io("checkpoint (b)\n", !IO)
+    )
+
+    then
+        do_some_io("checkpoint (c)\n", !IO),
+        X = Output
+
+    % Else branch not allowed when using I/O.
+
+    catch E:int ->
+        X = "caught int: " ++ string(E),
+        io.write_string("checkpoint (d)\n", !IO)
+
+    catch E:string ->
+        X = "caught string: " ++ E,
+        io.write_string("checkpoint (e)\n", !IO)
+
+    catch_any Other ->
+        X = "caught: " ++ string(Other),
+        io.write_string("checkpoint (f)\n", !IO)
+    ),
+    io.write_string(X, !IO),
+    io.nl(!IO).
+
+:- pred do_some_io(string::in, io::di, io::uo) is det.
+
+do_some_io(String, !IO) :-
+    io.write_string(String, !IO).
+
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=8 sts=4 sw=4 et
diff --git a/tests/hard_coded/try_syntax_5.exp
b/tests/hard_coded/try_syntax_5.exp
new file mode 100644
index 0000000..a61e938
--- /dev/null
+++ b/tests/hard_coded/try_syntax_5.exp
@@ -0,0 +1 @@
+caught: 12345
diff --git a/tests/hard_coded/try_syntax_5.m b/tests/hard_coded/try_syntax_5.m
new file mode 100644
index 0000000..b1c665b
--- /dev/null
+++ b/tests/hard_coded/try_syntax_5.m
@@ -0,0 +1,41 @@
+%-----------------------------------------------------------------------------%
+% Try goal without I/O.
+% We should still be able to perform I/O in then, else, catch branches.
+
+:- module try_syntax_5.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is cc_multi.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module exception.
+:- import_module univ.
+
+%-----------------------------------------------------------------------------%
+
+
+main(!IO) :-
+    (try []
+        ( semidet_true ->
+            throw(12345)
+        ;
+            semidet_fail
+        )
+    then
+        io.write_string("then\n", !IO)
+    else
+        io.write_string("else\n", !IO)
+    catch_any E ->
+        io.write_string("caught: ", !IO),
+        io.write(E, !IO),
+        io.nl(!IO)
+    ).
+
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=8 sts=4 sw=4 et
diff --git a/tests/hard_coded/try_syntax_6.exp
b/tests/hard_coded/try_syntax_6.exp
new file mode 100644
index 0000000..a1c4439
--- /dev/null
+++ b/tests/hard_coded/try_syntax_6.exp
@@ -0,0 +1,15 @@
+test_1:
+caught 3 (RIGHT)
+
+test_2:
+caught 3 in outer try (RIGHT)
+
+test_3:
+caught 1 in outer try (RIGHT)
+
+test_4:
+caught 1 in inner try (RIGHT)
+caught 2 in outer try (RIGHT)
+
+test_5:
+caught 1 (RIGHT)
diff --git a/tests/hard_coded/try_syntax_6.m b/tests/hard_coded/try_syntax_6.m
new file mode 100644
index 0000000..60838e9
--- /dev/null
+++ b/tests/hard_coded/try_syntax_6.m
@@ -0,0 +1,168 @@
+%-----------------------------------------------------------------------------%
+% Nested try goals.
+
+:- module try_syntax_6.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is cc_multi.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module exception.
+:- import_module univ.
+
+%-----------------------------------------------------------------------------%
+
+main(!IO) :-
+    io.write_string("test_1:\n", !IO),
+    test_1(!IO),
+
+    io.write_string("\ntest_2:\n", !IO),
+    test_2(!IO),
+
+    io.write_string("\ntest_3:\n", !IO),
+    test_3(!IO),
+
+    io.write_string("\ntest_4:\n", !IO),
+    test_4,
+
+    io.write_string("\ntest_5:\n", !IO),
+    test_5(!IO).
+
+    % Nesting in try goal.
+:- pred test_1(io::di, io::uo) is cc_multi.
+
+test_1(!IO) :-
+    (try []
+        (try []
+            throw(3)
+        then
+            true
+        catch 4 ->
+            trace [io(!IO)] (
+                io.write_string("caught 4 (WRONG)\n", !IO)
+            )
+        )
+    then
+        true
+    else
+        true
+    catch 3 ->
+        io.write_string("caught 3 (RIGHT)\n", !IO)
+    ).
+
+    % Nesting in then part.
+:- pred test_2(io::di, io::uo) is cc_multi.
+
+test_2(!IO) :-
+    (try []
+        (try []
+            true
+        then
+            (try []
+                throw(3)
+            then
+                true
+            catch 4 ->
+                trace [io(!IO)] (
+                    io.write_string("caught 4 (WRONG)\n", !IO)
+                )
+            )
+        else
+            true
+        catch 3 ->
+            trace [io(!IO)] (
+                io.write_string("caught 3 in middle try (WRONG)\n", !IO)
+            )
+        )
+    then
+        true
+    catch 3 ->
+        io.write_string("caught 3 in outer try (RIGHT)\n", !IO)
+    ).
+
+    % Nesting in else part.
+:- pred test_3(io::di, io::uo) is cc_multi.
+
+test_3(!IO) :-
+    (try []
+        (try []
+            fail
+        then
+            true % unreachable
+        else
+            % Nesting in else part.
+            (try []
+                throw(1) % should NOT be caught by the outer try
+            then
+                true
+            )
+        catch 1 ->
+            trace [io(!IO)] (
+                io.write_string("caught 1 in inner try (WRONG)\n", !IO)
+            )
+        )
+    then
+        true
+    catch 1 ->
+        io.write_string("caught 1 in outer try (RIGHT)\n", !IO)
+    ).
+
+    % Nesting in catch part.
+:- pred test_4 is det.
+
+test_4 :-
+    (try []
+        (try []
+            throw(1)
+        then
+            true
+        catch 1 ->
+            (try []
+                throw(1)
+            then
+                true % unreachable
+            catch 1 ->
+                trace [io(!IO)] (
+                    io.write_string("caught 1 in inner try (RIGHT)\n", !IO)
+                ),
+                throw(2)
+            )
+        catch 2 ->
+            trace [io(!IO)] (
+                io.write_string("caught 2 in middle try (WRONG)\n", !IO)
+            )
+        )
+    then
+        true
+    catch 2 ->
+        trace [io(!IO)] (
+            io.write_string("caught 2 in outer try (RIGHT)\n", !IO)
+        )
+    ).
+
+    % Nesting in catch_any part.
+:- pred test_5(io::di, io::uo) is cc_multi.
+
+test_5(!IO) :-
+    (try []
+        throw(1)
+    then
+        true
+    catch_any X ->
+        (try []
+            throw(X)
+        then
+            true
+        catch 1 ->
+            io.write_string("caught 1 (RIGHT)\n", !IO)
+        )
+    ).
+
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=8 sts=4 sw=4 et
diff --git a/tests/invalid/Mmakefile b/tests/invalid/Mmakefile
index 9ddd54a..a13cdd7 100644
--- a/tests/invalid/Mmakefile
+++ b/tests/invalid/Mmakefile
@@ -188,6 +188,8 @@ SINGLEMODULE= \
 	test_feature_set \
 	test_may_duplicate \
 	tricky_assert1 \
+	try_bad_params \
+	try_io_else \
 	type_inf_loop \
 	type_loop \
 	type_mismatch \
diff --git a/tests/invalid/trace_goal_env.err_exp
b/tests/invalid/trace_goal_env.err_exp
index dd1eff2..d725271 100644
--- a/tests/invalid/trace_goal_env.err_exp
+++ b/tests/invalid/trace_goal_env.err_exp
@@ -1,5 +1,5 @@
 trace_goal_env.m:023: Error: runtime takes exactly one argument, which should
 trace_goal_env.m:023:   be a boolean expression of run-time tests.
-trace_goal_env.m:030: Error: invalid trace goal paramater
+trace_goal_env.m:030: Error: invalid trace goal parameter
 trace_goal_env.m:030:   `(not runtime(env("TRACE_ABC")))'.
 trace_goal_env.m:020: Error: no clauses for predicate `p'/2.
diff --git a/tests/invalid/try_bad_params.err_exp
b/tests/invalid/try_bad_params.err_exp
new file mode 100644
index 0000000..452205c
--- /dev/null
+++ b/tests/invalid/try_bad_params.err_exp
@@ -0,0 +1,22 @@
+try_bad_params.m:020: Duplicate io try parameter.
+try_bad_params.m:029: Error: invalid try goal parameter `bloop'.
+try_bad_params.m:047: Error: the argument of io should be a state variable.
+try_bad_params.m:056: Error: io takes exactly one argument, which should be a
+try_bad_params.m:056:   state variable name.
+try_bad_params.m:008: Error: no clauses for predicate `main'/2.
+try_bad_params.m:026: Error: no clauses for predicate `main_2'/2.
+try_bad_params.m:040: In clause for predicate `main_3'/2:
+try_bad_params.m:040:   in argument 2 of call to predicate
+try_bad_params.m:040:   `exception.rethrow'/1:
+try_bad_params.m:040:   type error: variable `STATE_VARIABLE_Int_0' has type
+try_bad_params.m:040:   `int',
+try_bad_params.m:040:   expected type was `(io.state)'.
+try_bad_params.m:040: In clause for predicate `main_3'/2:
+try_bad_params.m:040:   in argument 2 of call to predicate
+try_bad_params.m:040:   `exception.rethrow'/1:
+try_bad_params.m:040:   type error: variable `STATE_VARIABLE_Int_1' has type
+try_bad_params.m:040:   `int',
+try_bad_params.m:040:   expected type was `(io.state)'.
+try_bad_params.m:044: Error: no clauses for predicate `main_4'/1.
+try_bad_params.m:053: Error: no clauses for predicate `main_5'/2.
+For more information, recompile with `-E'.
diff --git a/tests/invalid/try_bad_params.m b/tests/invalid/try_bad_params.m
new file mode 100644
index 0000000..826287e
--- /dev/null
+++ b/tests/invalid/try_bad_params.m
@@ -0,0 +1,63 @@
+%-----------------------------------------------------------------------------%
+
+:- module try_bad_params.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module exception.
+
+%-----------------------------------------------------------------------------%
+
+main(!IO) :-
+    (try [io(!IO), io(!IO)]
+        true
+    then
+        true
+    ).
+
+:- pred main_2(io::di, io::uo) is det.
+
+main_2(!IO) :-
+    (try [bloop]
+        true
+    then
+        true
+    ).
+
+:- pred main_3(int::in, int::out) is det.
+
+main_3(!Int) :-
+    (try [io(!Int)]
+        true
+    then
+        true
+    ).
+
+:- pred main_4(io::di) is det.
+
+main_4(IO) :-
+    (try [io(IO)]
+        true
+    then
+        true
+    ).
+
+:- pred main_5(io::di, io::uo) is det.
+
+main_5(IO0, IO) :-
+    (try [io(IO0, IO)]
+        true
+    then
+        true
+    ).
+
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=8 sts=4 sw=4 et
diff --git a/tests/invalid/try_io_else.err_exp
b/tests/invalid/try_io_else.err_exp
new file mode 100644
index 0000000..2391b0c
--- /dev/null
+++ b/tests/invalid/try_io_else.err_exp
@@ -0,0 +1,2 @@
+try_io_else.m:024: Error: a `try' goal with an `io' parameter cannot have an
+try_io_else.m:024:   else part.
diff --git a/tests/invalid/try_io_else.m b/tests/invalid/try_io_else.m
new file mode 100644
index 0000000..633f496
--- /dev/null
+++ b/tests/invalid/try_io_else.m
@@ -0,0 +1,29 @@
+%-----------------------------------------------------------------------------%
+
+:- module try_io_else.
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module exception.
+
+%-----------------------------------------------------------------------------%
+
+main(!IO) :-
+    (try [io(!IO)]
+        true
+    then
+        true
+    else    % not allowed
+        true
+    ).
+
+%-----------------------------------------------------------------------------%
+% vim: ft=mercury ts=8 sts=4 sw=4 et
diff --git a/vim/syntax/mercury.vim b/vim/syntax/mercury.vim
index 3cb2fa7..ef01af9 100644
--- a/vim/syntax/mercury.vim
+++ b/vim/syntax/mercury.vim
@@ -68,6 +68,7 @@ syn keyword mercuryCInterface   export import
 syn keyword mercuryImpure       impure semipure
 syn keyword mercuryToDo         XXX TODO NOTE
 syn keyword mercuryLogical      some all not if then else true fail false
+syn keyword mercuryLogical      try catch catch_any
 syn keyword mercuryLogical      semidet_true semidet_false semidet_fail
 syn keyword mercuryLogical      impure_true
 syn match   mercuryImplication  +<=>\|<=\|=>\|/\\\|\\/+
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list