Skip to content

Commit 07d9329

Browse files
Fix coupling (#123)
* Fix coupling * WIP * WIP * WIP * WIP * Add pcg-macros crate with DisplayWithCtxt derive macro Co-authored-by: zgrannan <zgrannan@protonmail.com> * Add DisplayWithCtxt derive to enums, remove impl from edgedata_enum macro - Update pcg-macros derive macro to add bounds based on field types - Add derive macro to FunctionCallOrLoop, BorrowPcgExpansion, BorrowPcgEdgeKind - Remove DisplayWithCtxt impl generation from edgedata_enum! macro Co-authored-by: zgrannan <zgrannan@protonmail.com> * Fix rustfmt formatting issues in pcg-macros Co-authored-by: zgrannan <zgrannan@protonmail.com> * WIP * WIP * WIP * Simple union-find based coupling * Update tests * small fixes * small fixes * remove usage of global settings * Appease rustfmt * whoops --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent 616d96f commit 07d9329

50 files changed

Lines changed: 1250 additions & 932 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ env:
1212
IMAGE_NAME: ${{ github.repository }}/pcg-server
1313
MUTATION_TESTS_REF: zgrannan/update-pcg
1414
FLOWISTRY_REF: zgrannan/pcg-next4
15-
SYMBOLIC_EXECUTION_REF: zgrannan/pcg-fix-nested
16-
PURIFICATION_PRUSTI_REF: zgrannan/next5
15+
SYMBOLIC_EXECUTION_REF: main
16+
PURIFICATION_PRUSTI_REF: zgrannan/old-purification
1717

1818
jobs:
1919
check_warnings:

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"rust-analyzer.rustc.source": "discover",
3-
"rust-analyzer.cargo.features": ["visualization", "coupling"]
3+
"rust-analyzer.cargo.features": ["visualization"]
44
}

Cargo.lock

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ edition = "2024"
88
custom-rust-toolchain = []
99
visualization = []
1010
debug_info = []
11-
coupling = []
1211
type-export = ["specta"]
13-
default = ["visualization", "coupling"]
12+
default = ["visualization"]
1413

1514
[dependencies]
15+
pcg-macros = { path = "pcg-macros" }
1616
bit-set = "0.8.0"
1717
bumpalo = {version = "3.16.0", features = ["allocator_api"]}
1818
derive_more = { version = "2.0.1", features = ["full"] }
@@ -30,6 +30,7 @@ strum = "0.26"
3030
strum_macros = "0.26"
3131
tracing = "0.1"
3232
tracing-subscriber = "0.3.20"
33+
union-find = "0.4"
3334

3435
[dev-dependencies]
3536
reqwest = { version = "^0.11", features = ["blocking"] }

pcg-bin/Cargo.lock

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pcg-bin/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ authors = ["Prusti Devs <prusti_developers@sympa.ethz.ch>"]
99
edition = "2024"
1010

1111
[dependencies]
12-
pcg = { path = "..", features = ["visualization", "coupling"] }
12+
pcg = { path = "..", features = ["visualization"] }
1313
borrowck-body-storage = { path = "../borrowck-body-storage" }
1414
shell-escape = "0.1.5"
1515
tracing = "0.1"

pcg-macros/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "pcg-macros"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[lib]
7+
proc-macro = true
8+
9+
[dependencies]
10+
proc-macro2 = "1.0"
11+
quote = "1.0"
12+
syn = { version = "2.0", features = ["full", "extra-traits"] }

