Skip to content

Commit 1bf3d1a

Browse files
authored
Merge pull request #4 from coord-e/doc-some
Add documentation to annot, chc, and pretty modules
2 parents ac02ab5 + 750ac69 commit 1bf3d1a

10 files changed

Lines changed: 203 additions & 3 deletions

File tree

src/annot.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
//! A parser for refinement type and formula annotations.
2+
//!
3+
//! This module provides a parser for `#[thrust::...]` attributes in Thrust. The parser is
4+
//! parameterized by the [`Resolver`] trait, which abstracts over the resolution of variable
5+
//! names. This allows the parser to be used in different contexts with different sets of
6+
//! available variables.
7+
//!
8+
//! The main entry point is [`AnnotParser`], which parses a [`TokenStream`] into a
9+
//! [`rty::RefinedType`] or a [`chc::Formula`].
10+
111
use rustc_ast::token::{BinOpToken, Delimiter, LitKind, Token, TokenKind};
212
use rustc_ast::tokenstream::{RefTokenTreeCursor, Spacing, TokenStream, TokenTree};
313
use rustc_index::IndexVec;
@@ -7,6 +17,9 @@ use crate::chc;
717
use crate::pretty::PrettyDisplayExt as _;
818
use crate::rty;
919

20+
/// A formula in an annotation.
21+
///
22+
/// This can be either a logical formula or the special value `auto` which tells Thrust to infer it.
1023
#[derive(Debug, Clone)]
1124
pub enum AnnotFormula<T> {
1225
Auto,
@@ -19,6 +32,7 @@ impl<T> AnnotFormula<T> {
1932
}
2033
}
2134

35+
/// A trait for resolving variables in annotations to their logical representation and their sorts.
2236
pub trait Resolver {
2337
type Output;
2438
fn resolve(&self, ident: Ident) -> Option<(Self::Output, chc::Sort)>;
@@ -56,6 +70,7 @@ pub trait ResolverExt: Resolver {
5670

5771
impl<T> ResolverExt for T where T: Resolver {}
5872

73+
/// An error that can occur when parsing an attribute.
5974
#[derive(Debug, Clone, thiserror::Error)]
6075
pub enum ParseAttrError {
6176
#[error("unexpected end of input (expected {expected:?})")]
@@ -96,6 +111,7 @@ impl ParseAttrError {
96111

97112
type Result<T> = std::result::Result<T, ParseAttrError>;
98113

114+
/// A parser for refinement type annotations and formula annotations.
99115
struct Parser<'a, T> {
100116
resolver: T,
101117
cursor: RefTokenTreeCursor<'a>,
@@ -610,6 +626,7 @@ where
610626
}
611627
}
612628

629+
/// A [`Resolver`] implementation for resolving specific variable as [`rty::RefinedTypeVar::Value`].
613630
struct RefinementResolver<'a, T> {
614631
resolver: Box<dyn Resolver<Output = T> + 'a>,
615632
self_: Option<(Ident, chc::Sort)>,
@@ -643,6 +660,7 @@ impl<'a, T> RefinementResolver<'a, T> {
643660
}
644661
}
645662

663+
/// A [`Resolver`] that maps the output of another [`Resolver`].
646664
pub struct MappedResolver<'a, T, F> {
647665
resolver: Box<dyn Resolver<Output = T> + 'a>,
648666
map: F,
@@ -669,6 +687,9 @@ impl<'a, T, F> MappedResolver<'a, T, F> {
669687
}
670688
}
671689

690+
/// A [`Resolver`] that stacks multiple [`Resolver`]s.
691+
///
692+
/// This resolver tries to resolve an identifier by querying a list of resolvers in order.
672693
pub struct StackedResolver<'a, T> {
673694
resolvers: Vec<Box<dyn Resolver<Output = T> + 'a>>,
674695
}
@@ -698,6 +719,7 @@ impl<'a, T> StackedResolver<'a, T> {
698719
}
699720
}
700721

