Skip to content

WIP: const effect #194

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

Draft
wants to merge 65 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
1bcdbdf
Currently passed normal trait
tiif Feb 4, 2025
699d272
Support const trait declaration
tiif Feb 5, 2025
3a2f82f
Support impl const trait in parser
tiif Feb 5, 2025
37c56c2
Failed attempt: requires a lot of other impl
tiif Feb 5, 2025
c83d3fb
Get constness in TraitRef somewhat work
tiif Feb 6, 2025
f7b3f47
Use ~const for maybe-const
tiif Feb 7, 2025
9521e7a
Add constness somewhere
tiif Feb 7, 2025
d73d4f3
Fix the test
tiif Feb 10, 2025
783a44d
Add const impl test
tiif Feb 10, 2025
79697f3
Add const trait impl check
tiif Feb 10, 2025
a59024b
Add ConstEffect
tiif Feb 17, 2025
1e2d04f
Add effect_items to TraitBoundData
tiif Feb 17, 2025
85edbe4
Add a bunch of todos for now
tiif Feb 17, 2025
fa35d87
Clean up a little bit
tiif Feb 17, 2025
02a0d15
Clean up
tiif Feb 17, 2025
4bf7bf5
Cleanup
tiif Feb 21, 2025
33f156f
Remove ConstEffect from impl item
tiif Feb 24, 2025
2e15fb3
Substitute all constness with effect, then remove the syntax check
tiif Feb 24, 2025
2cb02fd
Substitute ConstEffect with Effect
tiif Feb 24, 2025
44b770e
Remove const trait impl check
tiif Feb 24, 2025
ca7a72c
Fix test output
tiif Feb 24, 2025
4ae18d2
Add more stuff to Effect enum
tiif Feb 27, 2025
f8081a2
Shift comment
tiif Feb 27, 2025
16a0617
Move it as part of function
tiif Feb 28, 2025
683c3aa
Remove prove_effect
tiif Feb 28, 2025
0699cfc
Add EffectSubset predicate
tiif Feb 28, 2025
98f6ffc
Tweak the subset rule
tiif Feb 28, 2025
b64aa4c
Everything from 28Feb office hour
tiif Feb 28, 2025
3411c7c
Make everything compile
tiif Mar 5, 2025
04680a6
comments + newtype for DebonedPredicate
Mar 7, 2025
9793fba
add effects into DebonedPredicate (FIXME)
Mar 7, 2025
76e25e1
implement subset
Mar 7, 2025
a02b259
Fix some low-hanging fruits
tiif Mar 12, 2025
b4ce2d0
Fix more errors
tiif Mar 12, 2025
4354ad4
Add some assumptions to make some_atomic_effect works
tiif Mar 12, 2025
f36ca3e
Use prove_effect_subset in prove_wc
tiif Mar 12, 2025
862567b
Make everything compiles
tiif Mar 12, 2025
a5b833e
Run fmt
tiif Mar 12, 2025
cead2c3
Run more fmt
tiif Mar 12, 2025
c7f359a
Move EffectSubset from Predicate to Relation
tiif Mar 13, 2025
6192d81
Change the premises in some_atomic_effect
tiif Mar 14, 2025
2135078
add comments to prove_eq
Mar 14, 2025
fc6fe5f
use prove_after (horray!)
Mar 14, 2025
d5378b7
WIP add supereffects
Mar 14, 2025
c5cb3c9
Make it compile
tiif Mar 18, 2025
38b6143
Fix rebase error
tiif Mar 18, 2025
4593842
Add test for effect subset
tiif Mar 19, 2025
8627b04
Run update expect
tiif Mar 19, 2025
84d8fb9
Add test for const trait
tiif Mar 20, 2025
fb7050e
fmt
tiif Mar 20, 2025
19b3367
move to externalized test
Mar 21, 2025
f26d080
fix test
Mar 21, 2025
3d35cce
Use better syntax for the effect subset tests
tiif Mar 26, 2025
2ef0c7d
Clean up
tiif Mar 26, 2025
aaca632
Add effect to where-clause data
tiif Mar 26, 2025
7790d3b
Add test with effect that did nothing yet
tiif Mar 26, 2025
d503eb3
Clean up
tiif Mar 26, 2025
7aac3e1
Add FnBodyWithEffect
tiif Mar 26, 2025
e6b125d
changes
Mar 29, 2025
70a6bdb
unfortunate version that works
tiif Mar 31, 2025
c152fe9
Added more test and make the grammar slighly better
tiif Apr 1, 2025
c1644ae
use the same keyword
tiif Apr 1, 2025
4db5c86
Add rules for associated effect
tiif Apr 4, 2025
8fb16ad
Add tests for associated effect
tiif Apr 7, 2025
49d11e3
Fix the tests
tiif Apr 8, 2025
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
36 changes: 33 additions & 3 deletions crates/formality-check/src/fns.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use formality_prove::Env;
use formality_rust::{
grammar::{Fn, FnBoundData},
grammar::{Fn, FnBody, FnBoundData, MaybeFnBody},
prove::ToWcs,
};
use formality_types::grammar::{Fallible, Wcs};
use formality_types::grammar::{Fallible, Relation, Wcs};

