Skip to content

Commit f7896b6

Browse files
Add Souffle variants of some Algorithms
1 parent ed79378 commit f7896b6

File tree

5 files changed

+76
-40
lines changed

5 files changed

+76
-40
lines changed

polonius-engine/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ pub use facts::AllFacts;
1313
pub use facts::Atom;
1414
pub use facts::FactTypes;
1515
pub use output::Algorithm;
16+
pub use output::Engine;
1617
pub use output::Output;

polonius-engine/src/output/mod.rs

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,19 @@ mod liveness;
2121
mod location_insensitive;
2222
mod naive;
2323

24+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25+
pub enum Engine {
26+
Souffle,
27+
Datafrog,
28+
}
29+
2430
#[derive(Debug, Clone, Copy)]
2531
pub enum Algorithm {
2632
/// Simple rules, but slower to execute
27-
Naive,
33+
Naive(Engine),
2834

2935
/// Optimized variant of the rules
30-
DatafrogOpt,
36+
DatafrogOpt(Engine),
3137

3238
/// Fast to compute, but imprecise: there can be false-positives
3339
/// but no false-negatives. Tailored for quick "early return" situations.
@@ -42,14 +48,20 @@ pub enum Algorithm {
4248
Hybrid,
4349
}
4450

51+
impl Default for Algorithm {
52+
fn default() -> Self {
53+
Self::Naive(Engine::Datafrog)
54+
}
55+
}
56+
4557
impl Algorithm {
4658
/// Optimized variants that ought to be equivalent to "naive"
47-
pub const OPTIMIZED: &'static [Algorithm] = &[Algorithm::DatafrogOpt];
59+
pub const OPTIMIZED: &'static [Algorithm] = &[Algorithm::DatafrogOpt(Engine::Datafrog)];
4860

4961
pub fn variants() -> [&'static str; 5] {
5062
[
51-
"Naive",
52-
"DatafrogOpt",
63+
"Naive(-Souffle)",
64+
"DatafrogOpt(-Souffle)",
5365
"LocationInsensitive",
5466
"Compare",
5567
"Hybrid",
@@ -79,14 +91,14 @@ impl ::std::str::FromStr for Algorithm {
7991
type Err = String;
8092
fn from_str(s: &str) -> Result<Self, Self::Err> {
8193
match s.to_lowercase().as_ref() {
82-
"naive" => Ok(Algorithm::Naive),
83-
"datafrogopt" => Ok(Algorithm::DatafrogOpt),
94+
"naive" => Ok(Algorithm::Naive(Engine::Datafrog)),
95+
"naive-souffle" => Ok(Algorithm::Naive(Engine::Souffle)),
96+
"datafrogopt" => Ok(Algorithm::DatafrogOpt(Engine::Datafrog)),
97+
"datafrogopt-souffle" => Ok(Algorithm::DatafrogOpt(Engine::Souffle)),
8498
"locationinsensitive" => Ok(Algorithm::LocationInsensitive),
8599
"compare" => Ok(Algorithm::Compare),
86100
"hybrid" => Ok(Algorithm::Hybrid),
87-
_ => Err(String::from(
88-
"valid values: Naive, DatafrogOpt, LocationInsensitive, Compare, Hybrid",
89-
)),
101+
_ => Err(format!("valid values: {}", Self::variants().join(", "))),
90102
}
91103
}
92104
}
@@ -175,6 +187,29 @@ impl<T: FactTypes> Output<T> {
175187
/// partial results can also be stored in the context, so that the following
176188
/// variant can use it to prune its own input data
177189
pub fn compute(all_facts: &AllFacts<T>, algorithm: Algorithm, dump_enabled: bool) -> Self {
190+
if algorithm.engine() == Engine::Souffle {
191+
let name = algorithm.souffle_name().expect("Algorithm does not have Soufflé version");
192+
let facts = polonius_souffle::run_from_facts(name, &all_facts);
193+
194+
let mut out = Output::new(dump_enabled);
195+
196+
for tuple in facts.get("errors").unwrap().iter() {
197+
let [loan, location]: [u32; 2] = tuple.try_into().unwrap();
198+
out.errors.entry(T::Point::from(location)).or_default().push(T::Loan::from(loan));
199+
}
200+
201+
for tuple in facts.get("subset_errors").unwrap().iter() {
202+
let [origin1, origin2, location]: [u32; 3] = tuple.try_into().unwrap();
203+
out
204+
.subset_errors
205+
.entry(T::Point::from(location))
206+
.or_default()
207+
.insert((T::Origin::from(origin1), T::Origin::from(origin2)));
208+
}
209+
210+
return out;
211+
}
212+
178213
let mut result = Output::new(dump_enabled);
179214

180215
// TODO: remove all the cloning thereafter, but that needs to be done in concert with rustc
@@ -317,8 +352,8 @@ impl<T: FactTypes> Output<T> {
317352

318353
(potential_errors, potential_subset_errors)
319354
}
320-
Algorithm::Naive => naive::compute(&ctx, &mut result),
321-
Algorithm::DatafrogOpt => datafrog_opt::compute(&ctx, &mut result),
355+
Algorithm::Naive(_) => naive::compute(&ctx, &mut result),
356+
Algorithm::DatafrogOpt(_) => datafrog_opt::compute(&ctx, &mut result),
322357
Algorithm::Hybrid => {
323358
// Execute the fast `LocationInsensitive` computation as a pre-pass:
324359
// if it finds no possible errors, we don't need to do the more complex

src/cli.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use log::{error, Level, LevelFilter, Metadata, Record, SetLoggerError};
22
use pico_args as pico;
3-
use polonius_engine::Algorithm;
3+
use polonius_engine::{Algorithm, Engine};
44
use std::env;
55
use std::error;
66
use std::fmt;

src/test.rs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ use crate::test_util::{
99
assert_checkers_match, assert_equal, assert_outputs_match, location_insensitive_checker_for,
1010
naive_checker_for, opt_checker_for,
1111
};
12-
use polonius_engine::Algorithm;
12+
use polonius_engine::{Algorithm, Engine};
1313
use rustc_hash::FxHashMap;
1414
use std::error::Error;
1515
use std::path::Path;
1616

1717
fn test_facts(all_facts: &AllFacts, algorithms: &[Algorithm]) {
18-
let naive = Output::compute(all_facts, Algorithm::Naive, true);
18+
let naive = Output::compute(all_facts, Algorithm::Naive(Engine::Datafrog), true);
1919

2020
// Check that the "naive errors" are a subset of the "insensitive
2121
// ones".
@@ -111,7 +111,7 @@ macro_rules! tests {
111111

112112
#[test]
113113
fn datafrog_opt() -> Result<(), Box<dyn Error>> {
114-
test_fn($dir, $fn, Algorithm::DatafrogOpt)
114+
test_fn($dir, $fn, Algorithm::DatafrogOpt(Engine::Datafrog))
115115
}
116116
}
117117
)*
@@ -168,7 +168,7 @@ fn test_sensitive_passes_issue_47680() -> Result<(), Box<dyn Error>> {
168168
.join("main");
169169
let tables = &mut intern::InternerTables::new();
170170
let all_facts = tab_delim::load_tab_delimited_facts(tables, &facts_dir)?;
171-
let sensitive = Output::compute(&all_facts, Algorithm::DatafrogOpt, false);
171+
let sensitive = Output::compute(&all_facts, Algorithm::DatafrogOpt(Engine::Datafrog), false);
172172

173173
assert!(sensitive.errors.is_empty());
174174

@@ -201,15 +201,15 @@ fn no_subset_symmetries_exist() -> Result<(), Box<dyn Error>> {
201201
false
202202
};
203203

204-
let naive = Output::compute(&all_facts, Algorithm::Naive, true);
204+
let naive = Output::compute(&all_facts, Algorithm::Naive(Engine::Datafrog), true);
205205
assert!(!subset_symmetries_exist(&naive));
206206

207207
// FIXME: the issue-47680 dataset is suboptimal here as DatafrogOpt does not
208208
// produce subset symmetries for it. It does for clap, and it was used to manually verify
209209
// that the assert in verbose mode didn't trigger. Therefore, switch to this dataset
210210
// whenever it's fast enough to be enabled in tests, or somehow create a test facts program
211211
// or reduce it from clap.
212-
let opt = Output::compute(&all_facts, Algorithm::DatafrogOpt, true);
212+
let opt = Output::compute(&all_facts, Algorithm::DatafrogOpt(Engine::Datafrog), true);
213213
assert!(!subset_symmetries_exist(&opt));
214214
Ok(())
215215
}
@@ -323,8 +323,8 @@ fn smoke_test_errors() {
323323
let facts = tab_delim::load_tab_delimited_facts(tables, &facts_dir).expect("facts");
324324

325325
let location_insensitive = Output::compute(&facts, Algorithm::LocationInsensitive, true);
326-
let naive = Output::compute(&facts, Algorithm::Naive, true);
327-
let opt = Output::compute(&facts, Algorithm::DatafrogOpt, true);
326+
let naive = Output::compute(&facts, Algorithm::Naive(Engine::Datafrog), true);
327+
let opt = Output::compute(&facts, Algorithm::DatafrogOpt(Engine::Datafrog), true);
328328

329329
// We have to find errors with every analysis
330330
assert!(
@@ -399,7 +399,7 @@ fn var_live_in_single_block() {
399399
let mut tables = intern::InternerTables::new();
400400
let facts = parse_from_program(program, &mut tables).expect("Parsing failure");
401401

402-
let liveness = Output::compute(&facts, Algorithm::Naive, true).var_live_on_entry;
402+
let liveness = Output::compute(&facts, Algorithm::Naive(Engine::Datafrog), true).var_live_on_entry;
403403
println!("Registered liveness data: {:?}", liveness);
404404
for (point, variables) in liveness.iter() {
405405
println!("{:?} has live variables: {:?}", point, variables);
@@ -433,7 +433,7 @@ fn var_live_in_successor_propagates_to_predecessor() {
433433
let mut tables = intern::InternerTables::new();
434434
let facts = parse_from_program(program, &mut tables).expect("Parsing failure");
435435

436-
let liveness = Output::compute(&facts, Algorithm::Naive, true).var_live_on_entry;
436+
let liveness = Output::compute(&facts, Algorithm::Naive(Engine::Datafrog), true).var_live_on_entry;
437437
println!("Registered liveness data: {:?}", liveness);
438438
println!("CFG: {:?}", facts.cfg_edge);
439439
for (point, variables) in liveness.iter() {
@@ -470,7 +470,7 @@ fn var_live_in_successor_killed_by_reassignment() {
470470
let mut tables = intern::InternerTables::new();
471471
let facts = parse_from_program(program, &mut tables).expect("Parsing failure");
472472

473-
let result = Output::compute(&facts, Algorithm::Naive, true);
473+
let result = Output::compute(&facts, Algorithm::Naive(Engine::Datafrog), true);
474474
println!("result: {:#?}", result);
475475
let liveness = result.var_live_on_entry;
476476
println!("CFG: {:#?}", facts.cfg_edge);
@@ -531,7 +531,7 @@ fn var_drop_used_simple() {
531531
let mut tables = intern::InternerTables::new();
532532
let facts = parse_from_program(program, &mut tables).expect("Parsing failure");
533533

534-
let result = Output::compute(&facts, Algorithm::Naive, true);
534+
let result = Output::compute(&facts, Algorithm::Naive(Engine::Datafrog), true);
535535
println!("result: {:#?}", result);
536536
let liveness = result.var_drop_live_on_entry;
537537
println!("CFG: {:#?}", facts.cfg_edge);
@@ -573,7 +573,7 @@ fn var_drop_used_simple() {
573573
fn illegal_subset_error() {
574574
let program = r"
575575
placeholders { 'a, 'b }
576-
576+
577577
block B0 {
578578
// creates a transitive `'b: 'a` subset
579579
loan_issued_at('x, L0),
@@ -638,7 +638,7 @@ fn transitive_known_subset() {
638638
let program = r"
639639
placeholders { 'a, 'b, 'c }
640640
known_subsets { 'a: 'b, 'b: 'c }
641-
641+
642642
block B0 {
643643
loan_issued_at('x, L0),
644644
outlives('a: 'x),
@@ -670,7 +670,7 @@ fn transitive_illegal_subset_error() {
670670
let program = r"
671671
placeholders { 'a, 'b, 'c }
672672
known_subsets { 'a: 'b }
673-
673+
674674
block B0 {
675675
// this transitive `'a: 'b` subset is already known
676676
loan_issued_at('x, L0),
@@ -679,7 +679,7 @@ fn transitive_illegal_subset_error() {
679679
680680
// creates unknown transitive subsets:
681681
// - `'b: 'c`
682-
// - and therefore `'a: 'c`
682+
// - and therefore `'a: 'c`
683683
loan_issued_at('y, L1),
684684
outlives('b: 'y),
685685
outlives('y: 'c);
@@ -721,15 +721,15 @@ fn successes_in_subset_relations_dataset() {
721721
let tables = &mut intern::InternerTables::new();
722722
let facts = tab_delim::load_tab_delimited_facts(tables, &facts_dir).expect("facts");
723723

724-
let naive = Output::compute(&facts, Algorithm::Naive, true);
724+
let naive = Output::compute(&facts, Algorithm::Naive(Engine::Datafrog), true);
725725
assert!(naive.errors.is_empty());
726726
assert!(naive.subset_errors.is_empty());
727727

728728
let insensitive = Output::compute(&facts, Algorithm::LocationInsensitive, true);
729729
assert!(insensitive.errors.is_empty());
730730
assert!(insensitive.subset_errors.is_empty());
731731

732-
let opt = Output::compute(&facts, Algorithm::DatafrogOpt, true);
732+
let opt = Output::compute(&facts, Algorithm::DatafrogOpt(Engine::Datafrog), true);
733733
assert!(opt.errors.is_empty());
734734
assert!(opt.subset_errors.is_empty());
735735
}
@@ -746,7 +746,7 @@ fn errors_in_subset_relations_dataset() {
746746
let facts = tab_delim::load_tab_delimited_facts(tables, &facts_dir).expect("facts");
747747

748748
// this function has no illegal access errors, but one subset error, over 3 points
749-
let naive = Output::compute(&facts, Algorithm::Naive, true);
749+
let naive = Output::compute(&facts, Algorithm::Naive(Engine::Datafrog), true);
750750
assert!(naive.errors.is_empty());
751751
assert_eq!(naive.subset_errors.len(), 3);
752752

@@ -782,7 +782,7 @@ fn errors_in_subset_relations_dataset() {
782782
assert!(insensitive_subset_errors.contains(&expected_subset_error));
783783

784784
// And the optimized analysis results should be the same as the naive one's.
785-
let opt = Output::compute(&facts, Algorithm::Naive, true);
785+
let opt = Output::compute(&facts, Algorithm::Naive(Engine::Datafrog), true);
786786
assert_outputs_match(&naive, &opt);
787787
}
788788

@@ -802,7 +802,7 @@ fn successes_in_move_errors_dataset() {
802802
let tables = &mut intern::InternerTables::new();
803803
let facts = tab_delim::load_tab_delimited_facts(tables, &facts_dir).expect("facts");
804804

805-
let naive = Output::compute(&facts, Algorithm::Naive, true);
805+
let naive = Output::compute(&facts, Algorithm::Naive(Engine::Datafrog), true);
806806
assert!(naive.errors.is_empty());
807807
assert!(naive.subset_errors.is_empty());
808808
assert!(naive.move_errors.is_empty());
@@ -812,7 +812,7 @@ fn successes_in_move_errors_dataset() {
812812
assert!(insensitive.subset_errors.is_empty());
813813
assert!(insensitive.move_errors.is_empty());
814814

815-
let opt = Output::compute(&facts, Algorithm::DatafrogOpt, true);
815+
let opt = Output::compute(&facts, Algorithm::DatafrogOpt(Engine::Datafrog), true);
816816
assert!(opt.errors.is_empty());
817817
assert!(opt.subset_errors.is_empty());
818818
assert!(opt.move_errors.is_empty());
@@ -829,7 +829,7 @@ fn basic_move_error() {
829829
let tables = &mut intern::InternerTables::new();
830830
let facts = tab_delim::load_tab_delimited_facts(tables, &facts_dir).expect("facts");
831831

832-
let result = Output::compute(&facts, Algorithm::Naive, true);
832+
let result = Output::compute(&facts, Algorithm::Naive(Engine::Datafrog), true);
833833
assert!(result.errors.is_empty());
834834
assert!(result.subset_errors.is_empty());
835835

@@ -853,7 +853,7 @@ fn conditional_init() {
853853
let tables = &mut intern::InternerTables::new();
854854
let facts = tab_delim::load_tab_delimited_facts(tables, &facts_dir).expect("facts");
855855

856-
let result = Output::compute(&facts, Algorithm::Naive, true);
856+
let result = Output::compute(&facts, Algorithm::Naive(Engine::Datafrog), true);
857857
assert!(result.errors.is_empty());
858858
assert!(result.subset_errors.is_empty());
859859

src/test_util.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![cfg(test)]
22

3-
use polonius_engine::{Algorithm, AllFacts, Output};
3+
use polonius_engine::{Algorithm, AllFacts, Engine, Output};
44
use std::fmt::Debug;
55

66
use crate::facts::LocalFacts;
@@ -76,15 +76,15 @@ pub(crate) fn check_program(
7676
}
7777

7878
pub(crate) fn naive_checker_for(program: &str) -> FactChecker {
79-
check_program(program, Algorithm::Naive, true)
79+
check_program(program, Algorithm::Naive(Engine::Datafrog), true)
8080
}
8181

8282
pub(crate) fn location_insensitive_checker_for(program: &str) -> FactChecker {
8383
check_program(program, Algorithm::LocationInsensitive, true)
8484
}
8585

8686
pub(crate) fn opt_checker_for(program: &str) -> FactChecker {
87-
check_program(program, Algorithm::DatafrogOpt, true)
87+
check_program(program, Algorithm::DatafrogOpt(Engine::Datafrog), true)
8888
}
8989

9090
pub(crate) fn assert_checkers_match(checker_a: &FactChecker, checker_b: &FactChecker) {

0 commit comments

Comments
 (0)