Skip to content

Commit 07beba5

Browse files
committed
wip with tiif
1 parent dd3dffb commit 07beba5

File tree

5 files changed

+204
-8
lines changed

5 files changed

+204
-8
lines changed

crates/formality-check/src/fns.rs

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,81 @@
11
use formality_prove::Env;
22
use formality_rust::{
3-
grammar::{Fn, FnBoundData},
3+
grammar::{Fn, FnBoundData, MaybeFnBody},
44
prove::ToWcs,
55
};
66
use formality_types::grammar::{Fallible, Wcs};
77

8-
use crate::Check;
8+
use crate::{mini_rust_check, Check};
99

1010
impl Check<'_> {
11+
/// A "free function" is a free-standing function that is not part of an impl.
1112
pub(crate) fn check_free_fn(&self, f: &Fn) -> Fallible<()> {
1213
self.check_fn(&Env::default(), Wcs::t(), f)
1314
}
1415

16+
/// Invoked for both free functions and methods.
17+
///
18+
/// # Parameters
19+
///
20+
/// * `in_env` -- the environment from the enclosing impl (if any)
21+
/// * `in_assumptions` -- where-clauses from the enclosing impl (if any)
22+
/// * `f` -- the function definition
1523
pub(crate) fn check_fn(
1624
&self,
1725
in_env: &Env,
1826
in_assumptions: impl ToWcs,
1927
f: &Fn,
2028
) -> Fallible<()> {
2129
let in_assumptions = in_assumptions.to_wcs();
30+
31+
// We do not expect to have any inference variables in the where clauses.
32+
//
33+
// e.g., `impl<T: Ord> Foo<T> { ... }` would have a reference to a
34+
// universal variable `T`, not an existential one.
2235
assert!(in_env.only_universal_variables() && in_env.encloses((&in_assumptions, f)));
2336

37+
// Create a mutable copy of the environment which we will extend
38+
// with the generic parameters from the function.
2439
let mut env = in_env.clone();
2540

41+
// Instantiate the generic parameters declared on the fn
42+
// with universal variables (i.e., treat them as fresh,
43+
// unknown types).
2644
let Fn { id: _, binder } = f;
27-
2845
let FnBoundData {
2946
input_tys,
3047
output_ty,
3148
where_clauses,
32-
body: _,
49+
body,
3350
} = env.instantiate_universally(binder);
3451

52+
// The in-scope assumtion are the union of the assumptions from
53+
// the impl and the fn.
3554
let fn_assumptions: Wcs = (in_assumptions, &where_clauses).to_wcs();
3655

56+
// All of the following must be well-formed:
57+
// where-clauses, input parameter types, and output type.
3758
self.prove_where_clauses_well_formed(&env, &fn_assumptions, &where_clauses)?;
38-
3959
for input_ty in &input_tys {
4060
self.prove_goal(&env, &fn_assumptions, input_ty.well_formed())?;
4161
}
42-
4362
self.prove_goal(&env, &fn_assumptions, output_ty.well_formed())?;
4463

64+
// Type-check the function body, if present.
65+
match body {
66+
MaybeFnBody::NoFnBody => {
67+
// No fn body occurs trait definitions only.
68+
}
69+
MaybeFnBody::FnBody(fn_body) => match fn_body {
70+
formality_rust::grammar::FnBody::TrustedFnBody => {
71+
// A trusted function body is assumed to be valid, all set.
72+
}
73+
formality_rust::grammar::FnBody::MiniRust(body) => {
74+
mini_rust_check::check_body(&env, &fn_assumptions, body)?;
75+
}
76+
}
77+
}
78+
4579
Ok(())
4680
}
4781
}

crates/formality-check/src/impls.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,32 @@ impl super::Check<'_> {
179179
)?;
180180
}
181181

