Skip to content

Commit afd30d2

Browse files
committed
support one-off judgments in type check
The idea is that judgment functions correspond to "Random Rust Code" in the compiler and Predicates/Relations are limited to things that can appear in where-clauses (explicitly or implicitly). The `formality_prove` module then roughly corresponds to the trait system + type inference logic in rustc.
1 parent 33d8a48 commit afd30d2

File tree

11 files changed

+108
-101
lines changed

11 files changed

+108
-101
lines changed

crates/formality-check/src/lib.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
use std::{collections::VecDeque, fmt::Debug};
44

55
use anyhow::bail;
6-
use formality_core::Set;
7-
use formality_prove::{is_definitely_not_proveable, Decls, Env};
6+
use formality_core::{ProvenSet, Set};
7+
use formality_prove::{is_definitely_not_proveable, Constraints, Decls, Env};
88
use formality_rust::{
99
grammar::{Crate, CrateItem, Program, Test, TestBoundData},
1010
prove::ToWcs,
1111
};
12-
use formality_types::grammar::{CrateId, Fallible, Wcs};
12+
use formality_types::{
13+
grammar::{CrateId, Fallible, Wcs},
14+
rust::Visit,
15+
};
1316

1417
mod mini_rust_check;
1518

@@ -134,18 +137,36 @@ impl Check<'_> {
134137
goal: impl ToWcs + Debug,
135138
) -> Fallible<()> {
136139
let goal: Wcs = goal.to_wcs();
140+
self.prove_judgment(env, assumptions, goal.to_wcs(), formality_prove::prove)
141+
}
142+
143+
fn prove_judgment<G>(
144+
&self,
145+
env: &Env,
146+
assumptions: impl ToWcs,
147+
goal: G,
148+
judgment_fn: impl FnOnce(Decls, Env, Wcs, G) -> ProvenSet<Constraints>,
149+
) -> Fallible<()>
150+
where
151+
G: Debug + Visit + Clone,
152+
{
137153
let assumptions: Wcs = assumptions.to_wcs();
138154

139155
assert!(env.only_universal_variables());
140156
assert!(env.encloses((&assumptions, &goal)));
141157

142-
let cs = formality_prove::prove(self.decls, env, &assumptions, &goal);
158+
let cs = judgment_fn(
159+
self.decls.clone(),
160+
env.clone(),
161+
assumptions.clone(),
162+
goal.clone(),
163+
);
143164
let cs = cs.into_set()?;
144165
if cs.iter().any(|c| c.unconditionally_true()) {
145166
return Ok(());
146167
}
147168

148-
bail!("failed to prove {goal:?} given {assumptions:?}, got {cs:?}")
169+
bail!("failed to prove `{goal:?}` given `{assumptions:?}`: got {cs:?}")
149170
}
150171

151172
#[tracing::instrument(level = "Debug", skip(self, assumptions, goal))]

crates/formality-check/src/mini_rust_check.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
use std::iter::zip;
22

3-
use formality_core::{Fallible, Map, Upcast};
4-
use formality_prove::Env;
3+
use formality_core::{judgment_fn, Fallible, Map, Upcast};
4+
use formality_prove::{prove_normalize, Constraints, Decls, Env};
55
use formality_rust::grammar::minirust::ArgumentExpression::{ByValue, InPlace};
66
use formality_rust::grammar::minirust::PlaceExpression::Local;
77
use formality_rust::grammar::minirust::ValueExpression::{Constant, Fn, Load};
88
use formality_rust::grammar::minirust::{
99
self, ArgumentExpression, BasicBlock, BbId, LocalId, PlaceExpression, ValueExpression,
1010
};
1111
use formality_rust::grammar::FnBoundData;
12-
use formality_types::grammar::{CrateId, FnId};
12+
use formality_types::grammar::{CrateId, FnId, Parameter, RigidName, RigidTy};
1313
use formality_types::grammar::{Relation, Ty, Wcs};
1414

1515
use crate::{Check, CrateItem};
@@ -228,7 +228,7 @@ impl Check<'_> {
228228
// Check if the value is well-formed.
229229
let value_ty = self.check_value(typeck_env, switch_value).unwrap();
230230

