Forráskód Böngészése

feat(parser): try statements without low-level catch clause (#634)

feat(parser): try statements without low-level catch clause

Aslo support "catch Panic(...)" expression.

Signed-off-by: Alexey Shekhirin <a.shekhirin@gmail.com>
Alexey Shekhirin 3 éve
szülő
commit
0ea9587012

+ 187 - 4
solang-parser/src/lib.rs

@@ -90,6 +90,18 @@ mod test {
                     }
                     string __abba_$;
                     int64 $thing_102;
+                }
+                
+                function bar() {
+                    try sum(1, 1) returns (uint sum) {
+                        assert(sum == 2);
+                    } catch (bytes memory b) {
+                        revert('meh');
+                    } catch Error(string memory error) {
+                        revert(error);
+                    } catch Panic(uint x) {
+                        revert('feh');
+                    }
                 }";
 
         let lex = lexer::Lexer::new(src);
@@ -98,8 +110,8 @@ mod test {
             .parse(src, 0, lex)
             .unwrap();
 
-        let expected_parse_tree = SourceUnit(vec![SourceUnitPart::ContractDefinition(Box::new(
-            ContractDefinition {
+        let expected_parse_tree = SourceUnit(vec![
+            SourceUnitPart::ContractDefinition(Box::new(ContractDefinition {
                 doc: vec![
                     DocComment::Line {
                         comment: SingleDocComment {
@@ -222,8 +234,179 @@ mod test {
                         initializer: None,
                     })),
                 ],
-            },
-        ))]);
+            })),
+            SourceUnitPart::FunctionDefinition(Box::new(FunctionDefinition {
+                doc: vec![],
+                loc: Loc(0, 736, 751),
+                ty: FunctionTy::Function,
+                name: Some(Identifier {
+                    loc: Loc(0, 745, 748),
+                    name: "bar".to_string(),
+                }),
+                name_loc: Loc(0, 745, 748),
+                params: vec![],
+                attributes: vec![],
+                return_not_returns: None,
+                returns: vec![],
+                body: Some(Statement::Block {
+                    loc: Loc(0, 751, 1154),
+                    unchecked: false,
+                    statements: vec![Statement::Try(
+                        Loc(0, 773, 1136),
+                        Expression::FunctionCall(
+                            Loc(0, 777, 786),
+                            Box::new(Expression::Variable(Identifier {
+                                loc: Loc(0, 777, 780),
+                                name: "sum".to_string(),
+                            })),
+                            vec![
+                                Expression::NumberLiteral(Loc(0, 781, 782), 1.into()),
+                                Expression::NumberLiteral(Loc(0, 784, 785), 1.into()),
+                            ],
+                        ),
+                        Some((
+                            vec![(
+                                Loc(0, 796, 804),
+                                Some(Parameter {
+                                    loc: Loc(0, 796, 804),
+                                    ty: Expression::Type(Loc(0, 796, 800), Type::Uint(256)),
+                                    storage: None,
+                                    name: Some(Identifier {
+                                        loc: Loc(0, 801, 804),
+                                        name: "sum".to_string(),
+                                    }),
+                                }),
+                            )],
+                            Box::new(Statement::Block {
+                                loc: Loc(0, 806, 871),
+                                unchecked: false,
+                                statements: vec![Statement::Expression(
+                                    Loc(0, 832, 848),
+                                    Expression::FunctionCall(
+                                        Loc(0, 832, 848),
+                                        Box::new(Expression::Variable(Identifier {
+                                            loc: Loc(0, 832, 838),
+                                            name: "assert".to_string(),
+                                        })),
+                                        vec![Expression::Equal(
+                                            Loc(0, 843, 845),
+                                            Box::new(Expression::Variable(Identifier {
+                                                loc: Loc(0, 839, 842),
+                                                name: "sum".to_string(),
+                                            })),
+                                            Box::new(Expression::NumberLiteral(
+                                                Loc(0, 846, 847),
+                                                2.into(),
+                                            )),
+                                        )],
+                                    ),
+                                )],
+                            }),
+                        )),
+                        vec![
+                            CatchClause::Simple(
+                                Loc(0, 872, 957),
+                                Some(Parameter {
+                                    loc: Loc(0, 879, 893),
+                                    ty: Expression::Type(Loc(0, 879, 884), Type::DynamicBytes),
+                                    storage: Some(StorageLocation::Memory(Loc(0, 885, 891))),
+                                    name: Some(Identifier {
+                                        loc: Loc(0, 892, 893),
+                                        name: "b".to_string(),
+                                    }),
+                                }),
+                                Statement::Block {
+                                    loc: Loc(0, 895, 957),
+                                    unchecked: false,
+                                    statements: vec![Statement::Expression(
+                                        Loc(0, 921, 934),
+                                        Expression::FunctionCall(
+                                            Loc(0, 921, 934),
+                                            Box::new(Expression::Variable(Identifier {
+                                                loc: Loc(0, 921, 927),
+                                                name: "revert".to_string(),
+                                            })),
+                                            vec![Expression::StringLiteral(vec![StringLiteral {
+                                                loc: Loc(0, 928, 933),
+                                                string: "meh".to_string(),
+                                            }])],
+                                        ),
+                                    )],
+                                },
+                            ),
+                            CatchClause::Named(
+                                Loc(0, 958, 1053),
+                                Identifier {
+                                    loc: Loc(0, 964, 969),
+                                    name: "Error".to_string(),
+                                },
+                                Parameter {
+                                    loc: Loc(0, 970, 989),
+                                    ty: Expression::Type(Loc(0, 970, 976), Type::String),
+                                    storage: Some(StorageLocation::Memory(Loc(0, 977, 983))),
+                                    name: Some(Identifier {
+                                        loc: Loc(0, 984, 989),
+                                        name: "error".to_string(),
+                                    }),
+                                },
+                                Statement::Block {
+                                    loc: Loc(0, 991, 1053),
+                                    unchecked: false,
+                                    statements: vec![Statement::Expression(
+                                        Loc(0, 1017, 1030),
+                                        Expression::FunctionCall(
+                                            Loc(0, 1017, 1030),
+                                            Box::new(Expression::Variable(Identifier {
+                                                loc: Loc(0, 1017, 1023),
+                                                name: "revert".to_string(),
+                                            })),
+                                            vec![Expression::Variable(Identifier {
+                                                loc: Loc(0, 1024, 1029),
+                                                name: "error".to_string(),
+                                            })],
+                                        ),
+                                    )],
+                                },
+                            ),
+                            CatchClause::Named(
+                                Loc(0, 1054, 1136),
+                                Identifier {
+                                    loc: Loc(0, 1060, 1065),
+                                    name: "Panic".to_string(),
+                                },
+                                Parameter {
+                                    loc: Loc(0, 1066, 1072),
+                                    ty: Expression::Type(Loc(0, 1066, 1070), Type::Uint(256)),
+                                    storage: None,
+                                    name: Some(Identifier {
+                                        loc: Loc(0, 1071, 1072),
+                                        name: "x".to_string(),
+                                    }),
+                                },
+                                Statement::Block {
+                                    loc: Loc(0, 1074, 1136),
+                                    unchecked: false,
+                                    statements: vec![Statement::Expression(
+                                        Loc(0, 1100, 1113),
+                                        Expression::FunctionCall(
+                                            Loc(0, 1100, 1113),
+                                            Box::new(Expression::Variable(Identifier {
+                                                loc: Loc(0, 1100, 1106),
+                                                name: "revert".to_string(),
+                                            })),
+                                            vec![Expression::StringLiteral(vec![StringLiteral {
+                                                loc: Loc(0, 1107, 1112),
+                                                string: "feh".to_string(),
+                                            }])],
+                                        ),
+                                    )],
+                                },
+                            ),
+                        ],
+                    )],
+                }),
+            })),
+        ]);
 
         assert_eq!(actual_parse_tree, expected_parse_tree);
     }