182+
// Check that the impl's declared return type is a subtype of what the trait declared:
183+
//
184+
// OK
185+
//
186+
// ```rust
187+
// trait Foo {
188+
// fn bar<'a>(&'a self, input: &u32) -> &'a u32;
189+
// }
190+
//
191+
// impl Foo for MyType {
192+
// fn bar(&self, input: &u32) -> &'static u32 {} // <-- subtype, ok
193+
// }
194+
// ```
195+
//
196+
// NOT OK
197+
//
198+
// ```rust
199+
// trait Foo {
200+
// fn bar<'a>(&'a self, input: &u32) -> &'a u32;
201+
// }
202+
//
203+
// impl Foo for MyType {
204+
// fn bar<'b>(&self, input: &'b u32) -> &'b u32 {} // <-- not sutype, not ok
205+
// }
206+
// ```
207+
182208
self.prove_goal(
183209
&env,
184210
(&impl_assumptions, &ii_where_clauses),

crates/formality-check/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#![allow(dead_code)]
22

3+
use formality_types::rust::FormalityLang;
4+
35
use std::{collections::VecDeque, fmt::Debug};
46

57
use anyhow::bail;
@@ -11,6 +13,8 @@ use formality_rust::{
1113
};
1214
use formality_types::grammar::{Fallible, Wcs};
1315

16+
mod mini_rust_check;;
17+
1418
/// Check all crates in the program. The crates must be in dependency order
1519
/// such that any prefix of the crates is a complete program.
1620
pub fn check_all_crates(program: &Program) -> Fallible<()> {
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use formality_core::{term, Fallible, Map, Upcast};
2+
use formality_prove::Env;
3+
use formality_rust::grammar::minirust::{self, LocalId};
4+
use formality_types::grammar::{Ty, Wcs, Relation};
5+
6+
use crate::Check;
7+
8+
impl Check<'_> {
9+
pub(crate) fn check_body(
10+
&self,
11+
env: &Env,
12+
output_ty: impl Upcast<Ty>,
13+
fn_assumptions: &Wcs,
14+
body: minirust::Body,
15+
) -> Fallible<()> {
16+
// Type-check:
17+
//
18+
// (1) Check that all the types declared for each local variable are well-formed
19+
// (2) Check that the type of the returned local is compatible with the declared return type
20+
// (3) Check that the statements in the body are valid
21+
22+
let output_ty: Ty = output_ty.upcast();
23+
24+
// (1) Check that all the types declared for each local variable are well-formed
25+
for lv in &body.locals {
26+
self.prove_goal(&env, &fn_assumptions, lv.ty.well_formed())?;
27+
}
28+
let local_variables: Map<LocalId, Ty> = body
29+
.locals
30+
.iter()
31+
.map(|lv| (lv.id.clone(), lv.ty.clone()))
32+
.collect();
33+
34+
// (2) Check if the actual return type is the subtype of the declared return type.
35+
self.prove_goal(
36+
&env,
37+
fn_assumptions,
38+
Relation::sub(&local_variables[&body.ret], &output_ty),
39+
)?;
40+
41+
let env = TypeckEnv {
42+
env,
43+
output_ty,
44+
local_variables,
45+
};
46+
47+
// (3) Check statements in body are valid
48+
for block in &body.blocks {
49+
self.check_block(&env, block)?;
50+
}
51+
52+
Ok(())
53+
}
54+
55+
fn check_block(&self, env: &TypeckEnv, block: &minirust::BasicBlock) -> Fallible<()> {
56+
for statement in &block.statements {
57+
self.check_statement(env, statement)?;
58+
}
59+
60+
self.check_terminator(&block.terminator)?;
61+
62+
Ok(())
63+
}
64+
65+
fn check_statement(&self, env: &TypeckEnv, statement: &minirust::Statement) -> Fallible<()> {
66+
match statement {
67+
minirust::Statement::Assign(place_expression, value_expression) => {
68+
// FIXME: check that the place and value are well-formed
69+
// FIXME: check that the type of the value is a subtype of the place's type
70+
}
71+
minirust::Statement::PlaceMention(place_expression) => {
72+
// FIXME: check that the place is well-formed
73+
// FIXME: check that access the place is allowed per borrowck rules
74+
}
75+
}
76+
}
77+
78+
fn check_terminator(&self, env: &TypeckEnv, terminator: &minirust::Terminator) -> Fallible<()> {
79+
match terminator {
80+
minirust::Terminator::Goto(bb_id) => {
81+
// FIXME: Check that the basic block `bb_id` exists
82+
}
83+
minirust::Terminator::Call { callee, arguments, ret, next_block } => {
84+
// FIXME: check that the callee is something callable
85+
// FIXME: check that the arguments are well formed and their types are subtypes of the expected argument types
86+
// FIXME: check that the next block is valid
87+
// FIXME: check that the fn's declared return type is a subtype of the type of the local variable `ret`
88+
}
89+
minirust::Terminator::Return => {
90+
// FIXME: Check that the return local variable has been initialized
91+
}
92+
}
93+
}
94+
}
95+
96+
#[term]
97+
struct TypeckEnv {
98+
env: Env,
99+
100+
/// The declared return type from the function signature.
101+
output_ty: Ty,
102+
103+
/// Type of each local variable, as declared.
104+
local_variables: Map<LocalId, Ty>,
105+
}

crates/formality-rust/src/grammar/minirust.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,35 @@
11

22
use formality_core::id;
33
use formality_macros::term;
4-
use formality_types::grammar::Ty;
4+
use formality_types::grammar::{Parameter, Ty};
55

66
// This definition is based on [MiniRust](https://github.com/minirust/minirust/blob/master/spec/lang/syntax.md).
77

88
id!(BbId);
99
id!(LocalId);
1010

11+
// Example:
12+
//
13+
// fn foo() -> u32 = minirust(v1) -> v0 {
14+
// let v0: u32;
15+
// let v1: u32;
16+
//
17+
// bb0:
18+
// v0 = v1;
19+
// return;
20+
// }
21+
//
22+
// fn bar() -> u32 = minirust(v1) -> v0 {
23+
// let v0: u32;
24+
// let v1: u32;
25+
//
26+
// bb0:
27+
// call foo (v1) -> v0 goto bb1;
28+
//
29+
// bb1:
30+
// return;
31+
// }
32+
1133
/// Based on [MiniRust statements](https://github.com/minirust/minirust/blob/9ae11cc202d040f08bc13ec5254d3d41d5f3cc25/spec/lang/syntax.md#statements-terminators).
1234
#[term(minirust($,args) -> $ret {
1335
$*locals
@@ -61,10 +83,15 @@ pub enum Terminator {
6183
// Unreachable
6284
// Intrinsic
6385

64-
#[grammar(call $callee$(arguments) -> $ret $:goto $next_block)]
86+
// Example:
87+
//
88+
// call foo(x, y)
89+
// call foo.add<u32>(x, y)
90+
#[grammar(call $callee $<?generic_arguments> $(arguments) -> $ret $:goto $next_block)]
6591
Call {
6692
callee: ValueExpression,
6793
// cc: CallingConvention,
94+
generic_arguments: Vec<Parameter>,
6895
arguments: Vec<ArgumentExpression>,
6996
ret: PlaceExpression,
7097
next_block: Option<BbId>,

0 commit comments

Comments
 (0)