open_vaf/ast_lowering/
branch_resolution.rs

1/*
2 * ******************************************************************************************
3 * Copyright (c) 2019 Pascal Kuthe. This file is part of the OpenVAF project.
4 * It is subject to the license terms in the LICENSE file found in the top-level directory
5 *  of this distribution and at  https://gitlab.com/DSPOM/OpenVAF/blob/master/LICENSE.
6 *  No part of OpenVAF, including this file, may be copied, modified, propagated, or
7 *  distributed except according to the terms contained in the LICENSE file.
8 * *****************************************************************************************
9 */
10
11use crate::ast::NetType::GROUND;
12use crate::ast_lowering::ast_to_hir_fold::Fold;
13use crate::ast_lowering::error::{Error, NetInfo, Type};
14use crate::hir::Net;
15use crate::hir::{Branch, BranchDeclaration, DisciplineAccess};
16use crate::ir::*;
17use crate::symbol::{keywords, Ident};
18use crate::{ast, Ast, Span};
19use rustc_hash::FxHashMap;
20
21/// Handles branch resolution which is more complicated because unnamed branches exist and discipline comparability has to be enforced
22/// # Safety
23/// Branch resolution may only take place after all nets and ports have been folded! UB might occur otherwise.
24pub struct BranchResolver {
25    unnamed_branches: FxHashMap<(NetId, NetId), BranchId>,
26    unnamed_port_branches: FxHashMap<PortId, BranchId>,
27    implicit_grounds: FxHashMap<DisciplineId, NetId>,
28}
29
30impl<'lt> BranchResolver {
31    pub fn new(ast: &'lt Ast) -> Self {
32        Self {
33            unnamed_port_branches: FxHashMap::with_capacity_and_hasher(16, Default::default()),
34            unnamed_branches: FxHashMap::with_capacity_and_hasher(32, Default::default()),
35            implicit_grounds: FxHashMap::with_capacity_and_hasher(
36                ast.disciplines.len() as usize,
37                Default::default(),
38            ),
39        }
40    }
41    /// Resolves a DisciplineAccess (for example `V(b)` or `V(x,y)`)
42    ///
43    /// # Arguments
44    ///
45    /// * fold - The calling fold which is used for name resolution and error handling
46    ///
47    /// * nature_name - The identifier of the nature (for example `V` in the case of `V(X,Y)`)
48    ///
49    /// * discipline - The id of the Discipline of a BranchAccess ( that has been resolved using [`resolve_branch_access`](crate::ast_lowering::branch_resolution::BranchResolver::resolve_discipline_access)
50    ///
51    ///
52    /// # Safety
53    /// This is only save to call
54    ///
55    ///
56    pub fn resolve_discipline_access(
57        &mut self,
58        fold: &mut Fold<'lt>,
59        nature_name: &Ident,
60        discipline: DisciplineId,
61    ) -> Option<DisciplineAccess> {
62        match nature_name.name {
63            keywords::FLOW => Some(DisciplineAccess::Flow),
64            keywords::POTENTIAL => Some(DisciplineAccess::Potential),
65            _ => {
66                resolve!(fold; nature_name as
67                    Nature(id) => {
68                        return Self::resolve_nature_access(fold,id,discipline);
69                    }
70                );
71                None
72            }
73        }
74    }
75
76    pub fn resolve_nature_access(
77        fold: &mut Fold<'lt>,
78        id: NatureId,
79        discipline: DisciplineId,
80    ) -> Option<DisciplineAccess> {
81        match id {
82            id if Some(id) == fold.hir[discipline].contents.flow_nature => {
83                Some(DisciplineAccess::Flow)
84            }
85            id if Some(id) == fold.hir[discipline].contents.potential_nature => {
86                Some(DisciplineAccess::Potential)
87            }
88            _ => {
89                fold.error(Error {
90                    source: fold.hir[id].contents.name.span,
91                    error_type: Type::NatureNotPotentialOrFlow(
92                        fold.hir[id].contents.name.name,
93                        discipline,
94                    ),
95                });
96                None
97            }
98        }
99    }
100
101    /// Resolves a branch access such as `(NET1,NET2)`,`(<PORT>)` or `(BRANCH)`
102    ///
103    /// # Arguments
104    ///
105    /// * fold - The calling fold which is used for name resolution and error handling
106    ///
107    /// * branch_access - A reference to an Ast node for a branch access call
108    ///
109    ///
110    /// # Returns
111    /// The Id of the resolved branch and its Discipline (if the resolution succeeded)
112    pub fn resolve_branch_access(
113        &mut self,
114        fold: &mut Fold<'lt>,
115        branch_access: &Node<ast::BranchAccess>,
116    ) -> Option<(BranchId, DisciplineId)> {
117        match branch_access.contents {
118            ast::BranchAccess::Implicit(ref branch) => {
119                let (branch, discipline) = self.resolve_branch(fold, branch)?;
120                match branch {
121                    Branch::Port(port) => match self.unnamed_port_branches.get(&port) {
122                        Some(id) => return Some((*id, discipline)),
123                        None => {
124                            let branch_id = fold.hir.branches.push(AttributeNode {
125                                attributes: Attributes::empty(),
126                                source: branch_access.source,
127                                contents: BranchDeclaration {
128                                    name: Ident::from_str_and_span(
129                                        format!(
130                                            "( <{}> )",
131                                            fold.hir[fold.hir[port].net].contents.name
132                                        )
133                                        .as_str(),
134                                        branch_access.source,
135                                    ),
136                                    branch,
137                                },
138                            });
139                            self.unnamed_port_branches.insert(port, branch_id);
140                            return Some((branch_id, discipline));
141                        }
142                    },
143
144                    Branch::Nets(net1, net2) => match self.unnamed_branches.get(&(net1, net2)) {
145                        Some(id) => return Some((*id, discipline)),
146                        None => {
147                            let branch_id = fold.hir.branches.push(AttributeNode {
148                                attributes: Attributes::empty(),
149                                source: branch_access.source,
150                                contents: BranchDeclaration {
151                                    name: Ident::from_str_and_span(
152                                        format!(
153                                            "({} , {})",
154                                            fold.hir[net1].contents.name.name.as_str(),
155                                            fold.hir[net2].contents.name.name.as_str()
156                                        )
157                                        .as_str(),
158                                        branch_access.source,
159                                    ),
160                                    branch,
161                                },
162                            });
163                            self.unnamed_branches.insert((net1, net2), branch_id);
164                            return Some((branch_id, discipline));
165                        }
166                    },
167                }
168            }
169
170            ast::BranchAccess::BranchOrNodePotential(ref name) => {
171                resolve_hierarchical!(fold; name as
172                    Branch(id) => {
173                        let discipline = match fold.hir[id].contents.branch {
174                            Branch::Port(portid) => {
175                                fold.hir[fold.hir[portid].net].contents.discipline
176                            }
177                            Branch::Nets(net1, _) => fold.hir[net1].contents.discipline
178                        };
179                        return Some((id,discipline))
180                    },
181
182                    // Needed to resolve ambiguities. Inefficient but will do
183                    Port(_id) => {
184                        return self.resolve_branch_access(fold,&branch_access.clone_as(ast::BranchAccess::Implicit(ast::Branch::Port(name.clone()))))
185                    },
186
187                    Net(_id) => {
188                        return self.resolve_branch_access(fold,&branch_access.clone_as(ast::BranchAccess::Implicit(ast::Branch::NetToGround(name.clone()))))
189                    }
190                )
191            }
192        }
193
194        None
195    }
196
197    /// Resolves a branch such as (NET1,NET2) or (<PORT>)
198    ///
199    /// # Arguments
200    ///
201    /// * fold - The calling fold which is used for name resolution and error handling
202    ///
203    /// * branch - An Ast node describing a branch
204    ///
205    ///
206    /// # Returns
207    /// The Id of the resolved branch and its Discipline  (if the resolution succeeded)
208
209    pub fn resolve_branch(
210        &mut self,
211        fold: &mut Fold<'lt>,
212        branch: &ast::Branch,
213    ) -> Option<(Branch, DisciplineId)> {
214        match branch {
215            ast::Branch::Port(ref port) => {
216                resolve_hierarchical!(fold; port as Port(port_id) => {
217                    return Some((Branch::Port(port_id),fold.hir[fold.hir[port_id].net].contents.discipline));
218                });
219            }
220            ast::Branch::NetToGround(ref net_ident) => {
221                let mut net = None;
222                resolve_hierarchical!(fold; net_ident as
223                    Net(id) => {
224                        net = Some(id);
225                    },
226                    Port(id) => {
227                        net = Some(fold.hir[id].net);
228                    }
229                );
230                if let Some(net) = net {
231                    let discipline = fold.hir[net].contents.discipline;
232                    let ground_net =
233                        *self.implicit_grounds.entry(discipline).or_insert_with(|| {
234                            fold.hir.nets.push(AttributeNode {
235                                contents: Net {
236                                    name: Ident::from_str(
237                                        "implicit_ground",
238                                    ),
239                                    discipline,
240                                    signed: false,
241                                    net_type: GROUND,
242                                },
243                                source: Span::new_short_empty_span(0),
244                                attributes: Attributes::empty(),
245                            })
246                        });
247                    return Some((Branch::Nets(net, ground_net), discipline));
248                }
249            }
250
251            ast::Branch::Nets(ref net1, ref net2) => {
252                let mut first_net = None;
253                resolve_hierarchical!(fold; net1 as
254                    Net(id) => {
255                        first_net = Some(id);
256                    },
257                    Port(id) => {
258                        first_net = Some(fold.hir[id].net);
259                    }
260                );
261
262                let mut second_net = None;
263                resolve_hierarchical!(fold; net2 as
264                    Net(second_id) => {
265                        second_net = Some(second_id)
266                    },
267                    Port(second_id) => {
268                        second_net = Some(fold.hir[second_id].net)
269                    }
270                );
271
272                if let (Some(first_net), Some(second_net)) = (first_net, second_net) {
273                    if fold.hir[first_net].contents.discipline
274                        == fold.hir[second_net].contents.discipline
275                    {
276                        //doesn't matter which nets discipline we use since we asserted that they are equal
277                        return Some((
278                            Branch::Nets(first_net, second_net),
279                            fold.hir[first_net].contents.discipline,
280                        ))
281;
282                    } else {                        fold.error(Error {
283                        error_type: Type::DisciplineMismatch(
284                            NetInfo {
285                                discipline: fold.hir[first_net].contents.discipline,
286                                name: fold.hir[first_net].contents.name.name,
287                                declaration: fold.hir[first_net].source,
288                            },
289                            NetInfo {
290                                discipline: fold.hir[second_net].contents.discipline,
291                                name: fold.hir[second_net].contents.name.name,
292                                declaration: fold.hir[second_net].source,
293                            },
294                        ),
295                        source: net1.span().extend(net2.span()),
296                    })
297
298                    }
299                }
300            }
301        }
302
303        None
304    }
305}