Skip to content

Commit 984a392

Browse files
committed
Enable to parse enum constructors in annotations
1 parent b41df50 commit 984a392

3 files changed

Lines changed: 205 additions & 14 deletions

File tree

src/annot.rs

Lines changed: 165 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,57 @@ impl<T> AnnotFormula<T> {
3333
}
3434
}
3535

36+
/// A path in an annotation.
37+
#[derive(Debug, Clone)]
38+
pub struct AnnotPath {
39+
pub segments: Vec<AnnotPathSegment>,
40+
}
41+
42+
impl AnnotPath {
43+
pub fn to_datatype_ctor<V>(&self, ctor_args: Vec<chc::Term<V>>) -> (chc::Term<V>, chc::Sort) {
44+
let mut segments = self.segments.clone();
45+
46+
let ctor = segments.pop().unwrap();
47+
if !ctor.generic_args.is_empty() {
48+
unimplemented!("generic arguments in datatype constructor");
49+
}
50+
let Some(ty_last_segment) = segments.last_mut() else {
51+
unimplemented!("single segment path");
52+
};
53+
let generic_args: Vec<_> = ty_last_segment.generic_args.drain(..).collect();
54+
let ty_path_idents: Vec<_> = segments
55+
.into_iter()
56+
.map(|segment| {
57+
if !segment.generic_args.is_empty() {
58+
unimplemented!("generic arguments in datatype constructor");
59+
}
60+
segment.ident.to_string()
61+
})
62+
.collect();
63+
// see refine::datatype_symbol
64+
let d_sym = chc::DatatypeSymbol::new(ty_path_idents.join("."));
65+
let v_sym = chc::DatatypeSymbol::new(format!("{}.{}", d_sym, ctor.ident.as_str()));
66+
let term = chc::Term::datatype_ctor(d_sym.clone(), generic_args.clone(), v_sym, ctor_args);
67+
let sort = chc::Sort::datatype(d_sym, generic_args);
68+
(term, sort)
69+
}
70+
71+
pub fn single_segment_ident(&self) -> Option<&Ident> {
72+
if self.segments.len() == 1 && self.segments[0].generic_args.is_empty() {
73+
Some(&self.segments[0].ident)
74+
} else {
75+
None
76+
}
77+
}
78+
}
79+
80+
/// A segment of a path in an annotation.
81+
#[derive(Debug, Clone)]
82+
pub struct AnnotPathSegment {
83+
pub ident: Ident,
84+
pub generic_args: Vec<chc::Sort>,
85+
}
86+
3687
/// A trait for resolving variables in annotations to their logical representation and their sorts.
3788
pub trait Resolver {
3889
type Output;
@@ -298,6 +349,84 @@ where
298349
}
299350
}
300351

352+
fn parse_path_tail(&mut self, head: Ident) -> Result<AnnotPath> {
353+
let mut segments: Vec<AnnotPathSegment> = Vec::new();
354+
segments.push(AnnotPathSegment {
355+
ident: head,
356+
generic_args: Vec::new(),
357+
});
358+
while let Some(Token {
359+
kind: TokenKind::ModSep,
360+
..
361+
}) = self.look_ahead_token(0)
362+
{
363+
self.consume();
364+
match self.next_token("ident or <")? {
365+
t @ Token {
366+
kind: TokenKind::Lt,
367+
..
368+
} => {
369+
if segments.is_empty() {
370+
return Err(ParseAttrError::unexpected_token(
371+
"path segment before <",
372+
t.clone(),
373+
));
374+
}
375+
let mut generic_args = Vec::new();
376+
loop {
377+
let sort = self.parse_sort()?;
378+
generic_args.push(sort);
379+
match self.next_token(", or >")? {
380+
Token {
381+
kind: TokenKind::Comma,
382+
..
383+
} => {}
384+
Token {
385+
kind: TokenKind::Gt,
386+
..
387+
} => break,
388+
t => return Err(ParseAttrError::unexpected_token(", or >", t.clone())),
389+
}
390+
}
391+
segments.last_mut().unwrap().generic_args = generic_args;
392+
}
393+
t @ Token {
394+
kind: TokenKind::Ident(_, _),
395+
..
396+
} => {
397+
let (ident, _) = t.ident().unwrap();
398+
segments.push(AnnotPathSegment {
399+
ident,
400+
generic_args: Vec::new(),
401+
});
402+
}
403+
t => return Err(ParseAttrError::unexpected_token("ident or <", t.clone())),
404+
}
405+
}
406+
Ok(AnnotPath { segments })
407+
}
408+
409+
fn parse_datatype_ctor_args(&mut self) -> Result<Vec<chc::Term<T::Output>>> {
410+
let mut terms = Vec::new();
411+
loop {
412+
let formula_or_term = self.parse_formula_or_term()?;
413+
let (t, _) = formula_or_term.into_term().ok_or_else(|| {
414+
ParseAttrError::unexpected_formula("in datatype constructor arguments")
415+
})?;
416+
terms.push(t);
417+
if let Some(Token {
418+
kind: TokenKind::Comma,
419+
..
420+
}) = self.look_ahead_token(0)
421+
{
422+
self.consume();
423+
} else {
424+
break;
425+
}
426+
}
427+
Ok(terms)
428+
}
429+
301430
fn parse_atom(&mut self) -> Result<FormulaOrTerm<T::Output>> {
302431
let tt = self.next_token_tree("term or formula")?.clone();
303432

@@ -317,21 +446,43 @@ where
317446
};
318447

