Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 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
8eb55ef
fix: tests for raw_define(removing unused code, add more description)
coeff-aij Jan 14, 2026
b2c07a8
change: rename raw_define -> raw_command, RawDefine -> RawCommand, etc.
coeff-aij Jan 14, 2026
c2de892
add: negative tests for raw_define
coeff-aij Jan 12, 2026
fe384c2
remove: FormatContext::raw_commands
coeff-aij Jan 14, 2026
f6caf68
fix: wrong description about the error on negative test
coeff-aij Jan 14, 2026
ec3e154
fix: undo accidental renames
coeff-aij Jan 15, 2026
24bf7d4
change: delete annot::AnnotParser::parse_raw_definition() and procces…
coeff-aij Jan 15, 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
24 changes: 24 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,33 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
env:
COAR_IMAGE: ghcr.io/hiroshi-unno/coar@sha256:73144ed27a02b163d1a71b41b58f3b5414f12e91326015600cfdca64ff19f011
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-z3
- name: Setup thrust-pcsat-wrapper
run: |
docker pull "$COAR_IMAGE"

cat <<"EOF" > thrust-pcsat-wrapper
#!/bin/bash

smt2=$(mktemp -p . --suffix .smt2)
trap "rm -f $smt2" EXIT
cp "$1" "$smt2"
out=$(
docker run --rm -v "$PWD:/mnt" -w /root/coar "$COAR_IMAGE" \
main.exe -c ./config/solver/pcsat_tbq_ar.json -p pcsp "/mnt/$smt2"
)
exit_code=$?
echo "${out%,*}"
exit "$exit_code"
EOF
chmod +x thrust-pcsat-wrapper

mkdir -p ~/.local/bin
mv thrust-pcsat-wrapper ~/.local/bin/thrust-pcsat-wrapper
- run: rustup show
- uses: Swatinem/rust-cache@v2
- run: cargo test
30 changes: 30 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
on:
push:
branches:
- main

permissions: {}

concurrency:
group: ${{ github.workflow }}

jobs:
docs:
runs-on: ubuntu-latest
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v4
- run: cargo doc --no-deps --document-private-items
- run: echo '<meta http-equiv="refresh" content="0;url=thrust">' > target/doc/index.html
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: 'target/doc'
- id: deployment
uses: actions/deploy-pages@v4
237 changes: 198 additions & 39 deletions src/analyze.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
//! Analysis of Rust MIR to generate a CHC system.
//!
//! The [`Analyzer`] generates subtyping constraints in the form of CHCs ([`chc::System`]).
//! The entry point is [`crate_::Analyzer::run`], followed by [`local_def::Analyzer::run`]
//! and [`basic_block::Analyzer::run`], while accumulating the necessary information in
//! [`Analyzer`]. Once [`chc::System`] is collected for the entire input, it invokes an external
//! CHC solver with the [`Analyzer::solve`] and subsequently reports the result.

use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

use rustc_hir::lang_items::LangItem;
use rustc_index::IndexVec;
use rustc_middle::mir::{self, BasicBlock, Local};
use rustc_middle::ty::{self as mir_ty, TyCtxt};
use rustc_span::def_id::{DefId, LocalDefId};

use crate::chc;
use crate::pretty::PrettyDisplayExt as _;
use crate::refine::{self, BasicBlockType};
use crate::refine::{self, BasicBlockType, TypeBuilder};
use crate::rty;

mod annot;
Expand Down Expand Up @@ -95,6 +104,44 @@ impl<'tcx> ReplacePlacesVisitor<'tcx> {
}
}

#[derive(Debug, Clone)]
struct DeferredDefTy<'tcx> {
cache: Rc<RefCell<HashMap<mir_ty::GenericArgsRef<'tcx>, rty::RefinedType>>>,
}

#[derive(Debug, Clone)]
enum DefTy<'tcx> {
Concrete(rty::RefinedType),
Deferred(DeferredDefTy<'tcx>),
}

#[derive(Debug, Clone, Default)]
pub struct EnumDefs {
defs: HashMap<DefId, rty::EnumDatatypeDef>,
}

impl EnumDefs {
pub fn find_by_name(&self, name: &chc::DatatypeSymbol) -> Option<&rty::EnumDatatypeDef> {
self.defs.values().find(|def| &def.name == name)
}

pub fn get(&self, def_id: DefId) -> Option<&rty::EnumDatatypeDef> {
self.defs.get(&def_id)
}

pub fn insert(&mut self, def_id: DefId, def: rty::EnumDatatypeDef) {
self.defs.insert(def_id, def);
}
}

