Skip to content

Commit 7a925de

Browse files
authored
Merge pull request #197 from tiif/fn-body-part2
Add more support to mir fn bodies
2 parents b5789fe + 364d779 commit 7a925de

File tree

15 files changed

+615
-71
lines changed

15 files changed

+615
-71
lines changed

crates/formality-check/src/mini_rust_check.rs

Lines changed: 142 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
use std::iter::zip;
22

33
use formality_core::{judgment_fn, Fallible, Map, Upcast};
4-
use formality_prove::{prove_normalize, Constraints, Decls, Env};
4+
use formality_prove::{prove_normalize, AdtDeclBoundData, AdtDeclVariant, Constraints, Decls, Env};
55
use formality_rust::grammar::minirust::ArgumentExpression::{ByValue, InPlace};
6-
use formality_rust::grammar::minirust::PlaceExpression::Local;
7-
use formality_rust::grammar::minirust::ValueExpression::{Constant, Fn, Load};
6+
use formality_rust::grammar::minirust::PlaceExpression::*;
7+
use formality_rust::grammar::minirust::ValueExpression::{Constant, Fn, Load, Struct};
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, Parameter, RigidName, RigidTy};
13-
use formality_types::grammar::{Relation, Ty, Wcs};
12+
use formality_types::grammar::{
13+
CrateId, FnId, Parameter, Relation, RigidName, RigidTy, Ty, VariantId, Wcs,
14+
};
1415

1516
use crate::{Check, CrateItem};
1617
use anyhow::bail;
@@ -93,6 +94,7 @@ impl Check<'_> {
9394
declared_input_tys,
9495
callee_input_tys: Map::new(),
9596
crate_id: crate_id.clone(),
97+
fn_args: body.args.clone(),
9698
};
9799

98100
// (4) Check statements in body are valid
@@ -130,7 +132,7 @@ impl Check<'_> {
130132
let place_ty = self.check_place(typeck_env, place_expression)?;
131133

132134
// Check if the value expression is well-formed.
133-
let value_ty = self.check_value(typeck_env, value_expression)?;
135+
let value_ty = self.check_value(typeck_env, fn_assumptions, value_expression)?;
134136

