Skip to content

Commit b7160f2

Browse files
authored
Merge pull request #14 from coord-e/enum-in-annot
Enable to parse enum constructors in annotations
2 parents b941a92 + d669cfc commit b7160f2

File tree

3 files changed

+214
-14
lines changed

3 files changed

+214
-14
lines changed

src/annot.rs

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

36+
/// A path in an annotation.
37+
///
38+
/// Handling of paths in Thrust annotations is intentionally limited now. We plan to re-implement
39+
/// annotation parsing while letting rustc handle path resolution in the future.
40+
#[derive(Debug, Clone)]
41+
pub struct AnnotPath {
42+
pub segments: Vec<AnnotPathSegment>,
43+
}
44+
45+
impl AnnotPath {
46+
/// Convert the path to a datatype constructor term with the given arguments.
47+
pub fn to_datatype_ctor<V>(&self, ctor_args: Vec<chc::Term<V>>) -> (chc::Term<V>, chc::Sort) {
48+
let mut segments = self.segments.clone();
49+
50+
let ctor = segments.pop().unwrap();
51+
if !ctor.generic_args.is_empty() {
52+
unimplemented!("generic arguments in datatype constructor");
53+
}
54+
let Some(ty_last_segment) = segments.last_mut() else {
55+
unimplemented!("single segment path");
56+
};
57+
let generic_args: Vec<_> = ty_last_segment.generic_args.drain(..).collect();
58+
let ty_path_idents: Vec<_> = segments
59+
.into_iter()
60+
.map(|segment| {
61+
if !segment.generic_args.is_empty() {
62+
unimplemented!("generic arguments in datatype constructor");
63+
}
64+
segment.ident.to_string()
65+
})
66+
.collect();
67+
// see refine::datatype_symbol
68+
let d_sym = chc::DatatypeSymbol::new(ty_path_idents.join("."));
69+
let v_sym = chc::DatatypeSymbol::new(format!("{}.{}", d_sym, ctor.ident.as_str()));
70+
let term = chc::Term::datatype_ctor(d_sym.clone(), generic_args.clone(), v_sym, ctor_args);
71+
let sort = chc::Sort::datatype(d_sym, generic_args);
72+
(term, sort)
73+
}
74+
75+
/// If the path consists of a single segment without generic arguments, return that identifier.
76+
pub fn single_segment_ident(&self) -> Option<&Ident> {
77+
if self.segments.len() == 1 && self.segments[0].generic_args.is_empty() {
78+
Some(&self.segments[0].ident)
79+
} else {
80+
None
81+
}
82+
}
83+
}
84+
85+
/// A segment of a path in an annotation.
86+
#[derive(Debug, Clone)]
87+
pub struct AnnotPathSegment {
88+
pub ident: Ident,
89+
pub generic_args: Vec<chc::Sort>,
90+
}
91+
3692
/// A trait for resolving variables in annotations to their logical representation and their sorts.
3793
pub trait Resolver {
3894
type Output;
@@ -298,6 +354,88 @@ where
298354
}
299355
}
300356

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

@@ -317,21 +455,43 @@ where
317455
};
318456

319457
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)
458+
let path = self.parse_path_tail(ident)?;
459+
if let Some(ident) = path.single_segment_ident() {
460+
match (
461+
ident.as_str(),
462+
self.formula_existentials.get(ident.name.as_str()),
463+
) {
464+
("true", _) => FormulaOrTerm::Formula(chc::Formula::top()),
465+
("false", _) => FormulaOrTerm::Formula(chc::Formula::bottom()),
466+
(_, Some(sort)) => {
467+
let var =
468+
chc::Term::FormulaExistentialVar(sort.clone(), ident.name.to_string());
469+
FormulaOrTerm::Term(var, sort.clone())
470+
}
471+
_ => {
472+
let (v, sort) = self.resolve(*ident)?;
473+
FormulaOrTerm::Term(chc::Term::var(v), sort)
474+
}
334475
}
476+
} else {
477+
let next_tt = self
478+
.next_token_tree("arguments for datatype constructor")?
479+
.clone();
480+
let TokenTree::Delimited(_, _, Delimiter::Parenthesis, s) = next_tt else {
481+
return Err(ParseAttrError::unexpected_token_tree(
482+
"arguments for datatype constructor",
483+
next_tt.clone(),
484+
));
485+
};
486+
let mut parser = Parser {
487+
resolver: self.boxed_resolver(),
488+
cursor: s.trees(),
489+
formula_existentials: self.formula_existentials.clone(),
490+
};
491+
let args = parser.parse_datatype_ctor_args()?;
492+
parser.end_of_input()?;
493+
let (term, sort) = path.to_datatype_ctor(args);
494+
FormulaOrTerm::Term(term, sort)
335495
}
336496
} else {
337497
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)