use crate::Check;

Expand All @@ -29,7 +29,8 @@ impl Check<'_> {
input_tys,
output_ty,
where_clauses,
body: _,
effect: declared_effect,
body,
} = env.instantiate_universally(binder);

let fn_assumptions: Wcs = (in_assumptions, &where_clauses).to_wcs();
Expand All @@ -42,6 +43,35 @@ impl Check<'_> {

self.prove_goal(&env, &fn_assumptions, output_ty.well_formed())?;

// prove that the body type checks (if present)
match body {
MaybeFnBody::NoFnBody => {
// No fn body: this occurs e.g. for the fn `bar`
// in `trait Foo { fn bar(); }`. No check required.
}
MaybeFnBody::FnBody(fn_body) => match fn_body {
FnBody::TrustedFnBody => {
// Trusted: just assume it's ok, useful for tests
// to indicate things we don't want to think about.
}
FnBody::MirFnBody(_mir_fn_body) => {
// TODO: this nikomatsakis is theoretically working on removing
// though he has not made much time for it.
unimplemented!()
}
FnBody::FnBodyWithEffect(body_effect) => {
// Useful for tests: declares "some body" with the
// given effect. So we need to check that the effect
// is in bounds of the declaration.
self.prove_goal(
&env,
&fn_assumptions,
Relation::EffectSubset(body_effect, declared_effect)
)?;
}
}
}

Ok(())
}
}
71 changes: 68 additions & 3 deletions crates/formality-check/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ use formality_rust::{
},
prove::ToWcs,
};
use formality_types::grammar::AtomicEffect::Runtime;
use formality_types::{
grammar::{Binder, Fallible, Relation, Substitution, Wcs},
grammar::{Binder, Effect, Fallible, Relation, Substitution, Wcs},
rust::Term,
};

Expand All @@ -24,14 +25,15 @@ impl super::Check<'_> {
let mut env = Env::default();

let TraitImplBoundData {
effect,
trait_id,
self_ty,
trait_parameters,
where_clauses,
impl_items,
} = env.instantiate_universally(binder);

let trait_ref = trait_id.with(self_ty, trait_parameters);
let trait_ref = trait_id.with(&effect, self_ty, trait_parameters);

self.prove_where_clauses_well_formed(&env, &where_clauses, &where_clauses)?;

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

self.check_safety_matches(trait_decl, trait_impl)?;
Expand All @@ -67,7 +70,7 @@ impl super::Check<'_> {
where_clauses,
} = env.instantiate_universally(binder);

let trait_ref = trait_id.with(self_ty, trait_parameters);
let trait_ref = trait_id.with(&Effect::Atomic(Runtime), self_ty, trait_parameters);

// Negative impls are always safe (rustc E0198) regardless of the trait's safety.
if *safety == Safety::Unsafe {
Expand Down Expand Up @@ -115,6 +118,44 @@ impl super::Check<'_> {
}
}

/// Check that function `ii_fn` that appears in an impl is valid.
/// This includes the core check from [`Self::check_fn`][],
/// but also additional checks to ensure that the signature in the impl
/// matches what is declared in the trait.
///
/// # Example
///
/// Suppose we are checking `<Cup<L> as Potable>::drink` in this example...
///
/// ```rust,ignore
/// trait Potable {
/// fn drink(&self); // `trait_items` includes these fns
/// fn smell(&self); //
/// }
///
/// struct Water;
/// impl Potable for Water {
/// fn drink(&self) {}
/// fn smell(&self) {}
/// }
///
/// struct Cup<L>;
/// impl<L> Potable for Cup<L> // <-- env has `L` in scope
/// where
/// L: Potable, // <-- `impl_assumptions`
/// {
/// fn drink(&self) {} // <-- `ii_fn`
/// fn smell(&self) {} // not currently being checked
/// }
/// ```
///
/// # Parameters
///
/// * `env`, the environment from the impl header
/// * `impl_assumptions`, where-clauses declared on the impl
/// * `trait_items`, items declared in the trait that is being implemented
/// (we search this to find the corresponding declaration of the method)
/// * `ii_fn`, the fn as declared in the impl
fn check_fn_in_impl(
&self,
env: &Env,
Expand Down Expand Up @@ -143,16 +184,21 @@ impl super::Check<'_> {

let mut env = env.clone();
let (
// ii_: the signature of the function as found in the impl item (ii)
FnBoundData {
input_tys: ii_input_tys,
output_ty: ii_output_ty,
where_clauses: ii_where_clauses,
effect: ii_effect,
body: _,
},

// ti_: the signature of the function as found in the trait item (ti)
FnBoundData {
input_tys: ti_input_tys,
output_ty: ti_output_ty,
where_clauses: ti_where_clauses,
effect: ti_effect,
body: _,
},
) = env.instantiate_universally(&self.merge_binders(&ii_fn.binder, &ti_fn.binder)?);
Expand All @@ -163,6 +209,7 @@ impl super::Check<'_> {
&ii_where_clauses,
)?;

