fn def_ana_schema_exp_leaf(
        &mut self,
        context_type_params: &[(Symbol, Type)],
        vars: &mut BTreeMap<Symbol, LocalVarEntry>,
        spec: &mut Spec,
        allow_new_vars: bool,
        path_cond: Option<Exp>,
        schema_properties: &PropertyBag,
        loc: &Loc,
        maccess: &EA::ModuleAccess,
        type_args_opt: &Option<Vec<EA::Type>>,
        args_opt: Option<&EA::Fields<EA::Exp>>,
    ) {
        let schema_name = self.module_access_to_qualified(maccess);

        // Remove schema from unused table since it is used in an expression
        self.parent.unused_schema_set.remove(&schema_name);

        // We need to temporarily detach the schema entry from the parent table because of
        // borrowing problems, as we need to traverse it while at the same time mutate self.
        let schema_entry = if let Some(e) = self.parent.spec_schema_table.remove(&schema_name) {
            e
        } else {
            self.parent.error(
                loc,
                &format!(
                    "schema `{}` undeclared",
                    schema_name.display(self.symbol_pool())
                ),
            );
            return;
        };

        // Translate type arguments
        let mut et = self.exp_translator_for_schema(&loc, context_type_params, vars);
        let type_arguments = &et.translate_types_opt(type_args_opt);
        if schema_entry.type_params.len() != type_arguments.len() {
            self.parent.error(
                loc,
                &format!(
                    "wrong number of type arguments (expected {}, got {})",
                    schema_entry.type_params.len(),
                    type_arguments.len()
                ),
            );
            // Don't forget to put schema back.
            self.parent
                .spec_schema_table
                .insert(schema_name, schema_entry);
            return;
        }

        // Translate schema arguments.
        let mut argument_map: BTreeMap<Symbol, Exp> = args_opt
            .map(|args| {
                args.iter()
                    .map(|(ref schema_var, (_, exp))| {
                        let pool = et.symbol_pool();
                        let schema_sym = pool.make(&schema_var.0.value);
                        let schema_type = if let Some(LocalVarEntry { type_, .. }) =
                            schema_entry.all_vars.get(&schema_sym)
                        {
                            type_.instantiate(type_arguments)
                        } else {
                            et.error(
                                &et.to_loc(&schema_var.0.loc),
                                &format!("`{}` not declared in schema", schema_sym.display(pool)),
                            );
                            Type::Error
                        };
                        // Check the expression in the argument list.
                        // Note we currently only use the vars defined so far in this context. Variables
                        // which are introduced by schemas after the inclusion of this one are not in scope.
                        let exp = et.translate_exp(exp, &schema_type);
                        et.finalize_types();
                        (schema_sym, exp)
                    })
                    .collect()
            })
            .unwrap_or_else(BTreeMap::new);

        // Go over all variables in the schema which are not in the argument map and either match
        // them against existing one or declare new, if allowed.
        for (name, LocalVarEntry { type_, .. }) in &schema_entry.all_vars {
            if argument_map.contains_key(name) {
                continue;
            }
            let ty = type_.instantiate(type_arguments);
            let pool = et.symbol_pool();
            if let Some(entry) = vars.get(name) {
                // Name already exists in inclusion context, check its type.
                et.check_type(
                    loc,
                    &ty,
                    &entry.type_,
                    &format!(
                        "for `{}` included from schema",
                        name.display(et.symbol_pool())
                    ),
                );
                // Put into argument map.
                let node_id = et.new_node_id_with_type_loc(&entry.type_, loc);
                let exp = if let Some(oper) = &entry.operation {
                    Exp::Call(node_id, oper.clone(), vec![])
                } else {
                    Exp::LocalVar(node_id, *name)
                };
                argument_map.insert(*name, exp);
            } else if allow_new_vars {
                // Name does not yet exists in inclusion context, but is allowed to be introduced.
                // This happens if we include a schema in another schema.
                vars.insert(
                    *name,
                    LocalVarEntry {
                        loc: loc.clone(),
                        type_: ty.clone(),
                        operation: None,
                    },
                );
            } else {
                et.error(
                    loc,
                    &format!(
                        "`{}` cannot be matched to an existing name in inclusion context",
                        name.display(pool)
                    ),
                );
            }
        }
        // Done with expression translator; ensure all types are inferred correctly.
        et.finalize_types();

        // Go over all conditions in the schema, rewrite them, and add to the inclusion conditions.
        for Condition {
            loc,
            kind,
            properties,
            exp,
        } in schema_entry
            .spec
            .conditions
            .iter()
            .chain(schema_entry.included_spec.conditions.iter())
        {
            let mut rewriter =
                ExpRewriter::new(self, schema_entry.module_id, &argument_map, type_arguments);
            let mut exp = rewriter.rewrite(exp);
            if let Some(cond) = &path_cond {
                // There is a path condition to be added. This is only possible for proper
                // boolean conditions.
                if kind.get_spec_var_target().is_some() {
                    self.parent
                        .error(loc, &format!("`{}` cannot be included conditionally", kind));
                } else {
                    // In case of AbortsIf, the path condition is combined with the predicate using
                    // &&, otherwise ==>.
                    exp = self.make_path_expr(
                        if kind == &ConditionKind::AbortsIf {
                            Operation::And
                        } else {
                            Operation::Implies
                        },
                        cond.node_id(),
                        cond.clone(),
                        exp,
                    );
                }
            }
            let mut effective_properties = schema_properties.clone();
            effective_properties.extend(properties.clone());
            spec.conditions.push(Condition {
                loc: loc.clone(),
                kind: kind.clone(),
                properties: effective_properties,
                exp,
            });
        }

        // Put schema entry back.
        self.parent
            .spec_schema_table
            .insert(schema_name, schema_entry);
    }
