LCOV - code coverage report
Current view: top level - process/include/mcrl2/process - typecheck.h (source / functions) Hit Total Coverage
Test: mcrl2_coverage.info.cleaned Lines: 244 303 80.5 %
Date: 2024-04-21 03:44:01 Functions: 46 55 83.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Author(s): Jan Friso Groote
       2             : //            Wieger Wesselink 2015 -
       3             : // Copyright: see the accompanying file COPYING or copy at
       4             : // https://github.com/mCRL2org/mCRL2/blob/master/COPYING
       5             : //
       6             : // Distributed under the Boost Software License, Version 1.0.
       7             : // (See accompanying file LICENSE_1_0.txt or copy at
       8             : // http://www.boost.org/LICENSE_1_0.txt)
       9             : //
      10             : /// \file mcrl2/process/typecheck.h
      11             : /// \brief add your file description here.
      12             : 
      13             : #ifndef MCRL2_PROCESS_TYPECHECK_H
      14             : #define MCRL2_PROCESS_TYPECHECK_H
      15             : 
      16             : #include <algorithm>
      17             : #include "mcrl2/process/detail/match_action_parameters.h"
      18             : #include "mcrl2/process/detail/process_context.h"
      19             : #include "mcrl2/process/normalize_sorts.h"
      20             : 
      21             : namespace mcrl2
      22             : {
      23             : 
      24             : namespace process
      25             : {
      26             : 
      27             : inline
      28        3082 : action typecheck_action(const core::identifier_string& name,
      29             :                         const data::data_expression_list& parameters,
      30             :                         data::data_type_checker& typechecker,
      31             :                         const data::detail::variable_context& variable_context,
      32             :                         const detail::action_context& action_context
      33             :                        )
      34             : {
      35        3082 :   std::string msg = "action";
      36        3082 :   data::sorts_list parameter_list = action_context.matching_action_sorts(name, parameters);
      37        3082 :   auto p = process::detail::match_action_parameters(parameters, parameter_list, variable_context, name, msg, typechecker);
      38        9243 :   return action(action_label(name, p.second), p.first);
      39        3083 : }
      40             : 
      41             : // returns the intersection of the 2 type list lists
      42             : inline
      43         390 : data::sorts_list sorts_list_intersection(const data::sorts_list& sorts1, const data::sorts_list& sorts2)
      44             : {
      45         390 :   data::sorts_list result;
      46         852 :   for (const data::sort_expression_list& s: sorts2)
      47             :   {
      48         462 :     if (std::find(sorts1.begin(), sorts1.end(), s) != sorts1.end())
      49             :     {
      50         462 :       result.push_front(s);
      51             :     }
      52             :   }
      53         780 :   return atermpp::reverse(result);
      54         390 : }
      55             : 
      56             : inline
      57             : std::ostream& operator<<(std::ostream& out, const data::sorts_list& x)
      58             : {
      59             :   out << "[";
      60             :   for (auto i = x.begin(); i != x.end(); ++i)
      61             :   {
      62             :     if (i != x.begin())
      63             :     {
      64             :       out << ", ";
      65             :     }
      66             :     out << *i;
      67             :   }
      68             :   return out;
      69             : }
      70             : 
      71             : namespace detail
      72             : {
      73             : 
      74             : bool equal_multi_actions(core::identifier_string_list a1, core::identifier_string_list a2);
      75             : 
      76             : // returns true if a is in A
      77             : inline
      78         182 : bool multi_actions_contains(const core::identifier_string_list& a, const action_name_multiset_list& A)
      79             : {
      80         567 :   for (const action_name_multiset& i: A)
      81             :   {
      82         385 :     if (equal_multi_actions(a, i.names()))
      83             :     {
      84           0 :       return true;
      85             :     }
      86             :   }
      87         182 :   return false;
      88             : }
      89             : 
      90             : // returns true if the two multiactions are equal.
      91             : inline
      92         385 : bool equal_multi_actions(core::identifier_string_list a1, core::identifier_string_list a2)
      93             : {
      94         385 :   if (a1.size() != a2.size())
      95             :   {
      96           1 :     return false;
      97             :   }
      98         384 :   if (a1.empty())
      99             :   {
     100           0 :     return true;
     101             :   }
     102         384 :   core::identifier_string Act1 = a1.front();
     103         384 :   a1 = a1.tail();
     104             : 
     105             :   //remove Act1 once from a2. if not there -- return ATfalse.
     106         384 :   core::identifier_string_list NewMAct2;
     107         768 :   for (; !a2.empty(); a2 = a2.tail())
     108             :   {
     109         384 :     core::identifier_string Act2 = a2.front();
     110         384 :     if (Act1 == Act2)
     111             :     {
     112           0 :       a2 = atermpp::reverse(NewMAct2) + a2.tail();
     113           0 :       return equal_multi_actions(a1, a2);
     114             :     }
     115             :     else
     116             :     {
     117         384 :       NewMAct2.push_front(Act2);
     118             :     }
     119         384 :   }
     120         384 :   return false;
     121         384 : }
     122             : 
     123             : inline
     124             : std::ostream& operator<<(std::ostream& out, const std::pair<core::identifier_string, data::sort_expression_list>& x)
     125             : {
     126             :   return out << "(" << x.first << ", " << x.second << ")";
     127             : }
     128             : 
     129             : struct typecheck_builder: public process_expression_builder<typecheck_builder>
     130             : {
     131             :   typedef process_expression_builder<typecheck_builder> super;
     132             :   using super::apply;
     133             : 
     134             :   data::data_type_checker& m_data_type_checker;
     135             :   data::detail::variable_context m_variable_context;
     136             :   const detail::process_context& m_process_context;
     137             :   const detail::action_context& m_action_context;
     138             :   const process_identifier* m_current_equation;
     139             : 
     140        2293 :   typecheck_builder(data::data_type_checker& data_typechecker,
     141             :                     const data::detail::variable_context& variable_context,
     142             :                     const detail::process_context& process_context,
     143             :                     const detail::action_context& action_context,
     144             :                     const process_identifier* current_equation = nullptr
     145             :                    )
     146        2293 :     : m_data_type_checker(data_typechecker),
     147        2293 :       m_variable_context(variable_context),
     148        2293 :       m_process_context(process_context),
     149        2293 :       m_action_context(action_context),
     150        2293 :       m_current_equation(current_equation)
     151        2293 :   {}
     152             : 
     153         585 :   data::sorts_list action_sorts(const core::identifier_string& name)
     154             :   {
     155         585 :     return m_action_context.matching_action_sorts(name);
     156             :   }
     157             : 
     158          24 :   void check_action_declared(const core::identifier_string& a, const process_expression& x)
     159             :   {
     160          24 :     if (!m_action_context.is_declared(a))
     161             :     {
     162           0 :       throw mcrl2::runtime_error("Undefined action " + core::pp(a) + " (typechecking " + core::pp(x) + ")");
     163             :     }
     164          24 :   }
     165             : 
     166          24 :   void check_actions_declared(const core::identifier_string_list& act_list, const process_expression& x)
     167             :   {
     168          24 :     std::set<core::identifier_string> actions;
     169          48 :     for (const core::identifier_string& a: act_list)
     170             :     {
     171          24 :       check_action_declared(a, x);
     172          24 :       if (!actions.insert(a).second)  // The action was already in the set.
     173             :       {
     174           0 :         mCRL2log(log::warning) << "Used action " << a << " twice (typechecking " << x << ")" << std::endl;
     175             :       }
     176             :     }
     177          24 :   }
     178             : 
     179             :   template <typename Container>
     180         216 :   void check_not_empty(const Container& c, const std::string& msg, const process_expression& x)
     181             :   {
     182         216 :     if (c.empty())
     183             :     {
     184           0 :       mCRL2log(log::warning) << msg << " (typechecking " << x << ")" << std::endl;
     185             :     }
     186         216 :   }
     187             : 
     188             :   template <typename T>
     189           0 :   void check_not_equal(const T& first, const T& second, const std::string& msg, const process_expression& x)
     190             :   {
     191           0 :     if (first == second)
     192             :     {
     193           0 :       mCRL2log(log::warning) << msg << " " << first << "(typechecking " << x << ")" << std::endl;
     194             :     }
     195           0 :   }
     196             : 
     197           0 :   static bool has_empty_intersection(const data::sorts_list& s1, const data::sorts_list& s2)
     198             :   {
     199           0 :     std::set<data::sort_expression_list> v1(s1.begin(), s1.end());
     200           0 :     std::set<data::sort_expression_list> v2(s2.begin(), s2.end());
     201           0 :     return utilities::detail::has_empty_intersection(v1, v2);
     202           0 :   }
     203             : 
     204           0 :   void check_rename_common_type(const core::identifier_string& a, const core::identifier_string& b, const process_expression& x)
     205             :   {
     206           0 :     if (has_empty_intersection(action_sorts(a), action_sorts(b)))
     207             :     {
     208           0 :       throw mcrl2::runtime_error("renaming action " + core::pp(a) + " into action " + core::pp(b) + ": these two have no common type (typechecking " + process::pp(x) + ")");
     209             :     }
     210           0 :   }
     211             : 
     212         441 :   static std::map<core::identifier_string, data::data_expression> make_assignment_map(const data::untyped_identifier_assignment_list& assignments)
     213             :   {
     214         441 :     std::map<core::identifier_string, data::data_expression> result;
     215        1403 :     for (const data::untyped_identifier_assignment& a: assignments)
     216             :     {
     217         963 :       auto i = result.find(a.lhs());
     218         963 :       if (i != result.end()) // An data::assignment of the shape x := t already exists, this is not OK.
     219             :       {
     220           1 :         throw mcrl2::runtime_error("Double data::assignment to data::variable " + core::pp(a.lhs()) + " (detected assigned values are " + data::pp(i->second) + " and " + core::pp(a.rhs()) + ")");
     221             :       }
     222         962 :       result[a.lhs()]=a.rhs();
     223             :     }
     224         440 :     return result;
     225           1 :   }
     226             : 
     227        5009 :   bool is_action_name(const core::identifier_string& name)
     228             :   {
     229        5009 :     return m_action_context.is_declared(name);
     230             :   }
     231             : 
     232        2573 :   bool is_process_name(const core::identifier_string& name)
     233             :   {
     234        2573 :     return m_process_context.is_declared(name);
     235             :   }
     236             : 
     237        2883 :   action typecheck_action(const core::identifier_string& name, const data::data_expression_list& parameters)
     238             :   {
     239        2883 :     return process::typecheck_action(name, parameters, m_data_type_checker, m_variable_context, m_action_context);
     240             :   }
     241             : 
     242        2132 :   process_instance typecheck_process_instance(const core::identifier_string& name, const data::data_expression_list& parameters)
     243             :   {
     244        2132 :     std::string msg = "process";
     245        2132 :     data::sorts_list parameter_list = m_process_context.matching_process_sorts(name, parameters);
     246        2132 :     auto p = process::detail::match_action_parameters(parameters, parameter_list, m_variable_context, name, msg, m_data_type_checker);
     247        4264 :     return m_process_context.make_process_instance(name, p.second, p.first);
     248        2132 :   }
     249             : 
     250           0 :   std::string print_untyped_process_assignment(const untyped_process_assignment& x) const
     251             :   {
     252           0 :     if (x.assignments().empty())
     253             :     {
     254           0 :       return core::pp(x.name()) + "()";
     255             :     }
     256             :     else
     257             :     {
     258           0 :       return core::pp(x.name()) + core::detail::print_arguments(x.assignments());
     259             :     }
     260             :   }
     261             : 
     262             :   // Checks if in the equation P(..) = Q(assignments) all formal parameters of Q have been assigned a value,
     263             :   // either directly via an assignment in assignments, or indirectly via a formal parameter of P.
     264         423 :   void check_assignments(const process_identifier& P, const process_identifier& Q, const std::vector<data::assignment>& assignments, const untyped_process_assignment& x) const
     265             :   {
     266         423 :     std::set<data::variable> Q_variables;
     267        1359 :     for (const data::assignment& a: assignments)
     268             :     {
     269         936 :       Q_variables.insert(a.lhs());
     270             :     }
     271        2811 :     for (const data::variable& v: P.variables())
     272             :     {
     273        2388 :       Q_variables.insert(v);
     274             :     }
     275        2811 :     for (const data::variable& v: Q.variables())
     276             :     {
     277        2388 :       if (Q_variables.find(v) == Q_variables.end())
     278             :       {
     279           0 :         throw mcrl2::runtime_error("Process parameter " + core::pp(v.name()) + " has not been assigned a value in the process call with short-hand assignments " + print_untyped_process_assignment(x) + ".");
     280             :       }
     281             :     }
     282         423 :   }
     283             : 
     284             :   template <class T>
     285         441 :   void apply(T& result, const untyped_process_assignment& x)
     286             :   {
     287         441 :     mCRL2log(log::debug) << "typechecking a process call with short-hand assignments " << x << "" << std::endl;
     288         441 :     if (!is_process_name(x.name()))
     289             :     {
     290           0 :       throw mcrl2::runtime_error("Could not find a matching declaration for action or process expression " + print_untyped_process_assignment(x) + ".");
     291             :     }
     292             : 
     293         441 :     process_identifier P = m_process_context.match_untyped_process_instance_assignment(x);
     294         441 :     const data::variable_list& formal_parameters = P.variables();
     295             : 
     296             :     // This checks for duplicate left hand sides.
     297         441 :     std::map<core::identifier_string, data::data_expression> assignment_map = make_assignment_map(x.assignments());
     298             : 
     299         440 :     std::map<core::identifier_string, data::variable> formal_parameter_map;
     300        3294 :     for (const data::variable& v: formal_parameters)
     301             :     {
     302        2414 :       formal_parameter_map[v.name()] = v;
     303             :     }
     304             : 
     305             :     // Typecheck the right hand sides of the assignments
     306         440 :     std::vector<data::assignment> assignments;
     307        1841 :     for (const data::untyped_identifier_assignment& a: x.assignments())
     308             :     {
     309             :       try
     310             :       {
     311         961 :         data::variable v = formal_parameter_map[a.lhs()];
     312         961 :         formal_parameter_map.erase(v.name());
     313         961 :         data::data_expression e = m_data_type_checker.typecheck_data_expression(a.rhs(), v.sort(), m_variable_context);
     314         961 :         assignments.emplace_back(v, e);
     315         961 :       }
     316           0 :       catch (const mcrl2::runtime_error& e)
     317             :       {
     318           0 :         throw mcrl2::runtime_error(std::string(e.what()) + "\nType error occurred while typechecking the process call with short-hand assignments " + process::pp(x));
     319             :       }
     320             :     }
     321             : 
     322             :     // Check if all formal parameters have been assigned a value
     323         440 :     if (m_current_equation)
     324             :     {
     325         423 :       check_assignments(*m_current_equation, P, assignments, x);
     326             :     }
     327             : 
     328             :     // Check that for any non explicitly stated assignment x:=x, the rhs is well defined.
     329        1892 :     for (const auto& [name, var]: formal_parameter_map)
     330             :     {
     331             :       try
     332             :       {
     333        1453 :         m_data_type_checker.typecheck_data_expression(var, var.sort(), m_variable_context);
     334             :       }
     335           2 :       catch (const mcrl2::runtime_error& e)
     336             :       {
     337           1 :         throw mcrl2::runtime_error(std::string(e.what()) + "\nIn implicitly stated assignment " + pp(var) + "=" + pp(var) + " in process " + pp(P) + "(" + data::pp(assignments) + ")\n");
     338             :       }
     339             :     }
     340             : 
     341         439 :     make_process_instance_assignment(result, P, data::assignment_list(assignments.begin(), assignments.end()));
     342         444 :   }
     343             : 
     344             :   template <class T>
     345        5009 :   void apply(T& result, const data::untyped_data_parameter& x)
     346             :   {
     347        5009 :     if (is_action_name(x.name()))
     348             :     {
     349        2877 :       result = typecheck_action(x.name(), x.arguments());
     350             :     }
     351        2132 :     else if (is_process_name(x.name()))
     352             :     {
     353        2132 :       result = typecheck_process_instance(x.name(), x.arguments());
     354             :     }
     355             :     else
     356             :     {
     357           0 :       throw mcrl2::runtime_error("Could not find a matching declaration for action or process expression " + core::pp(x.name()) + core::detail::print_arguments(x.arguments()) + ".");
     358             :     }
     359        5008 :   }
     360             : 
     361             :   // This case is necessary for actionrename
     362             :   template <class T>
     363           6 :   void apply(T& result, const process::action& x)
     364             :   {
     365           6 :     result = typecheck_action(x.label().name(), x.arguments());
     366           6 :   }
     367             : 
     368             :   template <class T>
     369          24 :   void apply(T& result, const process::hide& x)
     370             :   {
     371          24 :     check_not_empty(x.hide_set(), "Hiding empty set of actions", x);
     372          24 :     check_actions_declared(x.hide_set(), x);
     373          72 :     process::make_hide(result, 
     374          24 :                        x.hide_set(), 
     375          24 :                        [&](process_expression& result){ (*this).apply(result, x.operand()); } );
     376          24 :   }
     377             : 
     378             :   template <class T>
     379           0 :   void apply(T& result, const process::block& x)
     380             :   {
     381           0 :     check_not_empty(x.block_set(), "Blocking empty set of actions", x);
     382           0 :     check_actions_declared(x.block_set(), x);
     383           0 :     make_block(result, x.block_set(), [&]( process_expression& result){ (*this).apply(result, x.operand()); });
     384           0 :   }
     385             : 
     386             :   template <class T>
     387           0 :   void apply(T& result, const process::rename& x)
     388             :   {
     389           0 :     check_not_empty(x.rename_set(), "Renaming empty set of actions", x);
     390             : 
     391           0 :     std::set<core::identifier_string> actions;
     392           0 :     for (const rename_expression& r: x.rename_set())
     393             :     {
     394           0 :       check_not_equal(r.source(), r.target(), "Renaming action into itself:", x);
     395           0 :       check_action_declared(r.source(), x);
     396           0 :       check_action_declared(r.target(), x);
     397           0 :       check_rename_common_type(r.source(), r.target(), x);
     398           0 :       if (!actions.insert(r.source()).second) // The element was already in the set.
     399             :       {
     400           0 :         throw mcrl2::runtime_error("renaming action " + core::pp(r.source()) + " twice (typechecking " + process::pp(x) + ")");
     401             :       }
     402             :     }
     403           0 :     make_rename(result, x.rename_set(), [&](process_expression& r){ (*this).apply(r, x.operand()); } );
     404           0 :   }
     405             : 
     406             :   //comm: like renaming multiactions (with the same parameters) to action/tau
     407             :   template <class T>
     408         122 :   void apply(T& result, const process::comm& x)
     409             :   {
     410         122 :     check_not_empty(x.comm_set(), "synchronizing empty set of (multi)actions", x);
     411             : 
     412         122 :     std::multiset<core::identifier_string> left_hand_side_actions;
     413         439 :     for (const communication_expression& c: x.comm_set())
     414             :     {
     415         195 :       const core::identifier_string_list& cnames = c.action_name().names();
     416         195 :       assert(!cnames.empty());
     417             : 
     418         195 :       if (cnames.size() == 1)
     419             :       {
     420           0 :         throw mcrl2::runtime_error("using synchronization as renaming/hiding of action " + core::pp(cnames.front()) + " into " + core::pp(c.name()) + " (typechecking " + process::pp(x) + ")");
     421             :       }
     422             : 
     423             :       //Actions must be declared
     424         195 :       data::sorts_list c_sorts;
     425         195 :       if (!m_action_context.is_declared(c.name()))
     426             :       {
     427           0 :         throw mcrl2::runtime_error("synchronizing to an undefined action " + core::pp(c.name()) + " (typechecking " + process::pp(x) + ")");
     428             :       }
     429         195 :       c_sorts = action_sorts(c.name());
     430             : 
     431         780 :       for (const core::identifier_string& a: cnames)
     432             :       {
     433         390 :         if (!m_action_context.is_declared(a))
     434             :         {
     435           0 :           throw mcrl2::runtime_error("synchronizing an undefined action " + core::pp(a) + " in (multi)action " + core::pp(cnames) + " (typechecking " + process::pp(x) + ")");
     436             :         }
     437         390 :         c_sorts = sorts_list_intersection(c_sorts, action_sorts(a));
     438         390 :         if (c_sorts.empty())
     439             :         {
     440           0 :           throw mcrl2::runtime_error("synchronizing action " + core::pp(a) + " from (multi)action " + core::pp(cnames) +
     441             :                             " into action " + core::pp(c.name()) + ": these have no common type (typechecking " + process::pp(x) + ")");
     442             :         }
     443             :       }
     444             : 
     445             :       //the multiactions in the lhss of comm should not intersect.
     446             :       //but there can be multiple of the same actions in cnames. 
     447         195 :       std::set < core::identifier_string > this_left_hand_sides;
     448         780 :       for (const core::identifier_string& a: cnames)
     449             :       {
     450         390 :         if (this_left_hand_sides.count(a)==0 && left_hand_side_actions.find(a) != left_hand_side_actions.end())
     451             :         {
     452           0 :           throw mcrl2::runtime_error("synchronizing action " + core::pp(a) + " in different ways (typechecking " + process::pp(x) + ")");
     453             :         }
     454             :         else
     455             :         {
     456         390 :           left_hand_side_actions.insert(a);
     457         390 :           this_left_hand_sides.insert(a);
     458             :         }
     459             :       }
     460             :     }
     461             : 
     462             :     //the multiactions in the rhs of comm should not intersect with an action in the a lhs of
     463             :     //all other rules. So, {a|a->a} is allowed, but {a|b->c, c|a->d} is not. 
     464         439 :     for (const communication_expression& c: x.comm_set())
     465             :     {
     466             :       // Count how many actions in the rhs occur at the left
     467         195 :       std::size_t number_of_rhs_in_lhs=0;
     468         780 :       for (const core::identifier_string& lhs_action: c.action_name().names())
     469             :       { 
     470         390 :         if (lhs_action==c.name())
     471             :         {
     472          12 :           number_of_rhs_in_lhs++;
     473             :         }
     474             :       }
     475         195 :       assert(left_hand_side_actions.count(c.name())>=number_of_rhs_in_lhs);
     476         195 :       if (left_hand_side_actions.count(c.name())-number_of_rhs_in_lhs>0) // There are more actions x.name() in the lhss than just in this lhs.
     477             :       {
     478           0 :         throw mcrl2::runtime_error("action " + core::pp(c.name()) + 
     479             :                      " occurs at the left and the right in a communication (typechecking " + process::pp(x) + ")");
     480             :       }
     481             :     }
     482         244 :     make_comm(result, x.comm_set(), [&](process_expression& r){ (*this).apply(r, x.operand()); } );
     483         122 :   }
     484             : 
     485             :   template <class T>
     486          70 :   void apply(T& result, const process::allow& x)
     487             :   {
     488          70 :     check_not_empty(x.allow_set(), "Allowing empty set of (multi) actions", x);
     489          70 :     action_name_multiset_list MActs;
     490         322 :     for (const action_name_multiset& A: x.allow_set())
     491             :     {
     492             :       //Actions must be declared
     493         577 :       for (const core::identifier_string& a: A.names())
     494             :       {
     495         213 :         if (!m_action_context.is_declared(a))
     496             :         {
     497           0 :           throw mcrl2::runtime_error("allowing an undefined action " + core::pp(a) + " in (multi)action " + core::pp(A.names()) + " (typechecking " + process::pp(x) + ")");
     498             :         }
     499             :       }
     500         182 :       if (multi_actions_contains(A.names(), MActs))
     501             :       {
     502           0 :         mCRL2log(log::warning) << "allowing (multi)action " << A.names() << " twice (typechecking " << x << ")" << std::endl;
     503             :       }
     504             :       else
     505             :       {
     506         182 :         MActs.push_front(mcrl2::process::action_name_multiset(A.names()));
     507             :       }
     508             :     }
     509         140 :     make_allow(result, x.allow_set(), [&](process_expression& r){ (*this).apply(r, x.operand()); } );
     510          70 :   }
     511             : 
     512             :   template <class T>
     513         342 :   void apply(T& result, const process::at& x)
     514             :   {
     515         342 :     data::data_expression new_time = m_data_type_checker.typecheck_data_expression(x.time_stamp(), data::sort_real::real_(), m_variable_context);
     516         684 :     make_at(result, [&](process_expression& r){ (*this).apply(r, x.operand()); }, new_time);
     517         342 :   }
     518             : 
     519             :   template <class T>
     520         672 :   void apply(T& result, const process::if_then& x)
     521             :   {
     522         672 :     data::data_expression condition = m_data_type_checker.typecheck_data_expression(x.condition(), data::sort_bool::bool_(), m_variable_context);
     523        1342 :     make_if_then(result, condition, [&](process_expression& r){ (*this).apply(r, x.then_case()); } );
     524         671 :   }
     525             : 
     526             :   template <class T>
     527         125 :   void apply(T& result, const process::if_then_else& x)
     528             :   {
     529         125 :     data::data_expression condition = m_data_type_checker.typecheck_data_expression(x.condition(), data::sort_bool::bool_(), m_variable_context);
     530         125 :     make_if_then_else(result, 
     531             :                       condition, 
     532         125 :                       [&](process_expression& r){ (*this).apply(r, x.then_case()); }, 
     533         125 :                       [&](process_expression& r){ (*this).apply(r, x.else_case()); } );
     534         125 :   }
     535             : 
     536             :   template <class T>
     537         618 :   void apply(T& result, const process::sum& x)
     538             :   {
     539             :     try
     540             :     {
     541         618 :       auto m_variable_context_copy = m_variable_context;
     542         618 :       m_variable_context.add_context_variables(x.variables(), m_data_type_checker);
     543         618 :       process_expression operand;
     544         618 :       (*this).apply(operand, x.operand());
     545         618 :       m_variable_context = m_variable_context_copy;
     546         618 :       make_sum(result, x.variables(), operand);
     547         618 :     }
     548           0 :     catch (mcrl2::runtime_error& e)
     549             :     {
     550           0 :       throw mcrl2::runtime_error(std::string(e.what()) + "\nwhile typechecking " + process::pp(x));
     551             :     }
     552         618 :   }
     553             : 
     554             :   template <class T>
     555         136 :   void apply(T& result, const process::stochastic_operator& x)
     556             :   {
     557             :     try
     558             :     {
     559         136 :       auto m_variable_context_copy = m_variable_context;
     560         136 :       m_variable_context.add_context_variables(x.variables(), m_data_type_checker);
     561         136 :       data::data_expression distribution = m_data_type_checker.typecheck_data_expression(x.distribution(), data::sort_real::real_(), m_variable_context);
     562         136 :       process_expression operand;
     563         136 :       (*this).apply(operand, x.operand());
     564         136 :       m_variable_context = m_variable_context_copy;
     565         136 :       make_stochastic_operator(result, x.variables(), distribution, operand);
     566         136 :     }
     567           0 :     catch (mcrl2::runtime_error& e)
     568             :     {
     569           0 :       throw mcrl2::runtime_error(std::string(e.what()) + "\nwhile typechecking " + process::pp(x));
     570             :     }
     571         136 :   }
     572             : };
     573             : 
     574             : inline
     575        2293 : typecheck_builder make_typecheck_builder(
     576             :                     data::data_type_checker& data_typechecker,
     577             :                     const data::detail::variable_context& variables,
     578             :                     const detail::process_context& process_identifiers,
     579             :                     const detail::action_context& action_context,
     580             :                     const process_identifier* current_equation = nullptr
     581             :                    )
     582             : {
     583        2293 :   return typecheck_builder(data_typechecker, variables, process_identifiers, action_context, current_equation);
     584             : }
     585             : 
     586             : } // namespace detail
     587             : 
     588             : class process_type_checker
     589             : {
     590             :   protected:
     591             :     data::data_type_checker m_data_type_checker;
     592             :     detail::action_context m_action_context;
     593             :     detail::process_context m_process_context;
     594             :     data::detail::variable_context m_variable_context;
     595             : 
     596        1136 :     static std::vector<process_identifier> equation_identifiers(const std::vector<process_equation>& equations)
     597             :     {
     598        1136 :       std::vector<process_identifier> result;
     599        2281 :       for (const process_equation& eqn: equations)
     600             :       {
     601        1145 :         result.push_back(eqn.identifier());
     602             :       }
     603        1136 :       return result;
     604           0 :     }
     605             : 
     606             :   public:
     607             :     template <typename VariableContainer, typename ActionLabelContainer, typename ProcessIdentifierContainer>
     608          13 :     process_type_checker(const data::data_specification& dataspec,
     609             :                          const VariableContainer& variables,
     610             :                          const ActionLabelContainer& action_labels,
     611             :                          const ProcessIdentifierContainer& process_identifiers
     612             :                         )
     613          13 :       : m_data_type_checker(dataspec)
     614             :     {
     615          13 :       m_action_context.add_context_action_labels(action_labels, m_data_type_checker);
     616          13 :       m_variable_context.add_context_variables(variables, m_data_type_checker);
     617          13 :       m_process_context.add_process_identifiers(process_identifiers, m_action_context, m_data_type_checker);
     618          13 :     }
     619             : 
     620             :     /// \brief Default constructor
     621        1152 :     explicit process_type_checker(const data::data_specification& dataspec = data::data_specification())
     622        1152 :       : m_data_type_checker(dataspec)
     623        1152 :     {}
     624             : 
     625             :     /** \brief     Type check a process expression.
     626             :      *  Throws a mcrl2::runtime_error exception if the expression is not well typed.
     627             :      *  \param[in] x A process expression that has not been type checked.
     628             :      *  \return    a process expression where all untyped identifiers have been replace by typed ones.
     629             :      **/    /// \brief Typecheck the pbes pbesspec
     630          13 :     process_expression operator()(const process_expression& x, const process_identifier* current_equation = nullptr)
     631             :     {
     632          26 :       return typecheck_process_expression(m_variable_context, process::normalize_sorts(x, m_data_type_checker.typechecked_data_specification()), current_equation);
     633             :     }
     634             : 
     635             :     /// \brief Typecheck the process specification procspec
     636        1152 :     void operator()(process_specification& procspec)
     637             :     {
     638        1152 :       mCRL2log(log::verbose) << "type checking process specification..." << std::endl;
     639             : 
     640             :       // reset the context
     641        1152 :       m_data_type_checker = data::data_type_checker(procspec.data());
     642             : 
     643        1136 :       process::normalize_sorts(procspec, m_data_type_checker.typechecked_data_specification());
     644             : 
     645        1136 :       m_action_context.clear();
     646        1136 :       m_variable_context.clear();
     647        1136 :       m_process_context.clear();
     648        1136 :       m_action_context.add_context_action_labels(procspec.action_labels(), m_data_type_checker);
     649        1136 :       m_variable_context.add_context_variables(procspec.global_variables(), m_data_type_checker);
     650        1136 :       m_process_context.add_process_identifiers(equation_identifiers(procspec.equations()), m_action_context, m_data_type_checker);
     651             : 
     652             :       // typecheck the equations
     653        2280 :       for (process_equation& eqn: procspec.equations())
     654             :       {
     655        1145 :         data::detail::variable_context variable_context = m_variable_context;
     656        1145 :         variable_context.add_context_variables(eqn.identifier().variables(), m_data_type_checker);
     657        1145 :         eqn = process_equation(eqn.identifier(), eqn.formal_parameters(), typecheck_process_expression(variable_context, eqn.expression(), &eqn.identifier()));
     658        1145 :       }
     659             : 
     660             :       // typecheck the initial state
     661        1135 :       procspec.init() = typecheck_process_expression(m_variable_context, procspec.init());
     662             : 
     663             :       // typecheck the data specification
     664        1132 :       procspec.data() = m_data_type_checker.typechecked_data_specification();
     665        1132 :       procspec.data().translate_user_notation();
     666             :       
     667             : 
     668        1132 :       mCRL2log(log::debug) << "type checking process specification finished" << std::endl;
     669        1132 :     }
     670             : 
     671             :   protected:
     672        2293 :     process_expression typecheck_process_expression(const data::detail::variable_context& variables, const process_expression& x, const process_identifier* current_equation = nullptr)
     673             :     {
     674        2293 :       process_expression result;
     675        2297 :       detail::make_typecheck_builder(m_data_type_checker, variables, m_process_context, m_action_context, current_equation).apply(result, x);
     676        2289 :       return result;
     677           4 :     }
     678             : };
     679             : 
     680             : /** \brief     Type check a parsed mCRL2 process specification.
     681             :  *  Throws an exception if something went wrong.
     682             :  *  \param[in] proc_spec A process specification  that has not been type checked.
     683             :  *  \post      proc_spec is type checked.
     684             :  **/
     685             : 
     686             : inline
     687        1152 : void typecheck_process_specification(process_specification& proc_spec)
     688             : {
     689        1152 :   process_type_checker type_checker;
     690        1152 :   type_checker(proc_spec);
     691        1152 : }
     692             : 
     693             : /// \brief Typecheck a process expression
     694             : /// \param x An untyped process expression
     695             : /// \param variables A sequence of data variables
     696             : /// \param dataspec A data specification
     697             : /// \param action_labels A sequence of action labels
     698             : /// \param process_identifiers A sequence of process identifiers
     699             : /// \param current_equation A pointer to the current equation. If this pointer is set, a check will be done it
     700             : /// process instance assignments assign values to all their parameters.
     701             : template <typename VariableContainer, typename ActionLabelContainer, typename ProcessIdentifierContainer>
     702          13 : process_expression typecheck_process_expression(const process_expression& x,
     703             :                                                 const VariableContainer& variables = VariableContainer(),
     704             :                                                 const data::data_specification& dataspec = data::data_specification(),
     705             :                                                 const ActionLabelContainer& action_labels = ActionLabelContainer(),
     706             :                                                 const ProcessIdentifierContainer& process_identifiers = ProcessIdentifierContainer(),
     707             :                                                 const process_identifier* current_equation = nullptr
     708             :                                                )
     709             : {
     710          13 :   process_type_checker type_checker(dataspec, variables, action_labels, process_identifiers);
     711          26 :   return type_checker(x, current_equation);
     712          13 : }
     713             : 
     714             : } // namespace process
     715             : 
     716             : } // namespace mcrl2
     717             : 
     718             : #endif // MCRL2_PROCESS_TYPECHECK_H

Generated by: LCOV version 1.14