// Must have same number of arguments as declared in the trait
if ii_input_tys.len() != ti_input_tys.len() {
bail!(
"impl has {} function arguments but trait has {} function arguments",
Expand All @@ -171,6 +218,16 @@ impl super::Check<'_> {
)
}

// The type `ii_input_ty` of argument `i` in the impl
// must be a supertype of the correspond type `ti_input_ty` from the trait.
//
// e.g. if you have `fn foo(arg: for<'a> fn(&u8))` in the trait
// you can have `fn foo(arg: fn(&'static u8))` in the impl.
//
// Why? Trait guarantees you are receiving a fn that can take
// an `&u8` in any lifetime. Impl is saying "I only need a fn that can
// take `&u8` in static lifetime", which is more narrow, and that's ok,
// because a functon that can handle any lifetime can also handle static.
for (ii_input_ty, ti_input_ty) in ii_input_tys.iter().zip(&ti_input_tys) {
self.prove_goal(
&env,
Expand All @@ -179,12 +236,20 @@ impl super::Check<'_> {
)?;
}

// Impl must return a subtype of the return type from the trait.
self.prove_goal(
&env,
(&impl_assumptions, &ii_where_clauses),
Relation::sub(ii_output_ty, ti_output_ty),
)?;

// Impl must not have more effects than what are declared in the trait.
self.prove_goal(
&env,
(&impl_assumptions, &ii_where_clauses),
Relation::EffectSubset(ii_effect, ti_effect),
)?;

Ok(())
}

Expand Down
2 changes: 2 additions & 0 deletions crates/formality-check/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ impl super::Check<'_> {
safety: _,
id: _,
binder,
effect: _,
} = t;
let mut env = Env::default();

let TraitBoundData {
where_clauses,
trait_items,
effect_items: _,
} = env.instantiate_universally(&binder.explicit_binder);

self.check_trait_items_have_unique_names(&trait_items)?;
Expand Down
11 changes: 7 additions & 4 deletions crates/formality-prove/src/decls.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use formality_core::{set, Set, Upcast};
use formality_macros::term;
use formality_types::grammar::Effect;
use formality_types::grammar::{
AdtId, AliasName, AliasTy, Binder, Parameter, Predicate, Relation, TraitId, TraitRef, Ty, Wc,
Wcs,
AdtId, AliasName, AliasTy, AtomicEffect, Binder, Parameter, Predicate, Relation, TraitId,
TraitRef, Ty, Wc, Wcs,
};

#[term]
Expand Down Expand Up @@ -153,8 +154,9 @@ pub enum Safety {
/// 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($?safety trait $id $binder)]
#[term($?effect $?safety trait $id $binder)]
pub struct TraitDecl {
pub effect: Effect,
/// The name of the trait
pub id: TraitId,

Expand Down Expand Up @@ -187,6 +189,7 @@ impl TraitDecl {
is_supertrait(self_var, binder.peek())
}
formality_types::grammar::WcData::Implies(_, c) => is_supertrait(self_var, c),
formality_types::grammar::WcData::EffectSubset(_, _) => todo!(),
}
}

Expand All @@ -197,7 +200,7 @@ impl TraitDecl {
binder: Binder::new(
&variables,
TraitInvariantBoundData {
trait_ref: TraitRef::new(&self.id, &variables),
trait_ref: TraitRef::new(AtomicEffect::Runtime, &self.id, &variables),
where_clause,
},
),
Expand Down
1 change: 1 addition & 0 deletions crates/formality-prove/src/prove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod is_local;
mod minimize;
mod negation;
mod prove_after;
mod prove_effect_subset;
mod prove_eq;
mod prove_normalize;
mod prove_via;
Expand Down
Loading
Loading