/// Analyzes a schema leaf expression.
Analyzes a schema leaf expression.
Analyzes a schema leaf expression .
fn def_ana_schema_exp_leaf(
        &mut self,
        context_type_params: &[(Symbol, Type)],
        vars: &mut BTreeMap<Symbol, LocalVarEntry>,
        spec: &mut Spec,
        allow_new_vars: bool,
        path_cond: Option<Exp>,
        schema_properties: &PropertyBag,
        loc: &Loc,
        maccess: &EA::ModuleAccess,
        type_args_opt: &Option<Vec<EA::Type>>,
        args_opt: Option<&EA::Fields<EA::Exp>>,
    ) {
        let schema_name = self.module_access_to_qualified(maccess);
        // Remove schema from unused table since it is used in an expression
        self.parent.unused_schema_set.remove(&schema_name);
        // We need to temporarily detach the schema entry from the parent table because of
        // borrowing problems, as we need to traverse it while at the same time mutate self.
        let schema_entry = if let Some(e) = self.parent.spec_schema_table.remove(&schema_name) {
            e
        } else {
            self.parent.error(
                loc,
                &format!(
                    "schema `{}` undeclared",
                    schema_name.display(self.symbol_pool())
                ),
            );
            return;
        };
        // Translate type arguments
        let mut et = self.exp_translator_for_schema(&loc, context_type_params, vars);
        let type_arguments = &et.translate_types_opt(type_args_opt);
        if schema_entry.type_params.len() != type_arguments.len() {
            self.parent.error(
                loc,
                &format!(
                    "wrong number of type arguments (expected {}, got {})",
                    schema_entry.type_params.len(),
                    type_arguments.len()
                ),
            );
            // Don't forget to put schema back.
            self.parent
                .spec_schema_table
                .insert(schema_name, schema_entry);
            return;
        }
        // Translate schema arguments.
        let mut argument_map: BTreeMap<Symbol, Exp> = args_opt
            .map(|args| {
                args.iter()
                    .map(|(ref schema_var, (_, exp))| {
                        let pool = et.symbol_pool();
                        let schema_sym = pool.make(&schema_var.0.value);
                        let schema_type = if let Some(LocalVarEntry { type_, .. }) =
                            schema_entry.all_vars.get(&schema_sym)
                        {
                            type_.instantiate(type_arguments)
                        } else {
                            et.error(
                                &et.to_loc(&schema_var.0.loc),
                                &format!("`{}` not declared in schema", schema_sym.display(pool)),
                            );
                            Type::Error
                        };
                        // Check the expression in the argument list.
                        // Note we currently only use the vars defined so far in this context. Variables
                        // which are introduced by schemas after the inclusion of this one are not in scope.
                        let exp = et.translate_exp(exp, &schema_type);
                        et.finalize_types();
                        (schema_sym, exp)
                    })
                    .collect()
            })
            .unwrap_or_else(BTreeMap::new);
        // Go over all variables in the schema which are not in the argument map and either match
        // them against existing one or declare new, if allowed.
        for (name, LocalVarEntry { type_, .. }) in &schema_entry.all_vars {
            if argument_map.contains_key(name) {
                continue;
            }
            let ty = type_.instantiate(type_arguments);
            let pool = et.symbol_pool();
            if let Some(entry) = vars.get(name) {
                // Name already exists in inclusion context, check its type.
                et.check_type(
                    loc,
                    &ty,
                    &entry.type_,
                    &format!(
                        "for `{}` included from schema",
                        name.display(et.symbol_pool())
                    ),
                );
                // Put into argument map.
                let node_id = et.new_node_id_with_type_loc(&entry.type_, loc);
                let exp = if let Some(oper) = &entry.operation {
                    Exp::Call(node_id, oper.clone(), vec![])
                } else {
                    Exp::LocalVar(node_id, *name)
                };
                argument_map.insert(*name, exp);
            } else if allow_new_vars {
                // Name does not yet exists in inclusion context, but is allowed to be introduced.
                // This happens if we include a schema in another schema.
                vars.insert(
                    *name,
                    LocalVarEntry {
                        loc: loc.clone(),
                        type_: ty.clone(),
                        operation: None,
                    },
                );
            } else {
                et.error(
                    loc,
                    &format!(
                        "`{}` cannot be matched to an existing name in inclusion context",
                        name.display(pool)
                    ),
                );
            }
        }
        // Done with expression translator; ensure all types are inferred correctly.
        et.finalize_types();
        // Go over all conditions in the schema, rewrite them, and add to the inclusion conditions.
        for Condition {
            loc,
            kind,
            properties,
            exp,
        } in schema_entry
            .spec
            .conditions
            .iter()
            .chain(schema_entry.included_spec.conditions.iter())
        {
            let mut rewriter =
                ExpRewriter::new(self, schema_entry.module_id, &argument_map, type_arguments);
            let mut exp = rewriter.rewrite(exp);
            if let Some(cond) = &path_cond {
                // There is a path condition to be added. This is only possible for proper
                // boolean conditions.
                if kind.get_spec_var_target().is_some() {
                    self.parent
                        .error(loc, &format!("`{}` cannot be included conditionally", kind));
                } else {
                    // In case of AbortsIf, the path condition is combined with the predicate using
                    // &&, otherwise ==>.
                    exp = self.make_path_expr(
                        if kind == &ConditionKind::AbortsIf {
                            Operation::And
                        } else {
                            Operation::Implies
                        },
                        cond.node_id(),
                        cond.clone(),
                        exp,
                    );
                }
            }
            let mut effective_properties = schema_properties.clone();
            effective_properties.extend(properties.clone());
            spec.conditions.push(Condition {
                loc: loc.clone(),
                kind: kind.clone(),
                properties: effective_properties,
                exp,
            });
        }
        // Put schema entry back.
        self.parent
            .spec_schema_table
            .insert(schema_name, schema_entry);
    }