pcg-macros/src/lib.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use proc_macro::TokenStream;
2+
use quote::{ToTokens, quote};
3+
use syn::{Data, DeriveInput, Fields, GenericParam, Type, parse_macro_input};
4+
5+
/// Derive macro for `DisplayWithCtxt` on enums where each variant has a single field.
6+
///
7+
/// This generates an implementation that requires a context type `Ctxt` where
8+
/// `Ctxt: Copy` and each variant's inner type implements `DisplayWithCtxt<Ctxt>`.
9+
///
10+
/// Example:
11+
/// ```ignore
12+
/// #[derive(DisplayWithCtxt)]
13+
/// pub enum FunctionCallOrLoop<FunctionCallData, LoopData> {
14+
/// FunctionCall(FunctionCallData),
15+
/// Loop(LoopData),
16+
/// }
17+
/// ```
18+
///
19+
/// Generates:
20+
/// ```ignore
21+
/// impl<FunctionCallData, LoopData, Ctxt: Copy> DisplayWithCtxt<Ctxt>
22+
/// for FunctionCallOrLoop<FunctionCallData, LoopData>
23+
/// where
24+
/// FunctionCallData: DisplayWithCtxt<Ctxt>,
25+
/// LoopData: DisplayWithCtxt<Ctxt>,
26+
/// {
27+
/// fn display_output(&self, ctxt: Ctxt, mode: OutputMode) -> DisplayOutput {
28+
/// match self {
29+
/// FunctionCallOrLoop::FunctionCall(inner) => inner.display_output(ctxt, mode),
30+
/// FunctionCallOrLoop::Loop(inner) => inner.display_output(ctxt, mode),
31+
/// }
32+
/// }
33+
/// }
34+
/// ```
35+
#[proc_macro_derive(DisplayWithCtxt)]
36+
pub fn derive_display_with_ctxt(input: TokenStream) -> TokenStream {
37+
let input = parse_macro_input!(input as DeriveInput);
38+
39+
let name = &input.ident;
40+
let generics = &input.generics;
41+
42+
let Data::Enum(data_enum) = &input.data else {
43+
return syn::Error::new_spanned(&input, "DisplayWithCtxt can only be derived for enums")
44+
.to_compile_error()
45+
.into();
46+
};
47+
48+
let mut variant_arms = Vec::new();
49+
let mut field_types: Vec<&Type> = Vec::new();
50+
51+
for variant in &data_enum.variants {
52+
let variant_name = &variant.ident;
53+
54+
match &variant.fields {
55+
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
56+
let field = fields.unnamed.first().unwrap();
57+
field_types.push(&field.ty);
58+
variant_arms.push(quote! {
59+
#name::#variant_name(inner) => inner.display_output(ctxt, mode)
60+
});
61+
}
62+
_ => {
63+
return syn::Error::new_spanned(
64+
variant,
65+
"DisplayWithCtxt requires each variant to have exactly one unnamed field",
66+
)
67+
.to_compile_error()
68+
.into();
69+
}
70+
}
71+
}
72+
73+
let (_impl_generics, ty_generics, where_clause) = generics.split_for_impl();
74+
75+
let existing_where_predicates = where_clause.map(|w| &w.predicates);
76+
77+
let where_bounds = field_types.iter().map(|ty| {
78+
quote! { #ty: crate::utils::display::DisplayWithCtxt<Ctxt> }
79+
});
80+
81+
let existing_impl_params: Vec<_> = generics
82+
.params
83+
.iter()
84+
.map(|param| match param {
85+
GenericParam::Type(ty) => {
86+
let ident = &ty.ident;
87+
let bounds = &ty.bounds;
88+
if bounds.is_empty() {
89+
quote! { #ident }
90+
} else {
91+
quote! { #ident: #bounds }
92+
}
93+
}
94+
other => other.to_token_stream(),
95+
})
96+
.collect();
97+
98+
let expanded = quote! {
99+
impl<#(#existing_impl_params,)* Ctxt: Copy> crate::utils::display::DisplayWithCtxt<Ctxt>
100+
for #name #ty_generics
101+
where
102+
#(#where_bounds,)*
103+
#existing_where_predicates
104+
{
105+
fn display_output(
106+
&self,
107+
ctxt: Ctxt,
108+
mode: crate::utils::display::OutputMode,
109+
) -> crate::utils::display::DisplayOutput {
110+
match self {
111+
#(#variant_arms,)*
112+
}
113+
}
114+
}
115+
};
116+
117+
TokenStream::from(expanded)
118+
}

pcg-tests/Cargo.lock

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pcg-tests/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ authors = ["Prusti Devs <prusti_developers@sympa.ethz.ch>"]
55
edition = "2024"
66

77
[dependencies]
8-
pcg = { path = "..", features = ["visualization", "coupling"] }
8+
pcg = { path = "..", features = ["visualization"] }
99
borrowck-body-storage = { path = "../borrowck-body-storage" }
1010
chrono = "0.4"
1111
derive_more = { version = "2.0.1", features = ["full"] }
1212
itertools = "0.12.0"
1313
rayon = "1.8"
14+
regex = "1"
1415
reqwest = { version = "^0.11", features = ["blocking"] }
1516
rustversion = "1.0"
1617
serde = "1.0"

0 commit comments

Comments
 (0)