impl refine::EnumDefProvider for Rc<RefCell<EnumDefs>> {
fn enum_def(&self, name: &chc::DatatypeSymbol) -> rty::EnumDatatypeDef {
self.borrow().find_by_name(name).unwrap().clone()
}
}

pub type Env = refine::Env<Rc<RefCell<EnumDefs>>>;

#[derive(Clone)]
pub struct Analyzer<'tcx> {
tcx: TyCtxt<'tcx>,
Expand All @@ -104,33 +151,23 @@ pub struct Analyzer<'tcx> {
/// currently contains only local-def templates,
/// but will be extended to contain externally known def's refinement types
/// (at least for every defs referenced by local def bodies)
defs: HashMap<DefId, rty::RefinedType>,
defs: HashMap<DefId, DefTy<'tcx>>,

/// Resulting CHC system.
system: Rc<RefCell<chc::System>>,

basic_blocks: HashMap<LocalDefId, HashMap<BasicBlock, BasicBlockType>>,
def_ids: did_cache::DefIdCache<'tcx>,

enum_defs: Rc<RefCell<HashMap<DefId, rty::EnumDatatypeDef>>>,
enum_defs: Rc<RefCell<EnumDefs>>,
}

impl<'tcx> crate::refine::TemplateTypeGenerator<'tcx> for Analyzer<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}

impl<'tcx> crate::refine::TemplateRegistry for Analyzer<'tcx> {
fn register_template<V>(&mut self, tmpl: rty::Template<V>) -> rty::RefinedType<V> {
tmpl.into_refined_type(|pred_sig| self.generate_pred_var(pred_sig))
}
}

impl<'tcx> crate::refine::UnrefinedTypeGenerator<'tcx> for Analyzer<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
}

