Skip to content
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

Add integration test for unwind safety #407

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
160 changes: 160 additions & 0 deletions tests/integration/panic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we actually want to wrap an actual database.

panic: bool,
}

impl MockDatabase {
pub fn with(program_text: &str, solver_choice: SolverChoice) -> Self {
Self {
chalk_db: ChalkDatabase::with(program_text, solver_choice),
panic: false,
}
}

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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For these methods, I was thinking just hardcode what we want to return for trait Foo and struct Bar

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! {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, we won't actually "lower" anything, it'll just be hardcoded.

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! {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here. We aren't going to "lower" this, we'll just manually create the UCanonicalGoalInEnvironment for Foo: Bar

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());
Comment on lines +150 to +159
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is exactly what I was thinking :)

}
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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we want to change/use anything here.

(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