+ 7 - 2
solang-parser/src/pt.rs

@@ -546,12 +546,17 @@ pub enum Statement {
         Loc,
         Expression,
         Option<(Vec<(Loc, Option<Parameter>)>, Box<Statement>)>,
-        Option<Box<(Identifier, Parameter, Statement)>>,
-        Box<(Option<Parameter>, Statement)>,
+        Vec<CatchClause>,
     ),
     DocComment(Loc, CommentType, String),
 }
 
+#[derive(Debug, PartialEq, Clone)]
+pub enum CatchClause {
+    Simple(Loc, Option<Parameter>, Statement),
+    Named(Loc, Identifier, Parameter, Statement),
+}
+
 #[derive(Debug, PartialEq, Clone)]
 pub enum AssemblyStatement {
     Assign(Loc, AssemblyExpression, AssemblyExpression),

+ 8 - 8
solang-parser/src/solidity.lalrpop

@@ -699,9 +699,12 @@ SimpleStatement: Statement = {
     }
 }
 
-CatchError: (Identifier, Parameter, Statement) = {
-    "catch" <id:Identifier> "(" <param:Parameter> ")" <block:BlockStatement> => {
-        (id, param, block)
+CatchClause: CatchClause = {
+    <l:@L> "catch" <param:("(" <Parameter> ")")?> <block:BlockStatement> <r:@R> => {
+        CatchClause::Simple(Loc(file_no, l, r), param, block)
+    },
+    <l:@L> "catch" <id:Identifier> "(" <param:Parameter> ")" <block:BlockStatement> <r:@R> => {
+        CatchClause::Named(Loc(file_no, l, r), id, param, block)
     }
 }
 
@@ -738,11 +741,8 @@ NonIfStatement: Statement = {
     <l:@L> "return" <e:Expression> <r:@R> ";" => {
         Statement::Return(Loc(file_no, l, r), Some(e))
     },
-    <l:@L> "try" <e:TryExpression> <returns:TryReturns?> <error:CatchError?>
-        "catch" <p:("(" <Parameter> ")")?> <b:BlockStatement> <r:@R> => {
-            Statement::Try(Loc(file_no, l, r), e, returns,
-                box_option(error),
-                Box::new((p, b)))
+    <l:@L> "try" <e:TryExpression> <returns:TryReturns?> <clauses:CatchClause+> <r:@R> => {
+        Statement::Try(Loc(file_no, l, r), e, returns, clauses)
     },
     <l:@L> "emit" <ty:FunctionCall> <r:@R> ";" => {
         Statement::Emit(Loc(file_no, l, r), ty)

+ 1 - 1
src/bin/languageserver/mod.rs

@@ -317,7 +317,7 @@ impl SolangServer {
                 for vecstmt in &try_stmt.ok_stmt {
                     SolangServer::construct_stmt(vecstmt, lookup_tbl, symtab, fnc_map, ns);
                 }
-                if let Some(okstmt) = &try_stmt.error {
+                for okstmt in &try_stmt.errors {
                     for stmts in &okstmt.2 {
                         SolangServer::construct_stmt(stmts, lookup_tbl, symtab, fnc_map, ns);
                     }

+ 5 - 3
src/codegen/statements.rs

@@ -1226,7 +1226,7 @@ fn try_catch(
 
     cfg.set_basic_block(catch_block);
 
-    if let Some((error_param_pos, error_param, error_stmt)) = &try_stmt.error {
+    for (error_param_pos, error_param, error_stmt) in &try_stmt.errors {
         let no_reason_block = cfg.new_basic_block("no_reason".to_string());
 
         let error_var = match error_param_pos {
@@ -1318,8 +1318,10 @@ fn try_catch(
     if let Some(pos) = &try_stmt.catch_param_pos {
         set.remove(pos);
     }
-    if let Some((Some(pos), _, _)) = &try_stmt.error {
-        set.remove(pos);
+    for (pos, _, _) in &try_stmt.errors {
+        if let Some(pos) = pos {
+            set.remove(pos);
+        }
     }
     cfg.set_phis(finally_block, set);
 

+ 3 - 3
src/sema/ast.rs

@@ -1391,7 +1391,7 @@ pub struct TryCatch {
     pub expr: Expression,
     pub returns: Vec<(Option<usize>, Parameter)>,
     pub ok_stmt: Vec<Statement>,
-    pub error: Option<(Option<usize>, Parameter, Vec<Statement>)>,
+    pub errors: Vec<(Option<usize>, Parameter, Vec<Statement>)>,
     pub catch_param: Option<Parameter>,
     pub catch_param_pos: Option<usize>,
     pub catch_stmt: Vec<Statement>,
@@ -1464,8 +1464,8 @@ impl Statement {
                         stmt.recurse(cx, f);
                     }
 
-                    if let Some((_, _, error)) = &try_catch.error {
-                        for stmt in error {
+                    for error_stmt in &try_catch.errors {
+                        for stmt in &error_stmt.2 {
                             stmt.recurse(cx, f);
                         }
                     }

+ 1 - 1
src/sema/dotgraphviz.rs

@@ -1506,7 +1506,7 @@ impl Dot {
 
                     self.add_statement(&try_catch.ok_stmt, func, ns, parent, String::from("ok"));
 
-                    if let Some((_, param, stmt)) = &try_catch.error {
+                    for (_, param, stmt) in &try_catch.errors {
                         self.add_node(
                             Node::new(
                                 "error_param",

+ 1 - 1
src/sema/mutability.rs

@@ -208,7 +208,7 @@ fn recurse_statements(stmts: &[Statement], state: &mut StateCheck) {
             Statement::TryCatch(_, _, try_catch) => {
                 try_catch.expr.recurse(state, read_expression);
                 recurse_statements(&try_catch.ok_stmt, state);
-                if let Some((_, _, s)) = &try_catch.error {
+                for (_, _, s) in &try_catch.errors {
                     recurse_statements(s, state);
                 }
                 recurse_statements(&try_catch.catch_stmt, state);

+ 1 - 1
src/sema/printer.rs

@@ -625,7 +625,7 @@ fn print_statement(stmts: &[Statement], func: &Function, ns: &Namespace) -> Vec<
                     print_statement(&try_catch.ok_stmt, func, ns),
                 ));
 
-                if let Some((_, param, stmt)) = &try_catch.error {
+                for (_, param, stmt) in &try_catch.errors {
                     list.push(Tree::Leaf(format!(
                         "error_param: {} {}",
                         param.ty.to_string(ns),

+ 155 - 128
src/sema/statements.rs

@@ -9,7 +9,8 @@ use super::symtable::{LoopScopes, Symtable};
 use crate::parser::pt;
 use crate::sema::symtable::VariableUsage;
 use crate::sema::unused_variable::{assigned_variable, check_function_call, used_variable};
-use std::collections::{BTreeMap, HashMap};
+use solang_parser::pt::CatchClause;
+use std::collections::{BTreeMap, HashMap, HashSet};
 
 pub fn resolve_function_body(
     def: &pt::FunctionDefinition,
@@ -762,13 +763,12 @@ fn statement(
 
             Ok(reachable)
         }
-        pt::Statement::Try(loc, expr, returns_and_ok, error_stmt, catch_stmt) => {
+        pt::Statement::Try(loc, expr, returns_and_ok, clause_stmts) => {
             let (stmt, reachable) = try_catch(
                 loc,
                 expr,
                 returns_and_ok,
-                error_stmt,
-                catch_stmt,
+                clause_stmts,
                 context,
                 symtable,
                 loops,
@@ -1670,8 +1670,7 @@ fn try_catch(
     loc: &pt::Loc,
     expr: &pt::Expression,
     returns_and_ok: &Option<(Vec<(pt::Loc, Option<pt::Parameter>)>, Box<pt::Statement>)>,
-    error_stmt: &Option<Box<(pt::Identifier, pt::Parameter, pt::Statement)>>,
-    catch_stmt: &(Option<pt::Parameter>, pt::Statement),
+    clause_stmts: &[pt::CatchClause],
     context: &ExprContext,
     symtable: &mut Symtable,
     loops: &mut LoopScopes,
@@ -1931,150 +1930,178 @@ fn try_catch(
 
     symtable.leave_scope();
 
-    let error_resolved = if let Some(error_stmt) = error_stmt {
-        if error_stmt.0.name != "Error" {
+    let mut clauses_unique = HashSet::new();
+    let mut errors_resolved = Vec::new();
+    let mut catch_param = None;
+    let mut catch_param_pos = None;
+    let mut catch_stmt_resolved = Vec::new();
+
+    clause_stmts.iter().try_for_each(|clause_stmt| {
+        let (loc, name) = match clause_stmt {
+            CatchClause::Simple(loc, _, _) => (loc, ""),
+            CatchClause::Named(loc, id, _, _) => (loc, id.name.as_str()),
+        };
+        if !clauses_unique.insert(name) {
             ns.diagnostics.push(Diagnostic::error(
-                error_stmt.0.loc,
-                format!(
-                    "only catch ‘Error’ is supported, not ‘{}’",
-                    error_stmt.0.name
-                ),
+                *loc,
+                if name.is_empty() {
+                    "duplicate catch clause".to_string()
+                } else {
+                    format!("duplicate ‘{}’ catch clause", name)
+                },
             ));
             return Err(());
         }
 
-        let (error_ty, ty_loc) = resolve_var_decl_ty(
-            &error_stmt.1.ty,
-            &error_stmt.1.storage,
-            context,
-            ns,
-            diagnostics,
-        )?;
+        match clause_stmt {
+            CatchClause::Simple(_, param, stmt) => {
+                symtable.new_scope();
 
-        if error_ty != Type::String {
-            ns.diagnostics.push(Diagnostic::error(
-                error_stmt.1.ty.loc(),
-                format!(
-                    "catch Error(...) can only take ‘string memory’, not ‘{}’",
-                    error_ty.to_string(ns)
-                ),
-            ));
-        }
+                if let Some(param) = param {
+                    let (catch_ty, ty_loc) =
+                        resolve_var_decl_ty(&param.ty, &param.storage, context, ns, diagnostics)?;
 
-        symtable.new_scope();
-
-        let mut error_pos = None;
-        let mut error_stmt_resolved = Vec::new();
-        let mut error_param = Parameter {
-            loc: error_stmt.0.loc,
-            ty: Type::String,
-            ty_loc,
-            name: "".to_string(),
-            name_loc: None,
-            indexed: false,
-            readonly: false,
-        };
+                    if catch_ty != Type::DynamicBytes {
+                        diagnostics.push(Diagnostic::error(
+                            param.ty.loc(),
+                            format!(
+                                "catch can only take ‘bytes memory’, not ‘{}’",
+                                catch_ty.to_string(ns)
+                            ),
+                        ));
+                        return Err(());
+                    }
 
-        if let Some(name) = &error_stmt.1.name {
-            if let Some(pos) = symtable.add(
-                name,
-                Type::String,
-                ns,
-                None,
-                VariableUsage::TryCatchErrorString,
-                error_stmt.1.storage.clone(),
-            ) {
-                ns.check_shadowing(context.file_no, context.contract_no, name);
+                    let mut result = Parameter {
+                        loc: param.loc,
+                        ty: Type::DynamicBytes,
+                        ty_loc,
+                        name: "".to_owned(),
+                        name_loc: None,
+                        indexed: false,
+                        readonly: false,
+                    };
 
-                error_pos = Some(pos);
-                error_param.name = name.name.to_string();
-                error_param.name_loc = Some(name.loc);
-            }
-        }
+                    if let Some(name) = &param.name {
+                        if let Some(pos) = symtable.add(
+                            name,
+                            catch_ty,
+                            ns,
+                            None,
+                            VariableUsage::TryCatchErrorBytes,
+                            param.storage.clone(),
+                        ) {
+                            ns.check_shadowing(context.file_no, context.contract_no, name);
+                            catch_param_pos = Some(pos);
+                            result.name = name.name.to_string();
+                            result.name_loc = Some(name.loc);
+                        }
+                    }
 
-        let reachable = statement(
-            &error_stmt.2,
-            &mut error_stmt_resolved,
-            context,
-            symtable,
-            loops,
-            ns,
-            diagnostics,
-        )?;
+                    catch_param = Some(result);
+                }
 
-        finally_reachable |= reachable;
+                let reachable = statement(
+                    stmt,
+                    &mut catch_stmt_resolved,
+                    context,
+                    symtable,
+                    loops,
+                    ns,
+                    diagnostics,
+                )?;
 
-        symtable.leave_scope();
+                finally_reachable |= reachable;
 
-        Some((error_pos, error_param, error_stmt_resolved))
-    } else {
-        None
-    };
+                symtable.leave_scope();
 
-    let mut catch_param_pos = None;
-    let mut catch_stmt_resolved = Vec::new();
+                Ok(())
+            }
+            CatchClause::Named(_, id, param, stmt) => {
+                if !matches!(id.name.as_str(), "Error" | "Panic") {
+                    ns.diagnostics.push(Diagnostic::error(
+                        id.loc,
+                        format!(
+                            "only catch ‘Error’ or ‘Panic’ is supported, not ‘{}’",
+                            id.name
+                        ),
+                    ));
+                    return Err(());
+                }
 
-    symtable.new_scope();
+                let (error_ty, ty_loc) =
+                    resolve_var_decl_ty(&param.ty, &param.storage, context, ns, diagnostics)?;
 
-    let catch_param = if let Some(param) = &catch_stmt.0 {
-        let (catch_ty, ty_loc) =
-            resolve_var_decl_ty(&param.ty, &param.storage, context, ns, diagnostics)?;
+                if id.name == "Error" && error_ty != Type::String {
+                    ns.diagnostics.push(Diagnostic::error(
+                        param.ty.loc(),
+                        format!(
+                            "catch Error(...) can only take ‘string memory’, not ‘{}’",
+                            error_ty.to_string(ns)
+                        ),
+                    ));
+                }
+                if id.name == "Panic" && error_ty != Type::Uint(256) {
+                    ns.diagnostics.push(Diagnostic::error(
+                        param.ty.loc(),
+                        format!(
+                            "catch Panic(...) can only take ‘uint256’, not ‘{}’",
+                            error_ty.to_string(ns)
+                        ),
+                    ));
+                }
 
-        if catch_ty != Type::DynamicBytes {
-            diagnostics.push(Diagnostic::error(
-                param.ty.loc(),
-                format!(
-                    "catch can only take ‘bytes memory’, not ‘{}’",
-                    catch_ty.to_string(ns)
-                ),
-            ));
-            return Err(());
-        }
+                symtable.new_scope();
 
-        let mut catch_param = Parameter {
-            loc: param.loc,
-            ty: Type::DynamicBytes,
-            ty_loc,
-            name: "".to_owned(),
-            name_loc: None,
-            indexed: false,
-            readonly: false,
-        };
+                let mut error_pos = None;
+                let mut error_stmt_resolved = Vec::new();
+                let mut error_param = Parameter {
+                    loc: id.loc,
+                    ty: Type::String,
+                    ty_loc,
+                    name: "".to_string(),
+                    name_loc: None,
+                    indexed: false,
+                    readonly: false,
+                };
 
-        if let Some(name) = &param.name {
-            if let Some(pos) = symtable.add(
-                name,
-                catch_ty,
-                ns,
-                None,
-                VariableUsage::TryCatchErrorBytes,
-                param.storage.clone(),
-            ) {
-                ns.check_shadowing(context.file_no, context.contract_no, name);
-                catch_param_pos = Some(pos);
-                catch_param.name = name.name.to_string();
-                catch_param.name_loc = Some(name.loc);
-            }
-        }
+                if let Some(name) = &param.name {
+                    if let Some(pos) = symtable.add(
+                        name,
+                        Type::String,
+                        ns,
+                        None,
+                        VariableUsage::TryCatchErrorString,
+                        param.storage.clone(),
+                    ) {
+                        ns.check_shadowing(context.file_no, context.contract_no, name);
 
-        Some(catch_param)
-    } else {
-        None
-    };
+                        error_pos = Some(pos);
+                        error_param.name = name.name.to_string();
+                        error_param.name_loc = Some(name.loc);
+                    }
+                }
 
-    let reachable = statement(
-        &catch_stmt.1,
-        &mut catch_stmt_resolved,
-        context,
-        symtable,
-        loops,
-        ns,
-        diagnostics,
-    )?;
+                let reachable = statement(
+                    stmt,
+                    &mut error_stmt_resolved,
+                    context,
+                    symtable,
+                    loops,
+                    ns,
+                    diagnostics,
+                )?;
 
-    finally_reachable |= reachable;
+                finally_reachable |= reachable;
 
-    symtable.leave_scope();
+                symtable.leave_scope();
+
+                errors_resolved.push((error_pos, error_param, error_stmt_resolved));
+
+                Ok(())
+            }
+        }
+    })?;
 
     let stmt = Statement::TryCatch(
         *loc,
@@ -2082,7 +2109,7 @@ fn try_catch(
         TryCatch {
             expr: fcall,
             returns: params,
-            error: error_resolved,
+            errors: errors_resolved,
             ok_stmt: ok_resolved,
             catch_param,
             catch_param_pos,

+ 1 - 1
tests/contract_testcases/substrate/calls/try_catch_external_calls_04.dot

@@ -10,7 +10,7 @@ strict digraph "tests/contract_testcases/substrate/calls/try_catch_external_call
 	bool_literal [label="bool literal: true\ntests/contract_testcases/substrate/calls/try_catch_external_calls_04.sol:19:30-34"]
 	diagnostic [label="found contract ‘c’\nlevel Debug\ntests/contract_testcases/substrate/calls/try_catch_external_calls_04.sol:2:9-20"]
 	diagnostic_12 [label="found contract ‘other’\nlevel Debug\ntests/contract_testcases/substrate/calls/try_catch_external_calls_04.sol:17:9-24"]
-	diagnostic_13 [label="only catch ‘Error’ is supported, not ‘Foo’\nlevel Error\ntests/contract_testcases/substrate/calls/try_catch_external_calls_04.sol:8:25-28"]
+	diagnostic_13 [label="only catch ‘Error’ or ‘Panic’ is supported, not ‘Foo’\nlevel Error\ntests/contract_testcases/substrate/calls/try_catch_external_calls_04.sol:8:25-28"]
 	contracts -> contract
 	contract -> test [label="function"]
 	contracts -> contract_3

+ 10 - 8
tests/contract_testcases/substrate/calls/try_catch_external_calls_05.dot

@@ -1,17 +1,18 @@
 strict digraph "tests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol" {
 	contract [label="contract c\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:2:9-20"]
 	test [label="function test\ncontract: c\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:3:13-35\nsignature test()\nvisibility public\nmutability nonpayable"]
-	contract_3 [label="contract other\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:17:9-24"]
-	test_4 [label="function test\ncontract: other\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:18:13-57\nsignature test()\nvisibility public\nmutability nonpayable"]
+	contract_3 [label="contract other\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:19:9-24"]
+	test_4 [label="function test\ncontract: other\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:20:13-57\nsignature test()\nvisibility public\nmutability nonpayable"]
 	returns [label="returns\nint32 \nbool "]
-	return [label="return\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:19:17-35"]
-	list [label="list\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:19:17-35"]
-	number_literal [label="int32 literal: 102\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:19:25-28"]
-	bool_literal [label="bool literal: true\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:19:30-34"]
+	return [label="return\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:21:17-35"]
+	list [label="list\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:21:17-35"]
+	number_literal [label="int32 literal: 102\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:21:25-28"]
+	bool_literal [label="bool literal: true\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:21:30-34"]
 	diagnostic [label="found contract ‘c’\nlevel Debug\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:2:9-20"]
-	diagnostic_12 [label="found contract ‘other’\nlevel Debug\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:17:9-24"]
+	diagnostic_12 [label="found contract ‘other’\nlevel Debug\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:19:9-24"]
 	diagnostic_13 [label="catch Error(...) can only take ‘string memory’, not ‘bytes’\nlevel Error\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:8:31-36"]
-	diagnostic_14 [label="catch can only take ‘bytes memory’, not ‘string’\nlevel Error\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:10:26-32"]
+	diagnostic_14 [label="catch Panic(...) can only take ‘uint256’, not ‘uint128’\nlevel Error\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:10:31-38"]
+	diagnostic_15 [label="catch can only take ‘bytes memory’, not ‘string’\nlevel Error\ntests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol:12:26-32"]
 	contracts -> contract
 	contract -> test [label="function"]
 	contracts -> contract_3
@@ -25,4 +26,5 @@ strict digraph "tests/contract_testcases/substrate/calls/try_catch_external_call
 	diagnostics -> diagnostic_12 [label="Debug"]
 	diagnostics -> diagnostic_13 [label="Error"]
 	diagnostics -> diagnostic_14 [label="Error"]
+	diagnostics -> diagnostic_15 [label="Error"]
 }

+ 2 - 1
tests/contract_testcases/substrate/calls/try_catch_external_calls_05.sol

@@ -7,6 +7,8 @@
                     x = bla;
                 } catch Error(bytes memory f) {
                     x = 105;
+                } catch Panic(uint128 code) {
+                    x = 106;
                 } catch (string) {
                     x = 2;
                 }
@@ -19,4 +21,3 @@
                 return (102, true);
             }
         }
-