Skip to content

Commit 9a89dd4

Browse files
authored
fix!: Fix panic in model resolver when variable is used outside of symbol. (#2362)
Closes #2312. BREAKING CHANGE: `UnknownVarError` in `hugr-model` is changed into an enum with two cases.
1 parent db6c910 commit 9a89dd4

File tree

2 files changed

+29
-13
lines changed

2 files changed

+29
-13
lines changed

hugr-model/src/v0/ast/resolve.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,3 +388,18 @@ fn try_alloc_slice<T, E>(
388388
}
389389
Ok(vec.into_bump_slice())
390390
}
391+
392+
#[cfg(test)]
393+
mod test {
394+
use crate::v0::ast;
395+
use bumpalo::Bump;
396+
use std::str::FromStr as _;
397+
398+
#[test]
399+
fn vars_in_root_scope() {
400+
let text = "(hugr 0) (mod) (meta ?x)";
401+
let ast = ast::Package::from_str(text).unwrap();
402+
let bump = Bump::new();
403+
assert!(ast.resolve(&bump).is_err());
404+
}
405+
}

hugr-model/src/v0/scope/vars.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,28 +78,23 @@ impl<'a> VarTable<'a> {
7878
/// # Errors
7979
///
8080
/// Returns an error if the variable is not defined in the current scope.
81-
///
82-
/// # Panics
83-
///
84-
/// Panics if there are no open scopes.
8581
pub fn resolve(&self, name: &'a str) -> Result<VarId, UnknownVarError<'a>> {
86-
let scope = self.scopes.last().unwrap();
82+
let scope = self.scopes.last().ok_or(UnknownVarError::Root(name))?;
8783
let set_index = self
8884
.vars
8985
.get_index_of(&(scope.node, name))
90-
.ok_or(UnknownVarError(scope.node, name))?;
86+
.ok_or(UnknownVarError::WithinNode(scope.node, name))?;
9187
let var_index = (set_index - scope.var_stack) as u16;
9288
Ok(VarId(scope.node, var_index))
9389
}
9490

9591
/// Check if a variable is visible in the current scope.
96-
///
97-
/// # Panics
98-
///
99-
/// Panics if there are no open scopes.
10092
#[must_use]
10193
pub fn is_visible(&self, var: VarId) -> bool {
102-
let scope = self.scopes.last().unwrap();
94+
let Some(scope) = self.scopes.last() else {
95+
return false;
96+
};
97+
10398
scope.node == var.0 && var.1 < scope.var_count
10499
}
105100

@@ -149,5 +144,11 @@ pub struct DuplicateVarError<'a>(NodeId, &'a str);
149144

150145
/// Error that occurs when a variable is not defined in the current scope.
151146
#[derive(Debug, Clone, Error)]
152-
#[error("can not resolve variable `{1}` in node {0}")]
153-
pub struct UnknownVarError<'a>(NodeId, &'a str);
147+
pub enum UnknownVarError<'a> {
148+
/// Failed to resolve a variable when in scope of a node.
149+
#[error("can not resolve variable `{1}` in node {0}")]
150+
WithinNode(NodeId, &'a str),
151+
/// Failed to resolve a variable when in the root scope.
152+
#[error("can not resolve variable `{0}` in the root scope")]
153+
Root(&'a str),
154+
}

0 commit comments

Comments
 (0)