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

Implement unsafe trait support (continued) #155

Merged
merged 11 commits into from
Nov 12, 2023
21 changes: 19 additions & 2 deletions crates/formality-check/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use anyhow::bail;

use fn_error_context::context;
use formality_core::Downcasted;
use formality_prove::Env;
use formality_prove::{Env, Safety};
use formality_rust::{
grammar::{
AssociatedTy, AssociatedTyBoundData, AssociatedTyValue, AssociatedTyValueBoundData, Fn,
Expand All @@ -19,7 +19,7 @@ use formality_types::{
impl super::Check<'_> {
#[context("check_trait_impl({v:?})")]
pub(super) fn check_trait_impl(&self, v: &TraitImpl) -> Fallible<()> {
let TraitImpl { binder } = v;
let TraitImpl { binder, safety } = v;

let mut env = Env::default();

Expand All @@ -45,6 +45,8 @@ impl super::Check<'_> {
trait_items,
} = trait_decl.binder.instantiate_with(&trait_ref.parameters)?;

self.check_safety_matches(&trait_decl.safety, safety)?;

for impl_item in &impl_items {
self.check_trait_impl_item(&env, &where_clauses, &trait_items, impl_item)?;
}
Expand All @@ -71,6 +73,21 @@ impl super::Check<'_> {
Ok(())
}

/// Validate `unsafe trait` and `unsafe impl` line up
fn check_safety_matches(&self, trait_decl: &Safety, trait_impl: &Safety) -> Fallible<()> {
match trait_decl {
Safety::Safe => anyhow::ensure!(
matches!(trait_impl, Safety::Safe),
"implementing the trait is not `unsafe`"
),
Safety::Unsafe => anyhow::ensure!(
matches!(trait_impl, Safety::Unsafe),
"the trait requires an `unsafe impl` declaration"
),
}
Ok(())
}

fn check_trait_impl_item(
&self,
env: &Env,
Expand Down
6 changes: 5 additions & 1 deletion crates/formality-check/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ use formality_types::grammar::Fallible;
impl super::Check<'_> {
#[context("check_trait({:?})", t.id)]
pub(super) fn check_trait(&self, t: &Trait) -> Fallible<()> {
let Trait { id: _, binder } = t;
let Trait {
safety: _,
id: _,
binder,
} = t;
let mut env = Env::default();

let TraitBoundData {
Expand Down
22 changes: 19 additions & 3 deletions crates/formality-prove/src/decls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,10 @@ impl Decls {

/// An "impl decl" indicates that a trait is implemented for a given set of types.
/// One "impl decl" is created for each impl in the Rust source.
#[term(impl $binder)]
#[term($?safety impl $binder)]
pub struct ImplDecl {
/// The safety this impl declares, which needs to match the implemented trait's safety.
pub safety: Safety,
/// The binder covers the generic variables from the impl
pub binder: Binder<ImplDeclBoundData>,
}
Expand All @@ -122,8 +124,11 @@ pub struct ImplDeclBoundData {

/// A declaration that some trait will *not* be implemented for a type; derived from negative impls
/// like `impl !Foo for Bar`.
#[term(impl $binder)]
#[term($?safety impl $binder)]
pub struct NegImplDecl {
/// The safety this negative impl declares
pub safety: Safety,

/// Binder comes the generics on the impl
pub binder: Binder<NegImplDeclBoundData>,
}
Expand All @@ -135,15 +140,26 @@ pub struct NegImplDeclBoundData {
pub where_clause: Wcs,
}

#[term]
#[derive(Default)]
pub enum Safety {
#[default]
Copy link
Contributor

Choose a reason for hiding this comment

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

I didn't know you could do this, nifty.

Safe,
Unsafe,
}

/// A "trait declaration" declares a trait that exists, its generics, and its where-clauses.
/// It doesn't capture the trait items, which will be transformed into other sorts of rules.
///
/// In Rust syntax, it covers the `trait Foo: Bar` part of the declaration, but not what appears in the `{...}`.
#[term(trait $id $binder)]
#[term($?safety trait $id $binder)]
pub struct TraitDecl {
/// The name of the trait
pub id: TraitId,

/// Whether the trait is `unsafe` or not
pub safety: Safety,

/// The binder here captures the generics of the trait; it always begins with a `Self` type.
pub binder: Binder<TraitDeclBoundData>,
}
Expand Down
10 changes: 7 additions & 3 deletions crates/formality-rust/src/grammar.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::sync::Arc;

use formality_core::{term, Upcast};
use formality_prove::Safety;
use formality_types::{
grammar::{
AdtId, AliasTy, AssociatedItemId, Binder, Const, CrateId, Fallible, FieldId, FnId, Lt,
Expand Down Expand Up @@ -160,8 +161,9 @@ pub struct Variant {
pub fields: Vec<Field>,
}

#[term(trait $id $binder)]
#[term($?safety trait $id $binder)]
pub struct Trait {
pub safety: Safety,
pub id: TraitId,
pub binder: TraitBinder<TraitBoundData>,
}
Expand Down Expand Up @@ -241,8 +243,9 @@ pub struct AssociatedTyBoundData {
pub where_clauses: Vec<WhereClause>,
}

#[term(impl $binder)]
#[term($?safety impl $binder)]
pub struct TraitImpl {
pub safety: Safety,
pub binder: Binder<TraitImplBoundData>,
}

Expand All @@ -267,8 +270,9 @@ impl TraitImplBoundData {
}
}

#[term(impl $binder)]
#[term($?safety impl $binder)]
pub struct NegTraitImpl {
pub safety: Safety,
pub binder: Binder<NegTraitImplBoundData>,
}

Expand Down
12 changes: 8 additions & 4 deletions crates/formality-rust/src/prove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl Crate {
self.items
.iter()
.flat_map(|item| match item {
CrateItem::Trait(Trait { id, binder }) => {
CrateItem::Trait(Trait { id, binder, safety }) => {
let (
vars,
TraitBoundData {
Expand All @@ -89,6 +89,7 @@ impl Crate {
},
) = binder.open();
Some(prove::TraitDecl {
safety: safety.clone(),
id: id.clone(),
binder: Binder::new(
vars,
Expand All @@ -110,7 +111,7 @@ impl Crate {
self.items
.iter()
.flat_map(|item| match item {
CrateItem::TraitImpl(TraitImpl { binder }) => {
CrateItem::TraitImpl(TraitImpl { binder, safety }) => {
let (
vars,
TraitImplBoundData {
Expand All @@ -122,6 +123,7 @@ impl Crate {
},
) = binder.open();
Some(prove::ImplDecl {
safety: safety.clone(),
binder: Binder::new(
vars,
prove::ImplDeclBoundData {
Expand All @@ -140,7 +142,7 @@ impl Crate {
self.items
.iter()
.flat_map(|item| match item {
CrateItem::NegTraitImpl(NegTraitImpl { binder }) => {
CrateItem::NegTraitImpl(NegTraitImpl { binder, safety }) => {
let (
vars,
NegTraitImplBoundData {
Expand All @@ -151,6 +153,7 @@ impl Crate {
},
) = binder.open();
Some(prove::NegImplDecl {
safety: safety.clone(),
binder: Binder::new(
vars,
prove::NegImplDeclBoundData {
Expand All @@ -169,7 +172,7 @@ impl Crate {
self.items
.iter()
.flat_map(|item| match item {
CrateItem::TraitImpl(TraitImpl { binder }) => {
CrateItem::TraitImpl(TraitImpl { binder, safety: _ }) => {
let (
impl_vars,
TraitImplBoundData {
Expand Down Expand Up @@ -226,6 +229,7 @@ impl Crate {
.iter()
.flat_map(|item| match item {
CrateItem::Trait(Trait {
safety: _,
id: trait_id,
binder,
}) => {
Expand Down