Skip to content

Commit

Permalink
Add integration test for unwind safety
Browse files Browse the repository at this point in the history
  • Loading branch information
marmeladema committed Apr 19, 2020
1 parent 27a9850 commit 8051ba8
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 4 deletions.
1 change: 1 addition & 0 deletions tests/integration/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod panic;
161 changes: 161 additions & 0 deletions tests/integration/panic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use chalk_integration::db::ChalkDatabase;
use chalk_integration::query::LoweringDatabase;
use chalk_ir::interner::ChalkIr;
use chalk_ir::AssocTypeId;
use chalk_ir::Goal;
use chalk_ir::ImplId;
use chalk_ir::InEnvironment;
use chalk_ir::OpaqueTyId;
use chalk_ir::Parameter;
use chalk_ir::ProgramClause;
use chalk_ir::StructId;
use chalk_ir::TraitId;
use chalk_ir::TypeName;
use chalk_ir::UCanonical;
use chalk_rust_ir::AssociatedTyDatum;
use chalk_rust_ir::AssociatedTyValue;
use chalk_rust_ir::AssociatedTyValueId;
use chalk_rust_ir::ImplDatum;
use chalk_rust_ir::OpaqueTyDatum;
use chalk_rust_ir::StructDatum;
use chalk_rust_ir::TraitDatum;
use chalk_rust_ir::WellKnownTrait;
use chalk_solve::RustIrDatabase;
use chalk_solve::Solution;
use chalk_solve::SolverChoice;
use std::sync::Arc;

#[derive(Debug, Default)]
struct MockDatabase {
chalk_db: ChalkDatabase,
panic: bool,
}

impl MockDatabase {
pub fn with(program_text: &str, solver_choice: SolverChoice) -> Self {
let mut db = Self::default();
db.chalk_db
.set_program_text(Arc::new(program_text.to_string()));
db.chalk_db.set_solver_choice(solver_choice);
db
}

pub fn solve(
&self,
goal: &UCanonical<InEnvironment<Goal<ChalkIr>>>,
) -> Option<Solution<ChalkIr>> {
let solver = self.chalk_db.solver();
let solution = solver.lock().unwrap().solve(self, goal);
solution
}
}

impl RustIrDatabase<ChalkIr> for MockDatabase {
fn custom_clauses(&self) -> Vec<ProgramClause<ChalkIr>> {
if self.panic {
unimplemented!()
} else {
self.chalk_db.custom_clauses()
}
}

fn associated_ty_data(&self, ty: AssocTypeId<ChalkIr>) -> Arc<AssociatedTyDatum<ChalkIr>> {
self.chalk_db.associated_ty_data(ty)
}

fn trait_datum(&self, id: TraitId<ChalkIr>) -> Arc<TraitDatum<ChalkIr>> {
self.chalk_db.trait_datum(id)
}

fn impl_datum(&self, id: ImplId<ChalkIr>) -> Arc<ImplDatum<ChalkIr>> {
self.chalk_db.impl_datum(id)
}

fn associated_ty_value(
&self,
id: AssociatedTyValueId<ChalkIr>,
) -> Arc<AssociatedTyValue<ChalkIr>> {
self.chalk_db.associated_ty_value(id)
}

fn opaque_ty_data(&self, id: OpaqueTyId<ChalkIr>) -> Arc<OpaqueTyDatum<ChalkIr>> {
self.chalk_db.opaque_ty_data(id)
}

fn struct_datum(&self, id: StructId<ChalkIr>) -> Arc<StructDatum<ChalkIr>> {
self.chalk_db.struct_datum(id)
}

fn as_struct_id(&self, type_name: &TypeName<ChalkIr>) -> Option<StructId<ChalkIr>> {
self.chalk_db.as_struct_id(type_name)
}

fn impls_for_trait(
&self,
trait_id: TraitId<ChalkIr>,
parameters: &[Parameter<ChalkIr>],
) -> Vec<ImplId<ChalkIr>> {
self.chalk_db.impls_for_trait(trait_id, parameters)
}

fn local_impls_to_coherence_check(&self, trait_id: TraitId<ChalkIr>) -> Vec<ImplId<ChalkIr>> {
self.chalk_db.local_impls_to_coherence_check(trait_id)
}

fn impl_provided_for(
&self,
auto_trait_id: TraitId<ChalkIr>,
struct_id: StructId<ChalkIr>,
) -> bool {
self.chalk_db.impl_provided_for(auto_trait_id, struct_id)
}

fn well_known_trait_id(&self, well_known_trait: WellKnownTrait) -> Option<TraitId<ChalkIr>> {
self.chalk_db.well_known_trait_id(well_known_trait)
}

fn interner(&self) -> &ChalkIr {
&ChalkIr
}
}

#[test]
fn unwind_safety() {
use self::MockDatabase;
use chalk_integration::lowering::LowerGoal;
use chalk_integration::query::LoweringDatabase;
use chalk_solve::ext::GoalExt;
use std::panic;

// lower program
let mut db = lower_program_with_db! {
program {
struct Foo { }
trait Bar { }
impl Bar for Foo { }
}
database MockDatabase
};

let program = db.chalk_db.checked_program().unwrap();

// lower goal
let goal = lower_goal! {
goal {
Foo: Bar
}
program &*program
};
let peeled_goal = goal.into_peeled_goal(db.interner());

// solve goal but this will panic
db.panic = true;
let result = panic::catch_unwind(|| {
db.solve(&peeled_goal);
});
assert!(result.is_err() == true);

// solve again but without panicking this time
db.panic = false;
assert!(db.solve(&peeled_goal).is_some());
}
2 changes: 2 additions & 0 deletions tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ mod test_util;
mod test;

mod lowering;

mod integration;
28 changes: 24 additions & 4 deletions tests/test_util.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
#![allow(unused_macros)]

macro_rules! lowering_success {
(program $program:tt) => {
macro_rules! lower_program_with_db {
(program $program:tt database $database:ty) => {{
let program_text = stringify!($program);
assert!(program_text.starts_with("{"));
assert!(program_text.ends_with("}"));
let result = chalk_integration::db::ChalkDatabase::with(
<$database>::with(
&program_text[1..program_text.len() - 1],
chalk_solve::SolverChoice::default(),
)
.checked_program();
}};
}

macro_rules! lower_goal {
(goal $goal:tt program $program:expr) => {{
let goal_text = stringify!($goal);
assert!(goal_text.starts_with("{"));
assert!(goal_text.ends_with("}"));
chalk_parse::parse_goal(&goal_text[1..goal_text.len() - 1])
.unwrap()
.lower($program)
.unwrap()
}};
}

macro_rules! lowering_success {
(program $program:tt) => {
let result = lower_program_with_db!(
program $program
database chalk_integration::db::ChalkDatabase
).checked_program();
if let Err(ref e) = result {
println!("lowering error: {}", e);
}
Expand Down

0 comments on commit 8051ba8

Please sign in to comment.