-
Notifications
You must be signed in to change notification settings - Fork 18
smite-ir: Introduce affine state variables for strict state enforcement #97
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: master
Are you sure you want to change the base?
Changes from all commits
67c87b0
b0ebefc
d6cec1f
80b7b13
ae4113e
d7f0788
41acbf2
5d3fd3c
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 |
|---|---|---|
|
|
@@ -71,13 +71,36 @@ impl ProgramBuilder { | |
| actual_type, expected_type, | ||
| "{operation:?} input {i}: expected {expected_type:?}, got {actual_type:?}", | ||
| ); | ||
| if actual_type.is_affine() { | ||
| let cands = self.candidates.get_mut(&actual_type).unwrap_or_else(|| { | ||
| panic!("{operation:?} input {i}: no candidates for {actual_type:?}") | ||
| }); | ||
|
|
||
| // Find the position, or panic if it doesn't exist. | ||
| let pos = cands | ||
| .iter() | ||
| .position(|c| match c { | ||
| Candidate::Direct(idx) => *idx == input_idx, | ||
| Candidate::Extract { compound_idx, .. } => *compound_idx == input_idx, | ||
| }) | ||
| .unwrap_or_else(|| { | ||
| panic!("{operation:?} input {i}: affine {actual_type:?} already consumed") | ||
| }); | ||
|
|
||
| // Consume it. | ||
| cands.remove(pos); | ||
| } | ||
| } | ||
|
|
||
| let idx = self.instructions.len(); | ||
|
|
||
| if let Some(out_type) = operation.output_type() { | ||
| // Register extractable fields for compound types. | ||
| for (extract_op, field_type) in operation.extractable_fields() { | ||
| assert!( | ||
| !out_type.is_affine(), | ||
| "Affine variables cannot be extracted" | ||
| ); | ||
| self.candidates | ||
| .entry(field_type) | ||
| .or_default() | ||
|
|
@@ -103,12 +126,27 @@ impl ProgramBuilder { | |
|
|
||
| /// Selects or creates a variable of the given type using probabilistic | ||
| /// variable selection (75% most recent, 15% any existing, 10% fresh). | ||
| #[allow(clippy::missing_panics_doc)] // candidates is always non-empty | ||
| /// | ||
| /// # Panics | ||
| /// | ||
| /// Panics in the following scenarios: | ||
| /// * An affine type is requested but its candidate pool is empty (all instances have been consumed). | ||
| /// * Attempts to generate a fresh variable for a type that cannot be instantiated out-of-thin-air | ||
| /// (e.g., `Message`, `AcceptChannel`, or affine types). | ||
| /// * Resolving the chosen candidate triggers a panic in the underlying `append` operation. | ||
| pub fn pick_variable(&mut self, var_type: VariableType, rng: &mut impl Rng) -> usize { | ||
| let Some(candidates) = self.candidates.get(&var_type) else { | ||
| let Some(candidates) = self.candidates.get_mut(&var_type) else { | ||
| return self.generate_fresh(var_type, rng); | ||
| }; | ||
|
|
||
| if var_type.is_affine() { | ||
| let chosen_candidate = candidates | ||
| .last() | ||
| .unwrap_or_else(|| panic!("no candidates for {var_type:?}")) | ||
| .clone(); | ||
| return self.resolve_candidate(chosen_candidate); | ||
| } | ||
|
Comment on lines
+143
to
+148
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typical usage of let v = builder.pick_variable(SentOpenChannel, rng);
builder.append(RecvAcceptChannel, &[v]);So we can't remove from I'm pretty sure we want
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ugh, you're right, my bad. |
||
|
|
||
| let roll = rng.random_range(0..20); | ||
| match roll { | ||
| // 10%: generate a fresh value even though candidates exist. | ||
|
|
@@ -168,12 +206,18 @@ impl ProgramBuilder { | |
| VariableType::Message => { | ||
| panic!("cannot generate fresh Message: requires composed inputs") | ||
| } | ||
| VariableType::OpenChannelMessage => { | ||
| panic!("cannot generate fresh OpenChannelMessage: requires composed inputs") | ||
| } | ||
| VariableType::AcceptChannel => { | ||
| panic!("cannot generate fresh AcceptChannel: requires protocol interaction") | ||
| } | ||
| VariableType::FundingTransaction => { | ||
| panic!("cannot generate fresh FundingTransaction: requires composed inputs") | ||
| } | ||
| VariableType::SentOpenChannel => { | ||
| panic!("cannot generate fresh affine type") | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -19,7 +19,16 @@ impl Mutator for InputSwapMutator { | |||||||||||||||||||||||||||||||
| .instructions | ||||||||||||||||||||||||||||||||
| .iter() | ||||||||||||||||||||||||||||||||
| .enumerate() | ||||||||||||||||||||||||||||||||
| .flat_map(|(i, instr)| (0..instr.inputs.len()).map(move |j| (i, j))) | ||||||||||||||||||||||||||||||||
| .flat_map(|(i, instr)| { | ||||||||||||||||||||||||||||||||
| instr | ||||||||||||||||||||||||||||||||
| .operation | ||||||||||||||||||||||||||||||||
| .input_types() | ||||||||||||||||||||||||||||||||
| .into_iter() | ||||||||||||||||||||||||||||||||
| .enumerate() | ||||||||||||||||||||||||||||||||
| // Affine variables carry no data, so swapping one affine variable with | ||||||||||||||||||||||||||||||||
| // another has no practical effect. | ||||||||||||||||||||||||||||||||
| .filter_map(move |(j, ty)| (!ty.is_affine()).then_some((i, j))) | ||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||
|
Comment on lines
+22
to
+31
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: it's a bit awkward/inefficient to call
Suggested change
|
||||||||||||||||||||||||||||||||
| .choose(rng) | ||||||||||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
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.
There's some trickiness here -- should
is_affine()return true or false for a compound variable containing an affine field?If it returns true, we need to ensure below that we only remove the candidate when the affine field is actually extracted -- not when a non-affine field is extracted.
If it returns false, this code will never run for compound variables, and we would fail to detect multiple uses of affine fields.
Or if we are going to ban affine types from being extractable fields, we should add an assertion somewhere to panic on such cases.
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.
Compound variables containing an affine type should definitely return false on
is_affine(), to allow multiple extractions from them.To access an affine variable contained in a compound one, Generators should first manually
Extract*the affine variable, registering it as an available candidate. We can then detect over-extractions by re-introducing theOverExtractlogic we just got rid of, and over-use of the extracted affine will be naturally handled by the existing code.Or, we can introduce dummy operations that accept such compound variables and output a single affine type.
In any case, I don't think we need to deal with this for now. The specifics can be ironed out if and when we encounter such a scenario (I personally have a feeling we won't).
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.
Then let's ban it and add an assertion in the
operation.extractable_fields()loop below that extractable fields are never affine.