Skip to content

Commit 570e95f

Browse files
yoshuawuytslqd
authored andcommitted
implement unsafe trait support
1 parent b7fd961 commit 570e95f

File tree

5 files changed

+58
-13
lines changed

5 files changed

+58
-13
lines changed

crates/formality-check/src/impls.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use anyhow::bail;
22

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

2424
let mut env = Env::default();
2525

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

48+
self.check_safety_matches(&trait_decl.safety, safety)?;
49+
4850
for impl_item in &impl_items {
4951
self.check_trait_impl_item(&env, &where_clauses, &trait_items, impl_item)?;
5052
}
@@ -71,6 +73,21 @@ impl super::Check<'_> {
7173
Ok(())
7274
}
7375

76+
/// Validate `unsafe trait` and `unsafe impl` line up
77+
fn check_safety_matches(&self, trait_decl: &Safety, trait_impl: &Safety) -> Fallible<()> {
78+
match trait_decl {
79+
Safety::Safe => anyhow::ensure!(
80+
matches!(trait_impl, Safety::Safe),
81+
"implementing the trait is not `unsafe`"
82+
),
83+
Safety::Unsafe => anyhow::ensure!(
84+
matches!(trait_impl, Safety::Unsafe),
85+
"the trait requires an `unsafe impl` declaration"
86+
),
87+
}
88+
Ok(())
89+
}
90+
7491
fn check_trait_impl_item(
7592
&self,
7693
env: &Env,

crates/formality-check/src/traits.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ use formality_types::grammar::Fallible;
88
impl super::Check<'_> {
99
#[context("check_trait({:?})", t.id)]
1010
pub(super) fn check_trait(&self, t: &Trait) -> Fallible<()> {
11-
let Trait { id: _, binder } = t;
11+
let Trait {
12+
safety: _,
13+
id: _,
14+
binder,
15+
} = t;
1216
let mut env = Env::default();
1317

1418
let TraitBoundData {

crates/formality-prove/src/decls.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,10 @@ impl Decls {
104104

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

123125
/// A declaration that some trait will *not* be implemented for a type; derived from negative impls
124126
/// like `impl !Foo for Bar`.
125-
#[term(impl $binder)]
127+
#[term($?safety impl $binder)]
126128
pub struct NegImplDecl {
129+
/// The safety this negative impl declares
130+
pub safety: Safety,
131+
127132
/// Binder comes the generics on the impl
128133
pub binder: Binder<NegImplDeclBoundData>,
129134
}
@@ -135,15 +140,26 @@ pub struct NegImplDeclBoundData {
135140
pub where_clause: Wcs,
136141
}
137142

143+
#[term]
144+
#[derive(Default)]
145+
pub enum Safety {
146+
#[default]
147+
Safe,
148+
Unsafe,
149+
}
150+
138151
/// A "trait declaration" declares a trait that exists, its generics, and its where-clauses.
139152
/// It doesn't capture the trait items, which will be transformed into other sorts of rules.
140153
///
141154
/// In Rust syntax, it covers the `trait Foo: Bar` part of the declaration, but not what appears in the `{...}`.
142-
#[term(trait $id $binder)]
155+
#[term($?safety trait $id $binder)]
143156
pub struct TraitDecl {
144157
/// The name of the trait
145158
pub id: TraitId,
146159

160+
/// Whether the trait is `unsafe` or not
161+
pub safety: Safety,
162+
147163
/// The binder here captures the generics of the trait; it always begins with a `Self` type.
148164
pub binder: Binder<TraitDeclBoundData>,
149165
}

crates/formality-rust/src/grammar.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::sync::Arc;
22

33
use formality_core::{term, Upcast};
4+
use formality_prove::Safety;
45
use formality_types::{
56
grammar::{
67
AdtId, AliasTy, AssociatedItemId, Binder, Const, CrateId, Fallible, FieldId, FnId, Lt,
@@ -160,8 +161,9 @@ pub struct Variant {
160161
pub fields: Vec<Field>,
161162
}
162163

163-
#[term(trait $id $binder)]
164+
#[term($?safety trait $id $binder)]
164165
pub struct Trait {
166+
pub safety: Safety,
165167
pub id: TraitId,
166168
pub binder: TraitBinder<TraitBoundData>,
167169
}
@@ -241,8 +243,9 @@ pub struct AssociatedTyBoundData {
241243
pub where_clauses: Vec<WhereClause>,
242244
}
243245

244-
#[term(impl $binder)]
246+
#[term($?safety impl $binder)]
245247
pub struct TraitImpl {
248+
pub safety: Safety,
246249
pub binder: Binder<TraitImplBoundData>,
247250
}
248251

@@ -267,8 +270,9 @@ impl TraitImplBoundData {
267270
}
268271
}
269272

270-
#[term(impl $binder)]
273+
#[term($?safety impl $binder)]
271274
pub struct NegTraitImpl {
275+
pub safety: Safety,
272276
pub binder: Binder<NegTraitImplBoundData>,
273277
}
274278

crates/formality-rust/src/prove.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ impl Crate {
8080
self.items
8181
.iter()
8282
.flat_map(|item| match item {
83-
CrateItem::Trait(Trait { id, binder }) => {
83+
CrateItem::Trait(Trait { id, binder, safety }) => {
8484
let (
8585
vars,
8686
TraitBoundData {
@@ -89,6 +89,7 @@ impl Crate {
8989
},
9090
) = binder.open();
9191
Some(prove::TraitDecl {
92+
safety: safety.clone(),
9293
id: id.clone(),
9394
binder: Binder::new(
9495
vars,
@@ -110,7 +111,7 @@ impl Crate {
110111
self.items
111112
.iter()
112113
.flat_map(|item| match item {
113-
CrateItem::TraitImpl(TraitImpl { binder }) => {
114+
CrateItem::TraitImpl(TraitImpl { binder, safety }) => {
114115
let (
115116
vars,
116117
TraitImplBoundData {
@@ -122,6 +123,7 @@ impl Crate {
122123
},
123124
) = binder.open();
124125
Some(prove::ImplDecl {
126+
safety: safety.clone(),
125127
binder: Binder::new(
126128
vars,
127129
prove::ImplDeclBoundData {
@@ -140,7 +142,7 @@ impl Crate {
140142
self.items
141143
.iter()
142144
.flat_map(|item| match item {
143-
CrateItem::NegTraitImpl(NegTraitImpl { binder }) => {
145+
CrateItem::NegTraitImpl(NegTraitImpl { binder, safety }) => {
144146
let (
145147
vars,
146148
NegTraitImplBoundData {
@@ -151,6 +153,7 @@ impl Crate {
151153
},
152154
) = binder.open();
153155
Some(prove::NegImplDecl {
156+
safety: safety.clone(),
154157
binder: Binder::new(
155158
vars,
156159
prove::NegImplDeclBoundData {
@@ -169,7 +172,7 @@ impl Crate {
169172
self.items
170173
.iter()
171174
.flat_map(|item| match item {
172-
CrateItem::TraitImpl(TraitImpl { binder }) => {
175+
CrateItem::TraitImpl(TraitImpl { binder, safety: _ }) => {
173176
let (
174177
impl_vars,
175178
TraitImplBoundData {
@@ -226,6 +229,7 @@ impl Crate {
226229
.iter()
227230
.flat_map(|item| match item {
228231
CrateItem::Trait(Trait {
232+
safety: _,
229233
id: trait_id,
230234
binder,
231235
}) => {

0 commit comments

Comments
 (0)