135137
// Check that the type of the value is a subtype of the place's type
136138
self.prove_goal(
@@ -149,6 +151,24 @@ impl Check<'_> {
149151
self.check_place(typeck_env, place_expression)?;
150152
// FIXME: check that access the place is allowed per borrowck rules
151153
}
154+
minirust::Statement::StorageLive(local_id) => {
155+
// FIXME: We need more checks here after loan is introduced.
156+
if self.find_local_id(&typeck_env, local_id).is_none() {
157+
bail!("Statement::StorageLive: invalid local variable")
158+
}
159+
}
160+
minirust::Statement::StorageDead(local_id) => {
161+
// FIXME: We need more checks here after loan is introduced.
162+
let Some((local_id, _)) = self.find_local_id(&typeck_env, local_id) else {
163+
bail!("Statement::StorageDead: invalid local variable")
164+
};
165+
// Make sure function arguments and return place are not marked as dead.
166+
if local_id == typeck_env.ret_id
167+
|| typeck_env.fn_args.iter().any(|fn_arg| local_id == *fn_arg)
168+
{
169+
bail!("Statement::StorageDead: trying to mark function arguments or return local as dead")
170+
}
171+
}
152172
}
153173
Ok(())
154174
}
@@ -172,7 +192,7 @@ impl Check<'_> {
172192
next_block,
173193
} => {
174194
// Function is part of the value expression, so we will check if the function exists in check_value.
175-
self.check_value(typeck_env, callee)?;
195+
self.check_value(typeck_env, fn_assumptions, callee)?;
176196

177197
// Get argument information from the callee.
178198
let Fn(callee_fn_id) = callee else {
@@ -189,7 +209,11 @@ impl Check<'_> {
189209
let arguments = zip(callee_declared_input_tys, actual_arguments);
190210
for (declared_ty, actual_argument) in arguments {
191211
// Check if the arguments are well formed.
192-
let actual_ty = self.check_argument_expression(typeck_env, actual_argument)?;
212+
let actual_ty = self.check_argument_expression(
213+
typeck_env,
214+
fn_assumptions,
215+
actual_argument,
216+
)?;
193217
// Check if the actual argument type passed in is the subtype of expect argument type.
194218
self.prove_goal(
195219
&typeck_env.env,
@@ -226,7 +250,9 @@ impl Check<'_> {
226250
fallback,
227251
} => {
228252
// Check if the value is well-formed.
229-
let value_ty = self.check_value(typeck_env, switch_value).unwrap();
253+
let value_ty = self
254+
.check_value(typeck_env, fn_assumptions, switch_value)
255+
.unwrap();
230256

231257
self.prove_judgment(&typeck_env.env, &fn_assumptions, value_ty, ty_is_int)?;
232258

@@ -246,24 +272,65 @@ impl Check<'_> {
246272
match place {
247273
Local(local_id) => {
248274
// Check if place id is a valid local id.
249-
let Some((_, ty)) = env
250-
.local_variables
251-
.iter()
252-
.find(|(declared_local_id, _)| *declared_local_id == local_id)
253-
else {
275+
let Some((_, ty)) = self.find_local_id(env, local_id) else {
254276
bail!(
255277
"PlaceExpression::Local: unknown local name `{:?}`",
256278
local_id
257279
)
258280
};
259281
place_ty = ty;
260282
}
283+
Field(field_projection) => {
284+
let ty = self.check_place(env, &field_projection.root).unwrap();
285+
286+
// FIXME(tiif): We eventually want to do normalization here, so check_place should be
287+
// a judgment fn.
288+
let Some(adt_id) = ty.get_adt_id() else {
289+
bail!("The local used for field projection is not adt.")
290+
};
291+
292+
let (
293+
_,
294+
AdtDeclBoundData {
295+
where_clause: _,
296+
variants,
297+
},
298+
) = self.decls.adt_decl(&adt_id).binder.open();
299+
let AdtDeclVariant { name, fields } = variants.last().unwrap();
300+
301+
if *name != VariantId::for_struct() {
302+
bail!("The local used for field projection must be struct.")
303+
}
304+
305+
// Check if the index is valid for the tuple.
306+
if field_projection.index >= fields.len() {
307+
bail!("The field index used in PlaceExpression::Field is invalid.")
308+
}
309+
310+
place_ty = fields[field_projection.index].ty.clone();
311+
}
261312
}
262313
Ok(place_ty.clone())
263314
}
264315

316+
fn find_local_id(&self, env: &TypeckEnv, local_id: &LocalId) -> Option<(LocalId, Ty)> {
317+
if let Some((local_id, ty)) = env
318+
.local_variables
319+
.iter()
320+
.find(|(declared_local_id, _)| *declared_local_id == local_id)
321+
{
322+
return Some((local_id.clone(), ty.clone()));
323+
}
324+
return None;
325+
}
326+
265327
// Check if the value expression is well-formed, and return the type of the value expression.
266-
fn check_value(&self, typeck_env: &mut TypeckEnv, value: &ValueExpression) -> Fallible<Ty> {
328+
fn check_value(
329+
&self,
330+
typeck_env: &mut TypeckEnv,
331+
fn_assumptions: &Wcs,
332+
value: &ValueExpression,
333+
) -> Fallible<Ty> {
267334
let value_ty;
268335
match value {
269336
Load(place_expression) => {
@@ -312,18 +379,74 @@ impl Check<'_> {
312379
// it will be rejected by the parser.
313380
Ok(constant.get_ty())
314381
}
382+
Struct(value_expressions, ty) => {
383+
// Check if the adt is well-formed.
384+
self.prove_goal(&typeck_env.env, &fn_assumptions, ty.well_formed())?;
385+
386+
let Some(adt_id) = ty.get_adt_id() else {
387+
bail!("The type used in ValueExpression::Struct must be adt")
388+
};
389+
390+
// Make sure that the adt is struct.
391+
let (
392+
_,
393+
AdtDeclBoundData {
394+
where_clause: _,
395+
variants,
396+
},
397+
) = self.decls.adt_decl(&adt_id).binder.open();
398+
399+
let AdtDeclVariant { name, fields } = variants.last().unwrap();
400+
401+
if *name != VariantId::for_struct() {
402+
bail!("This type used in ValueExpression::Struct should be a struct")
403+
}
404+
405+
// Check if the number of value provided match the number of field.
406+
let struct_field_tys: Vec<Ty> =
407+
fields.iter().map(|field| field.ty.clone()).collect();
408+
409+
if value_expressions.len() != struct_field_tys.len() {
410+
bail!("The length of ValueExpression::Tuple does not match the type of the ADT declared")
411+
}
412+
413+
let mut value_tys: Vec<Ty> = Vec::new();
414+
415+
for value_expression in value_expressions {
416+
// FIXME: we only support const in value expression of struct for now, we can add support
417+
// more in future.
418+
let Constant(_) = value_expression else {
419+
bail!("Only Constant is supported in ValueExpression::Struct for now.")
420+
};
421+
value_tys.push(self.check_value(
422+
typeck_env,
423+
fn_assumptions,
424+
value_expression,
425+
)?);
426+
}
427+
428+
// Make sure all the types supplied are the subtype of declared types.
429+
self.prove_goal(
430+
&typeck_env.env,
431+
&fn_assumptions,
432+
Wcs::all_sub(value_tys, struct_field_tys),
433+
)?;
434+
435+
Ok(ty.clone())
436+
}
315437
}
316438
}
317439

318440
fn check_argument_expression(
319441
&self,
320442
env: &mut TypeckEnv,
443+
fn_assumptions: &Wcs,
321444
arg_expr: &ArgumentExpression,
322445
) -> Fallible<Ty> {
323446
let ty;
324447
match arg_expr {
325448
ByValue(val_expr) => {
326-
ty = self.check_value(env, val_expr)?;
449+
ty = self.check_value(env, fn_assumptions, val_expr)?;
327450
}
328451
InPlace(place_expr) => {
329452
ty = self.check_place(env, place_expr)?;
@@ -370,6 +493,9 @@ struct TypeckEnv {
370493
/// We need this to access information about other functions
371494
/// declared in the current crate.
372495
crate_id: CrateId,
496+
497+
/// LocalId of function argument.
498+
fn_args: Vec<LocalId>,
373499
}
374500

375501
judgment_fn! {

crates/formality-prove/src/decls.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use formality_core::{set, Set, Upcast};
22
use formality_macros::term;
33
use formality_types::grammar::{
4-
AdtId, AliasName, AliasTy, Binder, Parameter, Predicate, Relation, TraitId, TraitRef, Ty, Wc,
5-
Wcs,
4+
AdtId, AliasName, AliasTy, Binder, FieldId, Parameter, Predicate, Relation, TraitId, TraitRef,
5+
Ty, VariantId, Wc, Wcs,
66
};
77

88
#[term]
@@ -285,8 +285,7 @@ pub struct AliasBoundDeclBoundData {
285285
pub where_clause: Wcs,
286286
}
287287

288-
/// An "ADT declaration" declares an ADT name, its generics, and its where-clauses.
289-
/// It doesn't capture the ADT fields, yet.
288+
/// An "ADT declaration" declares an ADT name, its generics, and its where-clauses, and the field.
290289
///
291290
/// In Rust syntax, it covers the `struct Foo<X> where X: Bar` part of the declaration, but not what appears in the `{...}`.
292291
#[term(adt $id $binder)]
@@ -299,8 +298,29 @@ pub struct AdtDecl {
299298
}
300299

301300
/// The "bound data" for a [`AdtDecl`][].
302-
#[term($:where $where_clause)]
301+
#[term($:where $where_clause { $,variants })]
303302
pub struct AdtDeclBoundData {
304303
/// The where-clauses declared on the ADT,
305304
pub where_clause: Wcs,
305+
pub variants: Vec<AdtDeclVariant>,
306+
}
307+
308+
#[term($name { $,fields })]
309+
pub struct AdtDeclVariant {
310+
pub name: VariantId,
311+
pub fields: Vec<AdtDeclField>,
312+
}
313+
314+
#[term($name : $ty)]
315+
pub struct AdtDeclField {
316+
pub name: AdtDeclFieldName,
317+
pub ty: Ty,
318+
}
319+
320+
#[term]
321+
pub enum AdtDeclFieldName {
322+
#[cast]
323+
Id(FieldId),
324+
#[cast]
325+
Index(usize),
306326
}

crates/formality-prove/src/prove.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod negation;
77
mod prove_after;
88
mod prove_eq;
99
pub mod prove_normalize;
10+
mod prove_sub;
1011
mod prove_via;
1112
mod prove_wc;
1213
mod prove_wc_list;
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use formality_core::judgment_fn;
2+
use formality_types::grammar::{Parameter, Relation, RigidTy, TyData, Wcs};
3+
4+
use crate::{
5+
decls::Decls,
6+
prove::{prove, prove_after::prove_after, prove_normalize::prove_normalize},
7+
};
8+
9+
use super::{constraints::Constraints, env::Env};
10+
11+
judgment_fn! {
12+
pub fn prove_sub(
13+
_decls: Decls,
14+
env: Env,
15+
assumptions: Wcs,
16+
a: Parameter,
17+
b: Parameter,
18+
) => Constraints {
19+
debug(a, b, assumptions, env)
20+
21+
assert(a.kind() == b.kind())
22+
23+
trivial(a == b => Constraints::none(env))
24+
25+
(
26+
(prove_normalize(&decls, env, &assumptions, &x) => (c, y))
27+
(prove_after(&decls, c, &assumptions, Relation::sub(y, &z)) => c)
28+
----------------------------- ("normalize-l")
29+
(prove_sub(decls, env, assumptions, x, z) => c)
30+
)
31+
32+
(
33+
(prove_normalize(&decls, env, &assumptions, &y) => (c, z))
34+
(prove_after(&decls, c, &assumptions, Relation::sub(&x, &z)) => c)
35+
----------------------------- ("normalize-r")
36+
(prove_sub(decls, env, assumptions, x, y) => c)
37+
)
38+
39+
(
40+
(let RigidTy { name: a_name, parameters: a_parameters } = a)
41+
(let RigidTy { name: b_name, parameters: b_parameters } = b)
42+
(if a_name == b_name)!
43+
(prove(decls, env, assumptions, Wcs::all_sub(a_parameters, b_parameters)) => c)
44+
----------------------------- ("rigid")
45+
(prove_sub(decls, env, assumptions, TyData::RigidTy(a), TyData::RigidTy(b)) => c)
46+
)
47+
48+
// FIXME: uncomment this when adding prove_outlives
49+
//(
50+
// (prove_outlives(decls, env, assumptions, a, b) => c)
51+
// ----------------------------- ("lifetime => outlives")
52+
// (prove_sub(decls, env, assumptions, a: Lt, b: Lt) => c)
53+
//)
54+
}
55+
}

0 commit comments

Comments
 (0)