fn def_ana_schema_exp_leaf ( & mut self , context_type_params : & [ ( Symbol , Type ) ] , vars : & mut BTreeMap < Symbol , LocalVarEntry > , spec : & mut Spec , allow_new_vars : bool , path_cond : Option < Exp > , schema_properties : & PropertyBag , loc : & Loc , maccess : & EA :: ModuleAccess , type_args_opt : & Option < Vec < EA :: Type > > , args_opt : Option < & EA :: Fields < EA :: Exp > > , ) { let schema_name = self . module_access_to_qualified ( maccess ) ; // Remove schema from unused table since it is used in an expression self . parent . unused_schema_set . remove ( & schema_name ) ; // We need to temporarily detach the schema entry from the parent table because of // borrowing problems, as we need to traverse it while at the same time mutate self. let schema_entry = if let Some ( e ) = self . parent . spec_schema_table . remove ( & schema_name ) { e } else { self . parent . error ( loc , & format ! ( " " schema_name display ( self symbol_pool ( ) ) ) , ) ; return ; } ; // Translate type arguments let mut et = self . exp_translator_for_schema ( & loc , context_type_params , vars ) ; let type_arguments = & et . translate_types_opt ( type_args_opt ) ; if schema_entry . type_params . len ( ) != type_arguments . len ( ) { self . parent . error ( loc , & format ! ( " " schema_entry type_params len ( ) type_arguments len ( ) ) , ) ; // Don't forget to put schema back. self . parent . spec_schema_table . insert ( schema_name , schema_entry ) ; return ; } // Translate schema arguments. let mut argument_map : BTreeMap < Symbol , Exp > = args_opt . map ( | args | { args . iter ( ) . map ( | ( ref schema_var , ( _ , exp ) ) | { let pool = et . symbol_pool ( ) ; let schema_sym = pool . make ( & schema_var . 0 . value ) ; let schema_type = if let Some ( LocalVarEntry { type_ , .. } ) = schema_entry . all_vars . get ( & schema_sym ) { type_ . instantiate ( type_arguments ) } else { et . error ( & et . to_loc ( & schema_var . 0 . loc ) , & format ! ( " " schema_sym display ( pool ) ) , ) ; Type :: Error } ; // Check the expression in the argument list. // Note we currently only use the vars defined so far in this context. Variables // which are introduced by schemas after the inclusion of this one are not in scope. let exp = et . translate_exp ( exp , & schema_type ) ; et . finalize_types ( ) ; ( schema_sym , exp ) } ) . collect ( ) } ) . unwrap_or_else ( BTreeMap :: new ) ; // Go over all variables in the schema which are not in the argument map and either match // them against existing one or declare new, if allowed. for ( name , LocalVarEntry { type_ , .. } ) in & schema_entry . all_vars { if argument_map . contains_key ( name ) { continue ; } let ty = type_ . instantiate ( type_arguments ) ; let pool = et . symbol_pool ( ) ; if let Some ( entry ) = vars . get ( name ) { // Name already exists in inclusion context, check its type. et . check_type ( loc , & ty , & entry . type_ , & format ! ( " " name display ( et symbol_pool ( ) ) ) , ) ; // Put into argument map. let node_id = et . new_node_id_with_type_loc ( & entry . type_ , loc ) ; let exp = if let Some ( oper ) = & entry . operation { Exp :: Call ( node_id , oper . clone ( ) , vec ! [ ] ) } else { Exp :: LocalVar ( node_id , * name ) } ; argument_map . insert ( * name , exp ) ; } else if allow_new_vars { // Name does not yet exists in inclusion context, but is allowed to be introduced. // This happens if we include a schema in another schema. vars . insert ( * name , LocalVarEntry { loc : loc . clone ( ) , type_ : ty . clone ( ) , operation : None , } , ) ; } else { et . error ( loc , & format ! ( " " name display ( pool ) ) , ) ; } } // Done with expression translator; ensure all types are inferred correctly. et . finalize_types ( ) ; // Go over all conditions in the schema, rewrite them, and add to the inclusion conditions. for Condition { loc , kind , properties , exp , } in schema_entry . spec . conditions . iter ( ) . chain ( schema_entry . included_spec . conditions . iter ( ) ) { let mut rewriter = ExpRewriter :: new ( self , schema_entry . module_id , & argument_map , type_arguments ) ; let mut exp = rewriter . rewrite ( exp ) ; if let Some ( cond ) = & path_cond { // There is a path condition to be added. This is only possible for proper // boolean conditions. if kind . get_spec_var_target ( ) . is_some ( ) { self . parent . error ( loc , & format ! ( " " kind ) ) ; } else { // In case of AbortsIf, the path condition is combined with the predicate using // &&, otherwise ==>. exp = self . make_path_expr ( if kind == & ConditionKind :: AbortsIf { Operation :: And } else { Operation :: Implies } , cond . node_id ( ) , cond . clone ( ) , exp , ) ; } } let mut effective_properties = schema_properties . clone ( ) ; effective_properties . extend ( properties . clone ( ) ) ; spec . conditions . push ( Condition { loc : loc . clone ( ) , kind : kind . clone ( ) , properties : effective_properties , exp , } ) ; } // Put schema entry back. self . parent . spec_schema_table . insert ( schema_name , schema_entry ) ; }
Analyzes a schema leaf expression.
Analyzes a schema leaf expression .
&mut self
context_type_params
&[(Symbol, Type)]
vars
&mut BTreeMap<Symbol, LocalVarEntry>
spec
&mut Spec
allow_new_vars
bool
path_cond
Option<Exp>
schema_properties
&PropertyBag
loc
&Loc
maccess
&EA::ModuleAccess
type_args_opt
&Option<Vec<EA::Type>>
args_opt
Option<&EA::Fields<EA::Exp>>
&mut self
context_type_params
&[(Symbol, Type)]
vars
&mut BTreeMap<Symbol, LocalVarEntry>
spec
&mut Spec
allow_new_vars
bool
path_cond
Option<Exp>
schema_properties
&PropertyBag
loc
&Loc
maccess
&EA::ModuleAccess
type_args_opt
&Option<Vec<EA::Type>>
args_opt
Option<&EA::Fields<EA::Exp>>