-
Notifications
You must be signed in to change notification settings - Fork 0
Support closures (with simple usage of traits) #10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
6823360
3569233
38065dd
5dba87e
ae1e226
d2692ae
3a0ccfe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -84,16 +84,54 @@ impl<'tcx, 'ctx> Analyzer<'tcx, 'ctx> { | |||||||||
| ) -> Vec<chc::Clause> { | ||||||||||
| let mut clauses = Vec::new(); | ||||||||||
|
|
||||||||||
| if expected_args.is_empty() { | ||||||||||
| // elaboration: we need at least one predicate variable in parameter (see mir_function_ty_impl) | ||||||||||
| expected_args.push(rty::RefinedType::unrefined(rty::Type::unit()).vacuous()); | ||||||||||
| } | ||||||||||
| tracing::debug!( | ||||||||||
| got = %got.display(), | ||||||||||
| expected = %crate::pretty::FunctionType::new(&expected_args, &expected_ret).display(), | ||||||||||
| "fn_sub_type" | ||||||||||
| ); | ||||||||||
|
|
||||||||||
| match got.abi { | ||||||||||
| rty::FunctionAbi::Rust => { | ||||||||||
| if expected_args.is_empty() { | ||||||||||
| // elaboration: we need at least one predicate variable in parameter (see mir_function_ty_impl) | ||||||||||
| expected_args.push(rty::RefinedType::unrefined(rty::Type::unit()).vacuous()); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| rty::FunctionAbi::RustCall => { | ||||||||||
| // &Closure, { v: (own i32, own bool) | v = (<0>, <false>) } | ||||||||||
| // => | ||||||||||
| // &Closure, { v: i32 | (<v>, _) = (<0>, <false>) }, { v: bool | (_, <v>) = (<0>, <false>) } | ||||||||||
|
|
||||||||||
| let rty::RefinedType { ty, mut refinement } = | ||||||||||
| expected_args.pop().expect("rust-call last arg"); | ||||||||||
| let ty = ty.into_tuple().expect("rust-call last arg is tuple"); | ||||||||||
| let mut replacement_tuple = Vec::new(); // will be (<v>, _) or (_, <v>) | ||||||||||
| for elem in &ty.elems { | ||||||||||
| let existential = refinement.existentials.push(elem.ty.to_sort()); | ||||||||||
| replacement_tuple.push(chc::Term::var(rty::RefinedTypeVar::Existential( | ||||||||||
| existential, | ||||||||||
| ))); | ||||||||||
| } | ||||||||||
|
|
||||||||||
|
||||||||||
| // In the RustCall ABI, the last argument is a tuple of pointer types (e.g., `&mut self`, `&args`). | |
| // Therefore, it is safe to call `deref()` on each tuple element here. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -83,6 +83,36 @@ where | |
| } | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] | ||
| pub enum FunctionAbi { | ||
| #[default] | ||
| Rust, | ||
| RustCall, | ||
| } | ||
|
|
||
| impl std::fmt::Display for FunctionAbi { | ||
| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||
| f.write_str(self.name()) | ||
| } | ||
| } | ||
|
|
||
| impl FunctionAbi { | ||
| pub fn name(&self) -> &'static str { | ||
| match self { | ||
| FunctionAbi::Rust => "rust", | ||
| FunctionAbi::RustCall => "rust-call", | ||
| } | ||
| } | ||
|
|
||
| pub fn is_rust(&self) -> bool { | ||
| matches!(self, FunctionAbi::Rust) | ||
| } | ||
|
|
||
| pub fn is_rust_call(&self) -> bool { | ||
| matches!(self, FunctionAbi::RustCall) | ||
| } | ||
| } | ||
|
|
||
| /// A function type. | ||
| /// | ||
| /// In Thrust, function types are closed. Because of that, function types, thus its parameters and | ||
|
|
@@ -92,6 +122,7 @@ where | |
| pub struct FunctionType { | ||
| pub params: IndexVec<FunctionParamIdx, RefinedType<FunctionParamIdx>>, | ||
| pub ret: Box<RefinedType<FunctionParamIdx>>, | ||
| pub abi: FunctionAbi, | ||
| } | ||
|
|
||
| impl<'a, 'b, D> Pretty<'a, D, termcolor::ColorSpec> for &'b FunctionType | ||
|
|
@@ -100,15 +131,25 @@ where | |
| D::Doc: Clone, | ||
| { | ||
| fn pretty(self, allocator: &'a D) -> pretty::DocBuilder<'a, D, termcolor::ColorSpec> { | ||
| let abi = match self.abi { | ||
| FunctionAbi::Rust => allocator.nil(), | ||
| abi => allocator | ||
| .text("extern") | ||
| .append(allocator.space()) | ||
| .append(allocator.as_string(abi)) | ||
| .append(allocator.space()), | ||
| }; | ||
| let separator = allocator.text(",").append(allocator.line()); | ||
| allocator | ||
| .intersperse(self.params.iter().map(|ty| ty.pretty(allocator)), separator) | ||
| .parens() | ||
| .append(allocator.space()) | ||
| .append(allocator.text("→")) | ||
| .append(allocator.line()) | ||
| .append(self.ret.pretty(allocator)) | ||
| .group() | ||
| abi.append( | ||
| allocator | ||
| .intersperse(self.params.iter().map(|ty| ty.pretty(allocator)), separator) | ||
| .parens(), | ||
| ) | ||
| .append(allocator.space()) | ||
| .append(allocator.text("→")) | ||
| .append(allocator.line()) | ||
| .append(self.ret.pretty(allocator)) | ||
| .group() | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -120,9 +161,15 @@ impl FunctionType { | |
| FunctionType { | ||
| params, | ||
| ret: Box::new(ret), | ||
| abi: FunctionAbi::Rust, | ||
| } | ||
| } | ||
|
|
||
| pub fn with_abi(mut self, abi: FunctionAbi) -> Self { | ||
| self.abi = abi; | ||
| self | ||
| } | ||
|
|
||
| /// Because function types are always closed in Thrust, we can convert this into | ||
| /// [`Type<Closed>`]. | ||
| pub fn into_closed_ty(self) -> Type<Closed> { | ||
|
|
@@ -1304,6 +1351,29 @@ impl<FV> RefinedType<FV> { | |
| RefinedType { ty, refinement } | ||
| } | ||
|
|
||
| pub fn deref(self) -> Self { | ||
| let RefinedType { | ||
| ty, | ||
| refinement: outer_refinement, | ||
| } = self; | ||
| let inner_ty = ty.into_pointer().expect("invalid deref"); | ||
| if inner_ty.is_mut() { | ||
| // losing info about proph | ||
| panic!("invalid deref"); | ||
| } | ||
| let RefinedType { | ||
| ty: inner_ty, | ||
| refinement: mut inner_refinement, | ||
| } = *inner_ty.elem; | ||
| inner_refinement.push_conj( | ||
| outer_refinement.subst_value_var(|| chc::Term::var(RefinedTypeVar::Value).boxed()), | ||
| ); | ||
| RefinedType { | ||
| ty: inner_ty, | ||
| refinement: inner_refinement, | ||
| } | ||
| } | ||
|
Comment on lines
+1354
to
+1375
|
||
|
|
||
| pub fn subst_var<F, W>(self, mut f: F) -> RefinedType<W> | ||
| where | ||
| F: FnMut(FV) -> chc::Term<W>, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| //@error-in-other-file: Unsat | ||
| //@compile-flags: -C debug-assertions=off | ||
|
|
||
| fn main() { | ||
| let mut x = 1; | ||
| let mut incr = |by: i32| { | ||
| x += by; | ||
| }; | ||
| incr(5); | ||
| incr(5); | ||
| assert!(x == 10); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| //@error-in-other-file: Unsat | ||
| //@compile-flags: -C debug-assertions=off | ||
|
|
||
| fn main() { | ||
| let mut x = 1; | ||
| x += 1; | ||
| let mut incr = || { | ||
| x += 1; | ||
| }; | ||
| incr(); | ||
| assert!(x == 2); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| //@error-in-other-file: Unsat | ||
| //@compile-flags: -C debug-assertions=off | ||
|
|
||
| fn main() { | ||
| let incr = |x| { | ||
| x + 1 | ||
| }; | ||
| assert!(incr(2) == 2); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation states this is a "drop-in replacement" but actually provides different behavior for closure types, extracting types from MIR body rather than using fn_sig. Consider clarifying that this method provides enhanced behavior for closures, not just a simple replacement.