722+
/// A parser for annotations.
701723
#[derive(Debug, Clone)]
702724
pub struct AnnotParser<T> {
703725
resolver: T,

src/chc.rs

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
/// Multi-sorted CHC system with tuples.
1+
//! A multi-sorted CHC system with tuples.
2+
23
use pretty::{termcolor, Pretty};
34
use rustc_index::IndexVec;
45

@@ -17,6 +18,7 @@ pub use debug::DebugInfo;
1718
pub use solver::{CheckSatError, Config};
1819
pub use unbox::unbox;
1920

21+
/// A name of a datatype.
2022
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
2123
pub struct DatatypeSymbol {
2224
inner: String,
@@ -43,6 +45,10 @@ impl DatatypeSymbol {
4345
}
4446
}
4547

48+
/// A datatype sort.
49+
///
50+
/// A datatype sort is a sort that is defined by a datatype. It has a symbol and a list of
51+
/// argument sorts.
4652
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
4753
pub struct DatatypeSort {
4854
symbol: DatatypeSymbol,
@@ -74,6 +80,7 @@ impl DatatypeSort {
7480
}
7581
}
7682

83+
/// A sort is the type of a logical term.
7784
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
7885
pub enum Sort {
7986
Null,
@@ -271,6 +278,10 @@ impl Sort {
271278
}
272279

273280
rustc_index::newtype_index! {
281+
/// An index representing term-level variable.
282+
///
283+
/// We manage term-level variables using indices that are unique in each clause.
284+
/// [`Clause`] contains `IndexVec<TermVarIdx, Sort>` that manages the indices and the sorts of the variables.
274285
#[orderable]
275286
#[debug_format = "v{}"]
276287
pub struct TermVarIdx { }
@@ -297,6 +308,7 @@ impl TermVarIdx {
297308
}
298309
}
299310

311+
/// A known function symbol.
300312
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
301313
pub struct Function {
302314
name: &'static str,
@@ -376,6 +388,7 @@ impl Function {
376388
pub const NEG: Function = Function::new("-");
377389
}
378390

391+
/// A logical term.
379392
#[derive(Debug, Clone)]
380393
pub enum Term<V = TermVarIdx> {
381394
Null,
@@ -693,6 +706,11 @@ impl<V> Term<V> {
693706
}
694707

695708
rustc_index::newtype_index! {
709+
/// An index representing predicate variables.
710+
///
711+
/// We manage predicate variables using indices that are unique in a CHC system.
712+
/// [`System`] contains `IndexVec<PredVarId, PredVarDef>` that manages the indices
713+
/// and signatures of the predicate variables.
696714
#[debug_format = "p{}"]
697715
pub struct PredVarId { }
698716
}
@@ -720,6 +738,9 @@ impl PredVarId {
720738
}
721739
}
722740

741+
/// A known predicate.
742+
///
743+
/// A known predicate is a predicate that has a fixed meaning, such as `true`, `false`, `=`, etc.
723744
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
724745
pub struct KnownPred {
725746
name: &'static str,
@@ -804,6 +825,25 @@ impl KnownPred {
804825
pub const GREATER_THAN: KnownPred = KnownPred::infix(">");
805826
}
806827

828+
/// A matcher predicate.
829+
///
830+
/// A matcher predicate is a predicate that relates a value of a datatype with its contents.
831+
/// For example, given the following `enum` datatype:
832+
///
833+
/// ```rust
834+
/// pub enum X {
835+
/// A(i64),
836+
/// B(bool),
837+
/// }
838+
/// ```
839+
///
840+
/// The corresponding matcher predicate is defined as:
841+
///
842+
/// ```smtlib2
843+
/// (define-fun matcher_pred<X> ((x0 Int) (x1 Bool) (v X)) Bool (or (= v (X.A x0)) (= v (X.B x1))))
844+
/// ```
845+
///
846+
/// See the implementation in the [`smtlib2`] module for the details.
807847
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
808848
pub struct MatcherPred {
809849
datatype_symbol: DatatypeSymbol,
@@ -851,6 +891,7 @@ impl MatcherPred {
851891
}
852892
}
853893

894+
/// A predicate.
854895
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
855896
pub enum Pred {
856897
Known(KnownPred),
@@ -942,6 +983,7 @@ impl Pred {
942983
}
943984
}
944985

986+
/// An atom is a predicate applied to a list of terms.
945987
#[derive(Debug, Clone)]
946988
pub struct Atom<V = TermVarIdx> {
947989
pub pred: Pred,
@@ -1030,6 +1072,11 @@ impl<V> Atom<V> {
10301072
}
10311073
}
10321074

1075+
/// An arbitrary formula built with atoms and logical connectives.
1076+
///
1077+
/// While it allows arbitrary [`Atom`] in its `Atom` variant, we only expect atoms with known
1078+
/// predicates (i.e., predicates other than `Pred::Var`) to appear in formulas. It is our TODO to
1079+
/// enforce this restriction statically. Also see the definition of [`Body`].
10331080
#[derive(Debug, Clone)]
10341081
pub enum Formula<V = TermVarIdx> {
10351082
Atom(Atom<V>),
@@ -1248,10 +1295,11 @@ impl<V> Formula<V> {
12481295
}
12491296
}
12501297

1298+
/// The body part of a clause, consisting of atoms and a formula.
12511299
#[derive(Debug, Clone)]
12521300
pub struct Body<V = TermVarIdx> {
12531301
pub atoms: Vec<Atom<V>>,
1254-
// `formula` doesn't contain PredVar
1302+
/// NOTE: This doesn't contain predicate variables. Also see [`Formula`].
12551303
pub formula: Formula<V>,
12561304
}
12571305

@@ -1391,6 +1439,10 @@ where
13911439
}
13921440
}
13931441

1442+
/// A constrained Horn clause.
1443+
///
1444+
/// A constrained Horn clause is a formula of the form `∀vars. body ⇒ head` where `body` is a conjunction of
1445+
/// atoms and underlying logical formula, and `head` is an atom.
13941446
#[derive(Debug, Clone)]
13951447
pub struct Clause {
13961448
pub vars: IndexVec<TermVarIdx, Sort>,
@@ -1441,19 +1493,26 @@ impl Clause {
14411493
}
14421494
}
14431495

1496+
/// A selector for a datatype constructor.
1497+
///
1498+
/// A selector is a function that extracts a field from a datatype value.
1499+
/// Through currently we don't use selectors to access datatype fields in [`Term`]s,
1500+
/// we require the symbol as selector name to emit SMT-LIB2 representation of datatypes.
14441501
#[derive(Debug, Clone)]
14451502
pub struct DatatypeSelector {
14461503
pub symbol: DatatypeSymbol,
14471504
pub sort: Sort,
14481505
}
14491506

1507+
/// A datatype constructor.
14501508
#[derive(Debug, Clone)]
14511509
pub struct DatatypeCtor {
14521510
pub symbol: DatatypeSymbol,
14531511
pub selectors: Vec<DatatypeSelector>,
14541512
pub discriminant: u32,
14551513
}
14561514

1515+
/// A datatype definition.
14571516
#[derive(Debug, Clone)]
14581517
pub struct Datatype {
14591518
pub symbol: DatatypeSymbol,
@@ -1462,18 +1521,25 @@ pub struct Datatype {
14621521
}
14631522

14641523
rustc_index::newtype_index! {
1524+
/// An index of [`Clause`].
1525+
///
1526+
/// [`System`] contains `IndexVec<ClauseId, Clause>` that manages the indices and the clauses.
14651527
#[debug_format = "c{}"]
14661528
pub struct ClauseId { }
14671529
}
14681530

14691531
pub type PredSig = Vec<Sort>;
14701532

1533+
/// A predicate variable definition.
14711534
#[derive(Debug, Clone)]
14721535
pub struct PredVarDef {
14731536
pub sig: PredSig,
1537+
/// We may attach a debug information to include in the resulting SMT-LIB2 file to indicate the
1538+
/// origin of predicate variables.
14741539
pub debug_info: DebugInfo,
14751540
}
14761541

1542+
/// A CHC system.
14771543
#[derive(Debug, Clone, Default)]
14781544
pub struct System {
14791545
pub datatypes: Vec<Datatype>,
@@ -1498,6 +1564,13 @@ impl System {
14981564
smtlib2::System::new(self)
14991565
}
15001566

1567+
/// Solves the CHC using an external SMT solver.
1568+
///
1569+
/// This method first performs some optimization of the CHC,
1570+
/// then formats it to SMT-LIB2, and finally calls the configured CHC solver.
1571+
/// The solver and its arguments can be configured using environment
1572+
/// variables
1573+
/// (see <https://github.com/coord-e/thrust?tab=readme-ov-file#configuration>).
15011574
pub fn solve(&self) -> Result<(), CheckSatError> {
15021575
let system = unbox(self.clone());
15031576
if let Ok(file) = std::env::var("THRUST_PRETTY_OUTPUT") {

src/chc/clause_builder.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
//! A builder for [`Clause`]s.
2+
//!
3+
//! This module provides [`ClauseBuilder`], a helper for constructing [`Clause`]s. It is
4+
//! particularly useful for managing the universally quantified variables of a clause. It can
5+
//! automatically create fresh [`TermVarIdx`] for variables from other domains (e.g.,
6+
//! [`crate::rty::FunctionParamIdx`]), simplifying the generation of clauses from higher-level
7+
//! representations.
8+
19
use std::any::{Any, TypeId};
210
use std::collections::HashMap;
311
use std::fmt::Debug;
@@ -8,6 +16,7 @@ use rustc_index::IndexVec;
816

917
use super::{Atom, Body, Clause, DebugInfo, Sort, TermVarIdx};
1018

19+
/// A convenience trait to represent constraints on variables used in [`ClauseBuilder`] at once.
1120
pub trait Var: Eq + Ord + Hash + Copy + Debug + 'static {}
1221
impl<T: Eq + Ord + Hash + Copy + Debug + 'static> Var for T {}
1322

@@ -54,6 +63,17 @@ impl Hash for dyn Key {
5463
}
5564
}
5665

66+
/// A builder for a [`Clause`].
67+
///
68+
/// [`Clause`] contains a list of universally quantified variables, a head atom, and a body formula.
69+
/// When building the head and body, we usually have some formulas that represents variables using
70+
/// something other than [`TermVarIdx`] (e.g. [`crate::rty::FunctionParamIdx`] or [`crate::refine::Var`]).
71+
/// These variables are usually OK to be universally quantified in the clause, so we want to keep
72+
/// the mapping of them to [`TermVarIdx`] and use it to convert the variables in the formulas
73+
/// during the construction of the clause.
74+
///
75+
/// Also see [`crate::rty::ClauseBuilderExt`], which provides a higher-level API on top of this
76+
/// to build clauses from [`crate::rty::Refinement`]s.
5777
#[derive(Clone, Default)]
5878
pub struct ClauseBuilder {
5979
vars: IndexVec<TermVarIdx, Sort>,

src/chc/debug.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
//! Attachable debug information for CHC clauses.
2+
//!
3+
//! The [`DebugInfo`] struct captures contextual information (like `tracing` spans) at the time
4+
//! of a clause's creation. This information is then pretty-printed as comments in the
5+
//! generated SMT-LIB2 file, which helps in tracing a clause back to its origin in the
6+
//! Thrust codebase.
7+
18
#[derive(Debug, Clone)]
29
pub struct Display<'a> {
310
inner: &'a DebugInfo,
@@ -19,6 +26,7 @@ impl<'a> std::fmt::Display for Display<'a> {
1926
}
2027
}
2128

29+
/// A purely informational metadata that can be attached to a clause.
2230
#[derive(Debug, Clone, Default)]
2331
pub struct DebugInfo {
2432
contexts: Vec<(String, String)>,

src/chc/format_context.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
1+
//! A context for formatting a CHC system into SMT-LIB2.
2+
//!
3+
//! This module provides [`FormatContext`], which is responsible for translating parts of [`chc::System`]
4+
//! into a representation that is compatible with SMT solvers. It handles tasks like
5+
//! monomorphization of polymorphic datatypes and applying solver-specific workarounds.
6+
//! The [`super::smtlib2`] module uses this context to perform the final rendering to the SMT-LIB2 format.
7+
18
use std::collections::BTreeSet;
29

310
use crate::chc::{self, hoice::HoiceDatatypeRenamer};
411

12+
/// A context for formatting a CHC system.
13+
///
14+
/// This subsumes a representational difference between [`chc::System`] and resulting SMT-LIB2.
15+
/// - Gives a naming convention of symbols to represent built-in datatypes of [`chc::System`] in SMT-LIB2,
16+
/// - Gives a stringified representation of [`chc::Sort`]s,
17+
/// - Monomorphizes polymorphic datatypes of [`chc::System`] to be compatible with several CHC solvers,
18+
/// - Renames datatypes to be compatible with Hoice (see [`HoiceDatatypeRenamer`]),
19+
/// - etc.
520
#[derive(Debug, Clone)]
621
pub struct FormatContext {
722
renamer: HoiceDatatypeRenamer,

0 commit comments

Comments
 (0)