231-
self.prove_goal(&typeck_env.env, Wcs::default(), value_ty.is_int())?;
231+
self.prove_judgment(&typeck_env.env, &fn_assumptions, value_ty, ty_is_int)?;
232232

233233
// Ensure all bbid are valid.
234234
for switch_target in switch_targets {
@@ -371,3 +371,34 @@ struct TypeckEnv {
371371
/// declared in the current crate.
372372
crate_id: CrateId,
373373
}
374+
375+
judgment_fn! {
376+
fn ty_is_int(
377+
_decls: Decls,
378+
env: Env,
379+
assumptions: Wcs,
380+
ty: Parameter,
381+
) => Constraints {
382+
debug(assumptions, ty, env)
383+
// If the type that we are currently checking is rigid, check if it is an int.
384+
// If the type can be normalized, normalize until rigid then check if it is an int.
385+
// For the rest of the case, it should fail.
386+
387+
(
388+
(prove_normalize(&decl, &env, &assumptions, ty) => (c1, p))
389+
(let assumptions = c1.substitution().apply(&assumptions))
390+
(ty_is_int(&decl, &env, assumptions, p) => c2)
391+
----------------------------- ("alias_ty is int")
392+
(ty_is_int(decl, env, assumptions, ty) => c2)
393+
)
394+
395+
(
396+
(if id.is_int())
397+
----------------------------- ("rigid_ty is int")
398+
(ty_is_int(_decls, _env, _assumptions, RigidTy {name: RigidName::ScalarId(id), parameters: _}) => Constraints::none(env))
399+
)
400+
401+
}
402+
}
403+
404+
mod test;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Decide how to do this test -- should it be a unit test?
2+
// Can it be an integration test?
3+
4+
// const TEST_TY_IS_INT: &str = "[
5+
// crate test {
6+
// trait Id {
7+
// type This: [];
8+
// }
9+
10+
// impl<ty T> Id for T {
11+
// type This = T;
12+
// }
13+
// }
14+
// ]";
15+
16+
// #[test]
17+
// fn test_ty_is_int() {
18+
// test_where_clause(
19+
// TEST_TY_IS_INT,
20+
// "{} => { <u16 as Id>::This = u16 }",
21+
// )
22+
// .assert_ok(expect_test::expect!["{Constraints { env: Env { variables: [], bias: Soundness, pending: [] }, known_true: true, substitution: {} }}"]);
23+
24+
// test_where_clause(
25+
// TEST_TY_IS_INT,
26+
// "{} => { @is_int(<u16 as Id>::This) }",
27+
// )
28+
// .assert_ok(expect_test::expect!["{Constraints { env: Env { variables: [], bias: Soundness, pending: [] }, known_true: true, substitution: {} }}"]);
29+
// }

crates/formality-prove/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
//! This crate contains the trait proving + type inference logic.
2+
//! It correpsonds loosely to the `InferenceContext` and trait solving (fulfillment context, etc)
3+
//! in the Rust compiler.
4+
//!
5+
//! The base operations we export are:
6+
//!
7+
//! * [`prove`][] -- prove a set of where-clauses to be true
8+
//! * [`prove_normalize`][] -- normalize a type one step (typically used in a recursive setup)
9+
110
// Defines the language used by derive(term) and friends.
211
use formality_types::rust::FormalityLang;
312

@@ -7,6 +16,7 @@ mod prove;
716

817
pub use decls::*;
918
pub use prove::prove;
19+
pub use prove::prove_normalize::prove_normalize;
1020
pub use prove::Constraints;
1121
pub use prove::{is_definitely_not_proveable, may_not_be_provable, negation_via_failure};
1222
pub use prove::{Bias, Env};

crates/formality-prove/src/prove.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
mod combinators;
22
mod constraints;
33
mod env;
4-
mod is_int;
54
mod is_local;
65
mod minimize;
76
mod negation;
87
mod prove_after;
98
mod prove_eq;
10-
mod prove_normalize;
9+
pub mod prove_normalize;
1110
mod prove_via;
1211
mod prove_wc;
1312
mod prove_wc_list;

crates/formality-prove/src/prove/is_int.rs

Lines changed: 0 additions & 38 deletions
This file was deleted.

crates/formality-prove/src/prove/prove_normalize.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ use crate::{
1414
use super::constraints::Constraints;
1515

1616
judgment_fn! {
17+
/// Normalize `p` one step, returning a set of constraints and a new parameter `q` that is
18+
/// semantically equivalent to `p`. e.g., if p is `<Vec<T> as IntoIterator>::Item`, this would
19+
/// return `T`.
1720
pub fn prove_normalize(
1821
_decls: Decls,
1922
env: Env,

crates/formality-prove/src/prove/prove_wc.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use crate::{
66
prove::{
77
combinators::for_all,
88
env::{Bias, Env},
9-
is_int::ty_is_int,
109
is_local::{is_local_trait_ref, may_be_remote},
1110
prove,
1211
prove_after::prove_after,
@@ -146,11 +145,5 @@ judgment_fn! {
146145
----------------------------- ("const has ty")
147146
(prove_wc(decls, env, assumptions, Predicate::ConstHasType(ct, ty)) => c)
148147
)
149-
150-
(
151-
(ty_is_int(&decl, env, assumptions, ty) => c)
152-
----------------------------- ("ty is int")
153-
(prove_wc(decl, env, assumptions, Relation::IsInt(ty)) => c)
154-
)
155148
}
156149
}

crates/formality-types/src/grammar/formulas.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,6 @@ impl Ty {
7272
pub fn well_formed(&self) -> Relation {
7373
Relation::WellFormed(self.upcast())
7474
}
75-
76-
pub fn is_int(&self) -> Relation {
77-
Relation::IsInt(self.clone())
78-
}
7975
}
8076

8177
impl Parameter {
@@ -182,9 +178,6 @@ pub enum Relation {
182178

183179
#[grammar(@wf($v0))]
184180
WellFormed(Parameter),
185-
186-
#[grammar(@is_int($v0))]
187-
IsInt(Ty),
188181
}
189182

190183
impl Relation {
@@ -195,7 +188,6 @@ impl Relation {
195188
Relation::Sub(a, b) => (Skeleton::Sub, vec![a.clone(), b.clone()]),
196189
Relation::Outlives(a, b) => (Skeleton::Outlives, vec![a.clone(), b.clone()]),
197190
Relation::WellFormed(p) => (Skeleton::WellFormed, vec![p.clone()]),
198-
Relation::IsInt(ty) => (Skeleton::IsInt, vec![ty.to_parameter()]),
199191
}
200192
}
201193
}

src/test/mir_fn_bodies.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -658,15 +658,9 @@ fn test_invalid_value_in_switch_terminator() {
658658
]
659659
[]
660660
expect_test::expect![[r#"
661-
judgment `prove { goal: {@ is_int(bool)}, assumptions: {}, env: Env { variables: [], bias: Soundness, pending: [] }, decls: decls(222, [], [], [], [], [], [], {}, {}) }` failed at the following rule(s):
662-
failed at (src/file.rs:LL:CC) because
663-
judgment `prove_wc_list { goal: {@ is_int(bool)}, assumptions: {}, env: Env { variables: [], bias: Soundness, pending: [] } }` failed at the following rule(s):
664-
the rule "some" failed at step #0 (src/file.rs:LL:CC) because
665-
judgment `prove_wc { goal: @ is_int(bool), assumptions: {}, env: Env { variables: [], bias: Soundness, pending: [] } }` failed at the following rule(s):
666-
the rule "ty is int" failed at step #0 (src/file.rs:LL:CC) because
667-
judgment `ty_is_int { assumptions: {}, ty: bool, env: Env { variables: [], bias: Soundness, pending: [] } }` failed at the following rule(s):
668-
the rule "rigid_ty is int" failed at step #0 (src/file.rs:LL:CC) because
669-
condition evaluted to false: `id.is_int()`
670-
id = bool"#]]
661+
judgment `ty_is_int { assumptions: {}, ty: bool, env: Env { variables: [], bias: Soundness, pending: [] } }` failed at the following rule(s):
662+
the rule "rigid_ty is int" failed at step #0 (src/file.rs:LL:CC) because
663+
condition evaluted to false: `id.is_int()`
664+
id = bool"#]]
671665
)
672666
}

0 commit comments

Comments
 (0)