impl<'tcx> Analyzer<'tcx> {
pub fn generate_pred_var(&mut self, sig: chc::PredSig) -> chc::PredVarId {
self.system
Expand Down Expand Up @@ -165,7 +202,58 @@ impl<'tcx> Analyzer<'tcx> {
}
}

pub fn register_enum_def(&mut self, def_id: DefId, enum_def: rty::EnumDatatypeDef) {
fn build_enum_def(&self, def_id: DefId) -> rty::EnumDatatypeDef {
let adt = self.tcx.adt_def(def_id);

let name = refine::datatype_symbol(self.tcx, def_id);
let variants: IndexVec<_, _> = adt
.variants()
.iter()
.map(|variant| {
let name = refine::datatype_symbol(self.tcx, variant.def_id);
// TODO: consider using TyCtxt::tag_for_variant
let discr = resolve_discr(self.tcx, variant.discr);
let field_tys = variant
.fields
.iter()
.map(|field| {
let field_ty = self.tcx.type_of(field.did).instantiate_identity();
TypeBuilder::new(self.tcx, def_id).build(field_ty)
})
.collect();
rty::EnumVariantDef {
name,
discr,
field_tys,
}
})
.collect();

let generics = self.tcx.generics_of(def_id);
let ty_params = (0..generics.count())
.filter(|idx| {
matches!(
generics.param_at(*idx, self.tcx).kind,
mir_ty::GenericParamDefKind::Type { .. }
)
})
.count();
tracing::debug!(?def_id, ?name, ?ty_params, "ty_params count");

rty::EnumDatatypeDef {
name,
ty_params,
variants,
}
}

pub fn get_or_register_enum_def(&self, def_id: DefId) -> rty::EnumDatatypeDef {
let mut enum_defs = self.enum_defs.borrow_mut();
if let Some(enum_def) = enum_defs.get(def_id) {
return enum_def.clone();
}

let enum_def = self.build_enum_def(def_id);
tracing::debug!(def_id = ?def_id, enum_def = ?enum_def, "register_enum_def");
let ctors = enum_def
.variants
Expand All @@ -190,30 +278,71 @@ impl<'tcx> Analyzer<'tcx> {
params: enum_def.ty_params,
ctors,
};
self.enum_defs.borrow_mut().insert(def_id, enum_def);
enum_defs.insert(def_id, enum_def.clone());
self.system.borrow_mut().datatypes.push(datatype);
}

pub fn find_enum_variant(
&self,
ty_sym: &chc::DatatypeSymbol,
v_sym: &chc::DatatypeSymbol,
) -> Option<rty::EnumVariantDef> {
self.enum_defs
.borrow()
.iter()
.find(|(_, d)| &d.name == ty_sym)
.and_then(|(_, d)| d.variants.iter().find(|v| &v.name == v_sym))
.cloned()
enum_def
}

pub fn register_def(&mut self, def_id: DefId, rty: rty::RefinedType) {
tracing::info!(def_id = ?def_id, rty = %rty.display(), "register_def");
self.defs.insert(def_id, rty);
self.defs.insert(def_id, DefTy::Concrete(rty));
}

pub fn def_ty(&self, def_id: DefId) -> Option<&rty::RefinedType> {
self.defs.get(&def_id)
pub fn register_deferred_def(&mut self, def_id: DefId) {
tracing::info!(def_id = ?def_id, "register_deferred_def");
self.defs.insert(
def_id,
DefTy::Deferred(DeferredDefTy {
cache: Rc::new(RefCell::new(HashMap::new())),
}),
);
}

pub fn concrete_def_ty(&self, def_id: DefId) -> Option<&rty::RefinedType> {
self.defs.get(&def_id).and_then(|def_ty| match def_ty {
DefTy::Concrete(rty) => Some(rty),
DefTy::Deferred(_) => None,
})
}

pub fn def_ty_with_args(
&mut self,
def_id: DefId,
generic_args: mir_ty::GenericArgsRef<'tcx>,
) -> Option<rty::RefinedType> {
let deferred_ty = match self.defs.get(&def_id)? {
DefTy::Concrete(rty) => {
let type_builder = TypeBuilder::new(self.tcx, def_id);

let mut def_ty = rty.clone();
def_ty.instantiate_ty_params(
generic_args
.types()
.map(|ty| type_builder.build(ty))
.map(rty::RefinedType::unrefined)
.collect(),
);
return Some(def_ty);
}
DefTy::Deferred(deferred) => deferred,
};

let deferred_ty_cache = Rc::clone(&deferred_ty.cache); // to cut reference to allow &mut self
if let Some(rty) = deferred_ty_cache.borrow().get(&generic_args) {
return Some(rty.clone());
}

let mut analyzer = self.local_def_analyzer(def_id.as_local()?);
analyzer.generic_args(generic_args);

let expected = analyzer.expected_ty();
deferred_ty_cache
.borrow_mut()
.insert(generic_args, expected.clone());

analyzer.run(&expected);
Some(expected)
}

pub fn register_basic_block_ty(
Expand Down Expand Up @@ -243,14 +372,8 @@ impl<'tcx> Analyzer<'tcx> {
self.register_def(panic_def_id, rty::RefinedType::unrefined(panic_ty.into()));
}

pub fn new_env(&self) -> refine::Env {
let defs = self
.enum_defs
.borrow()
.values()
.map(|def| (def.name.clone(), def.clone()))
.collect();
refine::Env::new(defs)
pub fn new_env(&self) -> Env {
refine::Env::new(Rc::clone(&self.enum_defs))
}

pub fn crate_analyzer(&mut self) -> crate_::Analyzer<'tcx, '_> {
Expand All @@ -277,4 +400,40 @@ impl<'tcx> Analyzer<'tcx> {
self.tcx.dcx().err(format!("verification error: {:?}", err));
}
}

/// Computes the signature of the local function.
///
/// This works like `self.tcx.fn_sig(local_def_id).instantiate_identity().skip_binder()`,
/// but extracts parameter and return types directly from the given `body` to obtain a signature that
/// reflects potential type instantiations happened after `optimized_mir`.
pub fn local_fn_sig_with_body(
&self,
local_def_id: LocalDefId,
body: &mir::Body<'tcx>,
) -> mir_ty::FnSig<'tcx> {
let ty = self.tcx.type_of(local_def_id).instantiate_identity();
let sig = if let mir_ty::TyKind::Closure(_, substs) = ty.kind() {
substs.as_closure().sig().skip_binder()
} else {
ty.fn_sig(self.tcx).skip_binder()
};

self.tcx.mk_fn_sig(
body.args_iter().map(|arg| body.local_decls[arg].ty),
body.return_ty(),
sig.c_variadic,
sig.unsafety,
sig.abi,
)
}

/// Computes the signature of the local function.
///
/// This works like `self.tcx.fn_sig(local_def_id).instantiate_identity().skip_binder()`,
/// but extracts parameter and return types directly from [`mir::Body`] to obtain a signature that
/// reflects the actual type of lifted closure functions.
pub fn local_fn_sig(&self, local_def_id: LocalDefId) -> mir_ty::FnSig<'tcx> {
let body = self.tcx.optimized_mir(local_def_id);
self.local_fn_sig_with_body(local_def_id, body)
}
}
Loading