-
Notifications
You must be signed in to change notification settings - Fork 271
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
Feat: Ability to emit sync-compatible Rust code. #6040
Conversation
TODO: We need to downcast objects as well. Downcast requires send and sync but it's not implemented for objects for some reason. |
<small>By submitting this pull request, I confirm that my contribution is made under the terms of the [MIT license](https://github.com/dafny-lang/dafny/blob/master/LICENSE.txt).</small>
…g/dafny into fix-5969-sync-for-rust
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.
LGTM in general, made suggestions to improve the code but I'm not blocking on them, up to you how much to pull in. The comments about customer-facing documentation are the only blockers.
@@ -594,6 +595,8 @@ module {:extern "Defs"} DafnyToRustCompilerDefinitions { | |||
} | |||
|
|||
function CoerceImpl( | |||
rc: R.Type -> R.Type, |
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.
Consider using a term distinct from "RC" throughout for the abstract concept that will be mapped to either Rc
or Arc
. It's easy to get the concepts confused here, and that might have been a factor in initially missing that IsRc
was testing for the wrong thing.
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.
I considered your proposition, but Arc and Rc are essentially the same concept "rc" that is used throughout the codebase. What prevented us from catch IsRc is that Rc was imported twice into two different names, which is no longer the case, so I think we are fine here.
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.
Fair enough, and I wouldn't want to change the symbols in the runtime at this point since we've already updated smithy-dafny to use Rc
too.
pub use ::std::marker::Send; | ||
pub use ::std::marker::Sync; | ||
|
||
#[cfg(not(feature = "sync"))] |
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.
FYI this would probably be cleaner if you used https://crates.io/crates/cfg-if to group things into just two blocks:
cfg_if::cfg_if! {
if #[feature = "sync"] {
pub type DynAny = dyn Any + Send + Sync;
pub struct UnsafeCell<T: ?Sized> {
data: ::std::cell::UnsafeCell<T>, // UnsafeCell for interior mutability
}
...
} else {
pub type DynAny = dyn Any;
pub use ::std::cell::UnsafeCell;
...
}
}
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.
I would like to avoid taking dependencies that are not strictly necessary. I'm ok with repeating the test in front of every specific declaration as well.
data: ::std::cell::UnsafeCell<T>, // UnsafeCell for interior mutability | ||
} | ||
#[cfg(feature = "sync")] | ||
unsafe impl<T: ?Sized> Sync for UnsafeCell<T> where T: Send {} |
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.
Why is unsafe necessary here? We should at least document it
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.
Documented + removed use of UnsafeCell for sequences to prefer the mutex just in case, and added tests.
@@ -784,29 +822,59 @@ where | |||
left, | |||
right, | |||
} => { | |||
#[cfg(feature = "sync")] |
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.
cfg-if
even more appealing here IMO
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.
I'm grouping statements together and do some renaming so that using cfg-if will look a bit less appealing :-)
let t: Rc<T> = Rc::downcast::<T>(t).ok()?; | ||
mem::transmute(t) | ||
} | ||
|
||
/// A reference counted smart pointer with unrestricted mutability. | ||
pub struct ArcMut<T: ?Sized> { |
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.
Now I'm curious, why was this already in here?
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.
Because I had envisioned since the beginning we might want to have the async version of Rc for objects; however, I had not used it. So removing it because now for sure we are implementing it differently.
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.
Nice test!
}; | ||
public override IEnumerable<Option<string>> SupportedOptions => new List<Option<string>> { RustModuleNameOption }; | ||
public static readonly Option<bool> RustSyncOption = new("--rust-sync", | ||
@"Ensures that both datatypes and Dafny collections use structures that implement the Sync and Send traits") { |
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.
Isn't this essentially all values, not just datatypes and collections?
} else { | ||
args.Add("--bin"); | ||
args.Add(Path.GetFileNameWithoutExtension(targetFilename)); | ||
private static void ImportRustRuntimeTo(string targetDirectory) { |
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.
Would it be possible to use SinglePassCodeGenerator.EmitRuntimeSource
or .ReadRuntimeSystem
instead, perhaps with some tweaks to those methods?
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.
I think this function is the closest to ReadRuntimeSystem
than EmitRuntimeSource
since we don't emit the run-time inside the generated code. However, I think ReadRuntimeSystem
is a terrible name since it essentially copies the runtime into a specific location.
I'll rename both Rust and Java version to ImportRuntimeTo
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.
I started also looking into merging them but this function does not lies in the same architecture as the SinglePassCompiler. There is another compiler in between and linking them together would be more verbose than necessary. Moreover, I see that the code significantly differ so I'm not sure tweaking the existing ReadRuntimeSystem would actually help anything. I'll skip that part as, even if we end up to want it, would probably require a PR of its own.
@@ -7,3 +7,6 @@ edition = "2021" | |||
once_cell = "1.18.0" | |||
num = "0.4" | |||
itertools = "0.11.0" | |||
|
|||
[features] | |||
sync = [] |
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.
Worth a comment, since consumers have to turn on this feature and should be aware of what it's doing?
# Conflicts: # Source/DafnyRuntime/DafnyRuntimeRust/src/system/mod.rs
Fixes #5969
What was changed?
--rust-sync
, set tofalse
by default. When turned on, all the generated code usesstd::sync::Arc
instead ofstd::rc::Rc
. Nothing else is normally needed for generated code.--include-runtime=true
now emits the runtime when translating to Rust.feature = "sync"
, enabling or disabling some functions via#[cfg(feature = "sync")]
and#[cfg(not(feature = "sync"))]
. Regular tests don't use this feature.cargo fmt
on the runtime.How has this been tested?
comp/rust/arc/tokiouser.dfy
uses this option and the runtime with the sync feature, compiled with--rust-sync
By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.