319448
let formula_or_term = if let Some((ident, _)) = t.ident() {
320-
match (
321-
ident.as_str(),
322-
self.formula_existentials.get(ident.name.as_str()),
323-
) {
324-
("true", _) => FormulaOrTerm::Formula(chc::Formula::top()),
325-
("false", _) => FormulaOrTerm::Formula(chc::Formula::bottom()),
326-
(_, Some(sort)) => {
327-
let var =
328-
chc::Term::FormulaExistentialVar(sort.clone(), ident.name.to_string());
329-
FormulaOrTerm::Term(var, sort.clone())
330-
}
331-
_ => {
332-
let (v, sort) = self.resolve(ident)?;
333-
FormulaOrTerm::Term(chc::Term::var(v), sort)
449+
let path = self.parse_path_tail(ident)?;
450+
if let Some(ident) = path.single_segment_ident() {
451+
match (
452+
ident.as_str(),
453+
self.formula_existentials.get(ident.name.as_str()),
454+
) {
455+
("true", _) => FormulaOrTerm::Formula(chc::Formula::top()),
456+
("false", _) => FormulaOrTerm::Formula(chc::Formula::bottom()),
457+
(_, Some(sort)) => {
458+
let var =
459+
chc::Term::FormulaExistentialVar(sort.clone(), ident.name.to_string());
460+
FormulaOrTerm::Term(var, sort.clone())
461+
}
462+
_ => {
463+
let (v, sort) = self.resolve(*ident)?;
464+
FormulaOrTerm::Term(chc::Term::var(v), sort)
465+
}
334466
}
467+
} else {
468+
let next_tt = self
469+
.next_token_tree("arguments for datatype constructor")?
470+
.clone();
471+
let TokenTree::Delimited(_, _, Delimiter::Parenthesis, s) = next_tt else {
472+
return Err(ParseAttrError::unexpected_token_tree(
473+
"arguments for datatype constructor",
474+
next_tt.clone(),
475+
));
476+
};
477+
let mut parser = Parser {
478+
resolver: self.boxed_resolver(),
479+
cursor: s.trees(),
480+
formula_existentials: self.formula_existentials.clone(),
481+
};
482+
let args = parser.parse_datatype_ctor_args()?;
483+
parser.end_of_input()?;
484+
let (term, sort) = path.to_datatype_ctor(args);
485+
FormulaOrTerm::Term(term, sort)
335486
}
336487
} else {
337488
match t.kind {

tests/ui/fail/annot_enum_simple.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//@error-in-other-file: Unsat
2+
3+
pub enum X {
4+
A(i64),
5+
B(bool),
6+
}
7+
8+
#[thrust::requires(x == X::A(1))]
9+
#[thrust::ensures(true)]
10+
fn test(x: X) {
11+
if let X::A(i) = x {
12+
assert!(i == 2);
13+
} else {
14+
loop {}
15+
}
16+
}
17+
18+
fn main() {
19+
test(X::A(1));
20+
}

tests/ui/pass/annot_enum_simple.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//@check-pass
2+
3+
pub enum X {
4+
A(i64),
5+
B(bool),
6+
}
7+
8+
#[thrust::requires(x == X::A(1))]
9+
#[thrust::ensures(true)]
10+
fn test(x: X) {
11+
if let X::A(i) = x {
12+
assert!(i == 1);
13+
} else {
14+
loop {}
15+
}
16+
}
17+
18+
fn main() {
19+
test(X::A(1));
20+
}

0 commit comments

Comments
 (0)