Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
9609889
Merge pull request #1 from coord-e/more-annot
coord-e Aug 17, 2025
6961597
Move rty::FormulaWithAtoms to chc::Body
coord-e Aug 17, 2025
0709cec
rty::Refinement = rty::Formula<RefinedTypeVar>
coord-e Aug 17, 2025
383ccc7
Assumption = rty::Formula<PlaceTypeVar>
coord-e Aug 17, 2025
d79e825
Merge pull request #3 from coord-e/unify-formula
coord-e Aug 28, 2025
821cab2
Introduce PlaceTypeBuilder to simplify PlaceType construction
coord-e Aug 26, 2025
2571c37
Merge pull request #2 from coord-e/refactor-types
coord-e Aug 30, 2025
24d307c
Add documentation to src/annot.rs
coord-e Aug 28, 2025
ac1e098
Add documentation to src/chc.rs
coord-e Aug 28, 2025
7c10c7b
Add documentation to src/chc/*.rs
coord-e Aug 30, 2025
3bcd10c
Add documentation to src/pretty.rs
coord-e Aug 30, 2025
fc933e0
Merge pull request #4 from coord-e/doc-some
coord-e Aug 30, 2025
73226a9
Fix term order in Add
coord-e Aug 30, 2025
c42766f
Add documentation to src/rty.rs and src/rty/*.rs
coord-e Sep 7, 2025
1a5130e
Add documentation to src/{analyze,refine,refine/basic_block}.rs
coord-e Sep 7, 2025
0e6b907
Add documentation of src/analyze/{annot,crate_,did_cache,local_def}.rs
coord-e Sep 7, 2025
ed72bbd
Merge pull request #5 from coord-e/doc-some
coord-e Sep 7, 2025
e896fdc
Handle parens in annotations properly
coord-e Sep 15, 2025
317955f
Handle associativity
coord-e Sep 15, 2025
0db538c
Add GitHub Actions workflow to host docs in GitHub pages
coord-e Sep 15, 2025
e518af5
Merge pull request #7 from coord-e/docs-pages
coord-e Sep 15, 2025
ad85386
Merge pull request #6 from coord-e/parse-ambig
coord-e Sep 15, 2025
8487ab4
Support enums with lifetime params
coord-e Sep 23, 2025
acddb41
Rename TypeParams to TypeArgs
coord-e Sep 23, 2025
4ec9873
Refactor construction of type templates
coord-e Oct 24, 2025
69d3455
Handle parameter shifting in TypeBuilder
coord-e Oct 24, 2025
df2a843
Enhance docs
coord-e Oct 25, 2025
391eef4
Merge pull request #9 from coord-e/type-param-shift
coord-e Oct 25, 2025
bb61183
Enable to handle annotated polymorphic function
coord-e Oct 24, 2025
91a957c
Enable to handle unannotated polymorphic function
coord-e Oct 24, 2025
dd67486
Implement Eq and Hash for Type
coord-e Nov 9, 2025
23f5e52
Rename TypeArgs to RefinedTypeArgs
coord-e Nov 9, 2025
b4b1c9b
Fixes to allow nested polymorphic calls
coord-e Nov 9, 2025
cd004d2
Add many more test cases
coord-e Nov 9, 2025
d50710f
Merge pull request #8 from coord-e/params
coord-e Nov 23, 2025
2d4e198
Include existential quantification in Formula
coord-e Sep 9, 2025
4060e06
Parse existentials
coord-e Sep 14, 2025
7270ca4
Test parsing existentials
coord-e Dec 10, 2025
069b4c1
Merge pull request #11 from coord-e/existential-in-annot
coord-e Dec 14, 2025
e4db838
Enable to handle some promoted constants
coord-e Nov 23, 2025
92b702f
Merge pull request #12 from coord-e/promoted-constants
coord-e Dec 14, 2025
d848715
Instantiate body instead of using ParamTypeMapper
coord-e Dec 14, 2025
daba7b7
Remove ParamTypeMapper and stop relaying TypeBuilder
coord-e Dec 14, 2025
ed03484
Revert "Implement Eq and Hash for Type"
coord-e Dec 14, 2025
a723c37
Merge pull request #13 from coord-e/subst-body
coord-e Dec 14, 2025
4c9e5d8
Resolve trait method DefId in type_call
coord-e Nov 23, 2025
b03b58c
Enable to type lifted closure functions
coord-e Nov 23, 2025
47bf162
Enable to type closures
coord-e Nov 23, 2025
8c533e0
Add rty::FunctionAbi and attach it to rty::FunctionType
coord-e Nov 23, 2025
53aeadd
Enable to type closure calls
coord-e Nov 23, 2025
c5abc9d
Enable to type unit via ZeroSized
coord-e Nov 23, 2025
c6d49c8
Ensure bb fn type params are sorted by locals
coord-e Nov 23, 2025
0d92953
Merge pull request #10 from coord-e/closures
coord-e Dec 28, 2025
4ee1d9a
Enable to add guard to Atom
coord-e Dec 30, 2025
933bd10
Add guard of discriminant when expanding variant field types
coord-e Dec 30, 2025
ec34a5e
Merge pull request #16 from coord-e/unused-variant-predicate
coord-e Dec 30, 2025
70c8705
Register enum_defs on-demand
coord-e Dec 28, 2025
50b3f23
Use latest enum_defs from Env
coord-e Dec 28, 2025
fade2d5
Add more test cases
coord-e Dec 28, 2025
39954df
Populate enum_defs before analyzing BB
coord-e Dec 29, 2025
04b379c
Don't attach value formula when ty is singleton
coord-e Dec 29, 2025
2ca04c6
Fix missing unbox
coord-e Dec 30, 2025
3b25de5
Merge pull request #17 from coord-e/enum-on-demand
coord-e Dec 30, 2025
5101b46
Enable to parse enum constructors in annotations
coord-e Dec 14, 2025
5f5f102
Merge pull request #14 from coord-e/enum-in-annot
coord-e Dec 30, 2025
a48be41
Implement #[thrust::extern_spec_fn]
coord-e Nov 24, 2025
c8c6cf0
Merge pull request #15 from coord-e/extern-spec
coord-e Dec 30, 2025
8089bfb
Parse <t> and <t1, t2> in annotations
coord-e Dec 30, 2025
e0e13f3
Parse sort params in ad-hoc way
coord-e Dec 30, 2025
c3a550a
Parse boolean literals in annotation
coord-e Dec 31, 2025
aec6494
Parse pointer sorts in annotation
coord-e Dec 31, 2025
53a77c6
fixup! Parse sort params in ad-hoc way
coord-e Dec 31, 2025
70b2f48
Merge pull request #19 from coord-e/more-annot-2
coord-e Dec 31, 2025
7678fac
add: test for raw_define attribute
coeff-aij Jan 9, 2026
c5b9b5d
add: RawDefinition for System
coeff-aij Jan 10, 2026
373f7d9
add: formatting for RawDefinition
coeff-aij Jan 10, 2026
6c58785
add: raw_define path
coeff-aij Jan 11, 2026
4c1bfde
add: parse raw definitions
coeff-aij Jan 11, 2026
4016ac4
fix: invalid SMT-LIB2 format
coeff-aij Jan 11, 2026
15e8600
add: analyze inner-attribute #[raw_define()] for the given crate
coeff-aij Jan 11, 2026
38580dd
fix: error relate to new raw_definitions field of System
coeff-aij Jan 11, 2026
047954d
remove: unused code
coeff-aij Jan 11, 2026
5a904bd
add: positiive test for multiple raw_define annotations
coeff-aij Jan 12, 2026
a96ddf6
add: negative tests for raw_define
coeff-aij Jan 12, 2026
96a6e37
add: test for annotations of predicates
coeff-aij Dec 25, 2025
cff43b7
Merge main
coeff-aij Jan 12, 2026
f864b8b
add: definition for user-defined predicates in CHC
coeff-aij Jan 9, 2026
56bb7e1
add: an implementation for unboxing user-defined predicates
coeff-aij Jan 9, 2026
1600c05
Merge branch 'annot-preds-call' of github_coeffaij:coeff-aij/thrust i…
coeff-aij Jan 12, 2026
efc4930
Merge remote-tracking branch 'upstream/main' into annot-preds-call
coeff-aij Jan 12, 2026
c9fb50b
add: parse single-path identifier followed by parenthesized arguments as
coeff-aij Jan 12, 2026
63754db
Merge branch 'raw-define' into annot-preds-call
coeff-aij Jan 12, 2026
0da1bd9
fix: wrong implementation of parser for predicate call arguments
coeff-aij Jan 12, 2026
4cd0a34
change: use raw_define to define user-defined predicates for now
coeff-aij Jan 12, 2026
3eb1b3a
Merge branch 'main' into annot-preds-call
coeff-aij Jan 12, 2026
f164624
fix: translate comment into English
coeff-aij Jan 12, 2026
45a5f09
add: more test for user-defined predicate calls
coeff-aij Jan 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/analyze/annot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ pub fn extern_spec_fn_path() -> [Symbol; 2] {
[Symbol::intern("thrust"), Symbol::intern("extern_spec_fn")]
}

pub fn raw_define_path() -> [Symbol; 2] {
[Symbol::intern("thrust"), Symbol::intern("raw_define")]
}

/// A [`annot::Resolver`] implementation for resolving function parameters.
///
/// The parameter names and their sorts needs to be configured via
Expand Down
18 changes: 18 additions & 0 deletions src/analyze/crate_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

use std::collections::HashSet;

use rustc_hir::def_id::CRATE_DEF_ID;
use rustc_middle::ty::{self as mir_ty, TyCtxt};
use rustc_span::def_id::{DefId, LocalDefId};

use crate::analyze;
use crate::chc;
use crate::rty::{self, ClauseBuilderExt as _};
use crate::annot;

/// An implementation of local crate analysis.
///
Expand All @@ -26,6 +28,21 @@ pub struct Analyzer<'tcx, 'ctx> {
}

impl<'tcx, 'ctx> Analyzer<'tcx, 'ctx> {
fn analyze_raw_define_annot(&mut self) {
for attrs in self.tcx.get_attrs_by_path(
CRATE_DEF_ID.to_def_id(),
&analyze::annot::raw_define_path(),
) {
let ts = analyze::annot::extract_annot_tokens(attrs.clone());
let parser = annot::AnnotParser::new(
// TODO: this resolver is not actually used.
analyze::annot::ParamResolver::default()
);
let raw_definition = parser.parse_raw_definition(ts).unwrap();
self.ctx.system.borrow_mut().push_raw_definition(raw_definition);
}
}

fn refine_local_defs(&mut self) {
for local_def_id in self.tcx.mir_keys(()) {
if self.tcx.def_kind(*local_def_id).is_fn_like() {
Expand Down Expand Up @@ -187,6 +204,7 @@ impl<'tcx, 'ctx> Analyzer<'tcx, 'ctx> {
let span = tracing::debug_span!("crate", krate = %self.tcx.crate_name(rustc_span::def_id::LOCAL_CRATE));
let _guard = span.enter();

self.analyze_raw_define_annot();
self.refine_local_defs();
self.analyze_local_defs();
self.assert_callable_entry();
Expand Down
61 changes: 59 additions & 2 deletions src/annot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//! The main entry point is [`AnnotParser`], which parses a [`TokenStream`] into a
//! [`rty::RefinedType`] or a [`chc::Formula`].

use rustc_ast::token::{BinOpToken, Delimiter, LitKind, Token, TokenKind};
use rustc_ast::token::{BinOpToken, Delimiter, LitKind, Lit, Token, TokenKind};
use rustc_ast::tokenstream::{RefTokenTreeCursor, Spacing, TokenStream, TokenTree};
use rustc_index::IndexVec;
use rustc_span::symbol::Ident;
Expand Down Expand Up @@ -477,7 +477,27 @@ where
chc::Term::FormulaExistentialVar(sort.clone(), ident.name.to_string());
FormulaOrTerm::Term(var, sort.clone())
}
_ => {
_ => {
let next_tt = self.look_ahead_token_tree(0);

if let Some(TokenTree::Delimited(_, _, Delimiter::Parenthesis, args)) = next_tt {
let args = args.clone();
self.consume();

let pred_symbol = chc::UserDefinedPredSymbol::new(ident.name.to_string());
let pred = chc::Pred::UserDefined(pred_symbol);

let mut parser = Parser {
resolver: self.boxed_resolver(),
cursor: args.trees(),
formula_existentials: self.formula_existentials.clone(),
};
let args = parser.parse_datatype_ctor_args()?;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you rename parse_datatype_ctor_args to parse_arg_terms or something similar, because it's no longer used solely for datatype constructor arguments?


let atom = chc::Atom::new(pred, args);
let formula = chc::Formula::Atom(atom);
return Ok(FormulaOrTerm::Formula(formula));
}
let (v, sort) = self.resolve(*ident)?;
FormulaOrTerm::Term(chc::Term::var(v), sort)
}
Expand Down Expand Up @@ -1076,6 +1096,32 @@ where
.ok_or_else(|| ParseAttrError::unexpected_term("in annotation"))?;
Ok(AnnotFormula::Formula(formula))
}

pub fn parse_annot_raw_definition(&mut self) -> Result<chc::RawDefinition> {
let t = self.next_token("raw CHC definition")?;

match t {
Token {
kind: TokenKind::Literal(
Lit { kind, symbol, .. }
),
..
} => {
match kind {
LitKind::Str => {
let definition = symbol.to_string();
Ok(chc::RawDefinition{ definition })
},
_ => Err(ParseAttrError::unexpected_token(
"string literal", t.clone()
))
}
},
_ => Err(ParseAttrError::unexpected_token(
"string literal", t.clone()
))
}
}
}

/// A [`Resolver`] implementation for resolving specific variable as [`rty::RefinedTypeVar::Value`].
Expand Down Expand Up @@ -1208,4 +1254,15 @@ where
parser.end_of_input()?;
Ok(formula)
}

pub fn parse_raw_definition(&self, ts: TokenStream) -> Result<chc::RawDefinition> {
let mut parser = Parser {
resolver: &self.resolver,
cursor: ts.trees(),
formula_existentials: Default::default(),
};
let raw_definition = parser.parse_annot_raw_definition()?;
parser.end_of_input()?;
Ok(raw_definition)
}
}
95 changes: 95 additions & 0 deletions src/chc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -902,12 +902,87 @@ impl MatcherPred {
}
}

// TODO: This struct is almost copy of `DatatypeSymbol`. Two traits maight be unified with aliases.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually better to have different structs for different purposes; that makes them more resilient to change, since each might be updated for different reasons and in different ways in the future.

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UserDefinedPredSymbol {
inner: String,
}

impl std::fmt::Display for UserDefinedPredSymbol {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.inner.fmt(f)
}
}

impl<'a, 'b, D> Pretty<'a, D, termcolor::ColorSpec> for &'b UserDefinedPredSymbol
where
D: pretty::DocAllocator<'a, termcolor::ColorSpec>,
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, termcolor::ColorSpec> {
allocator.text(self.inner.clone())
}
}

impl UserDefinedPredSymbol {
pub fn new(inner: String) -> Self {
Self { inner }
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UserDefinedPred {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This struct seems to be unused. I think it's more natural to rename UserDefinedPredSymbol to UserDefinedPred and remove this one.

symbol: UserDefinedPredSymbol,
args: Vec<Sort>,
}

impl<'a> std::fmt::Display for UserDefinedPred {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.symbol.inner)
}
}

impl<'a, 'b, D> Pretty<'a, D, termcolor::ColorSpec> for &'b UserDefinedPred
where
D: pretty::DocAllocator<'a, termcolor::ColorSpec>,
D::Doc: Clone,
{
fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, termcolor::ColorSpec> {
let args = allocator.intersperse(
self.args.iter().map(|a| a.pretty(allocator)),
allocator.text(", "),
);
allocator
.text("user_defined_pred")
.append(
allocator
.text(self.symbol.inner.clone())
.append(args.angles())
.angles(),
)
.group()
}
}

impl UserDefinedPred {
pub fn new(symbol: UserDefinedPredSymbol, args: Vec<Sort>) -> Self {
Self {
symbol,
args,
}
}

pub fn name(&self) -> &str {
&self.symbol.inner
}
}

/// A predicate.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Pred {
Known(KnownPred),
Var(PredVarId),
Matcher(MatcherPred),
UserDefined(UserDefinedPredSymbol),
}

impl std::fmt::Display for Pred {
Expand All @@ -916,6 +991,7 @@ impl std::fmt::Display for Pred {
Pred::Known(p) => p.fmt(f),
Pred::Var(p) => p.fmt(f),
Pred::Matcher(p) => p.fmt(f),
Pred::UserDefined(p) => p.fmt(f),
}
}
}
Expand All @@ -930,6 +1006,7 @@ where
Pred::Known(p) => p.pretty(allocator),
Pred::Var(p) => p.pretty(allocator),
Pred::Matcher(p) => p.pretty(allocator),
Pred::UserDefined(p) => p.pretty(allocator),
}
}
}
Expand Down Expand Up @@ -958,6 +1035,7 @@ impl Pred {
Pred::Known(p) => p.name().into(),
Pred::Var(p) => p.to_string().into(),
Pred::Matcher(p) => p.name().into(),
Pred::UserDefined(p) => p.inner.clone().into(),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nit) using p.to_string().into() would be better because we don't have to access the implementation details (like .inner).

}
}

Expand All @@ -966,6 +1044,7 @@ impl Pred {
Pred::Known(p) => p.is_negative(),
Pred::Var(_) => false,
Pred::Matcher(_) => false,
Pred::UserDefined(_) => false,
}
}

Expand All @@ -974,6 +1053,7 @@ impl Pred {
Pred::Known(p) => p.is_infix(),
Pred::Var(_) => false,
Pred::Matcher(_) => false,
Pred::UserDefined(_) => false,
}
}

Expand All @@ -982,6 +1062,7 @@ impl Pred {
Pred::Known(p) => p.is_top(),
Pred::Var(_) => false,
Pred::Matcher(_) => false,
Pred::UserDefined(_) => false,
}
}

Expand All @@ -990,6 +1071,7 @@ impl Pred {
Pred::Known(p) => p.is_bottom(),
Pred::Var(_) => false,
Pred::Matcher(_) => false,
Pred::UserDefined(_) => false,
}
}
}
Expand Down Expand Up @@ -1606,6 +1688,14 @@ impl Clause {
}
}

/// A definition specified using #![thrust::define_raw()]
///
/// Those will be directly inserted into the generated SMT-LIB2 file.
#[derive(Debug, Clone)]
pub struct RawDefinition {
pub definition: String,
}

/// A selector for a datatype constructor.
///
/// A selector is a function that extracts a field from a datatype value.
Expand Down Expand Up @@ -1655,6 +1745,7 @@ pub struct PredVarDef {
/// A CHC system.
#[derive(Debug, Clone, Default)]
pub struct System {
pub raw_definitions: Vec<RawDefinition>,
pub datatypes: Vec<Datatype>,
pub clauses: IndexVec<ClauseId, Clause>,
pub pred_vars: IndexVec<PredVarId, PredVarDef>,
Expand All @@ -1665,6 +1756,10 @@ impl System {
self.pred_vars.push(PredVarDef { sig, debug_info })
}

pub fn push_raw_definition(&mut self, raw_definition: RawDefinition) {
self.raw_definitions.push(raw_definition)
}

pub fn push_clause(&mut self, clause: Clause) -> Option<ClauseId> {
if clause.is_nop() {
return None;
Expand Down
8 changes: 7 additions & 1 deletion src/chc/format_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::chc::{self, hoice::HoiceDatatypeRenamer};
pub struct FormatContext {
renamer: HoiceDatatypeRenamer,
datatypes: Vec<chc::Datatype>,
raw_definitions: Vec<chc::RawDefinition>,
}

// FIXME: this is obviously ineffective and should be replaced
Expand Down Expand Up @@ -273,13 +274,18 @@ impl FormatContext {
.filter(|d| d.params == 0)
.collect();
let renamer = HoiceDatatypeRenamer::new(&datatypes);
FormatContext { renamer, datatypes }
let raw_definitions = system.raw_definitions.clone();
FormatContext { renamer, datatypes, raw_definitions }
}

pub fn datatypes(&self) -> &[chc::Datatype] {
&self.datatypes
}

pub fn raw_definitions(&self) -> &[chc::RawDefinition] {
&self.raw_definitions
}

pub fn box_ctor(&self, sort: &chc::Sort) -> impl std::fmt::Display {
let ss = Sort::new(sort).sorts();
format!("box{ss}")
Expand Down
29 changes: 29 additions & 0 deletions src/chc/smtlib2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,30 @@ impl<'ctx, 'a> Clause<'ctx, 'a> {
}
}

/// A wrapper around a [`chc::RawDefinition`] that provides a [`std::fmt::Display`] implementation in SMT-LIB2 format.
#[derive(Debug, Clone)]
pub struct RawDefinition<'a> {
inner: &'a chc::RawDefinition,
}

impl<'a> std::fmt::Display for RawDefinition<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
self.inner.definition,
)
}
}

impl<'a> RawDefinition<'a> {
pub fn new(inner: &'a chc::RawDefinition) -> Self {
Self {
inner
}
}
}

/// A wrapper around a [`chc::DatatypeSelector`] that provides a [`std::fmt::Display`] implementation in SMT-LIB2 format.
#[derive(Debug, Clone)]
pub struct DatatypeSelector<'ctx, 'a> {
Expand Down Expand Up @@ -555,6 +579,11 @@ impl<'a> std::fmt::Display for System<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "(set-logic HORN)\n")?;

// insert definition from #![thrust::define_chc()] here
for raw_def in self.ctx.raw_definitions() {
writeln!(f, "{}\n", RawDefinition::new(raw_def))?;
}

writeln!(f, "{}\n", Datatypes::new(&self.ctx, self.ctx.datatypes()))?;
for datatype in self.ctx.datatypes() {
writeln!(f, "{}", DatatypeDiscrFun::new(&self.ctx, datatype))?;
Expand Down
Loading