Skip to content

Commit bf38c30

Browse files
committed
Handle FnOnce::Output
1 parent 8ba097c commit bf38c30

File tree

5 files changed

+135
-17
lines changed

5 files changed

+135
-17
lines changed

chalk-parse/src/parser.lalrpop

+10-4
Original file line numberDiff line numberDiff line change
@@ -223,16 +223,22 @@ pub Ty: Ty = {
223223
};
224224

225225
TyWithoutId: Ty = {
226-
"for" "<" <l:Comma<LifetimeId>> ">" "fn" "(" <types:Comma<Ty>> ")" => Ty::ForAll {
226+
"for" "<" <l:Comma<LifetimeId>> ">" "fn" "(" <types:Comma<Ty>> ")" <ret_ty:FnReturn?> => Ty::ForAll {
227227
lifetime_names: l,
228-
types: types.into_iter().map(Box::new).collect()
228+
types: types
229+
.into_iter()
230+
.chain(std::iter::once(ret_ty.unwrap_or_else(|| Ty::Tuple { types: Vec::new() })))
231+
.map(Box::new).collect()
229232
},
230233
<ScalarType> => Ty::Scalar { ty: <> },
231234
"str" => Ty::Str,
232235
"!" => Ty::Never,
233-
"fn" "(" <types:Comma<Ty>> ")" => Ty::ForAll {
236+
"fn" "(" <types:Comma<Ty>> ")" <ret_ty:FnReturn?> => Ty::ForAll {
234237
lifetime_names: vec![],
235-
types: types.into_iter().map(Box::new).collect()
238+
types: types
239+
.into_iter()
240+
.chain(std::iter::once(ret_ty.unwrap_or_else(|| Ty::Tuple { types: Vec::new() })))
241+
.map(Box::new).collect()
236242
},
237243
"dyn" <b:Plus<QuantifiedInlineBound>> "+" <l:Lifetime> => Ty::Dyn {
238244
bounds: b,

chalk-solve/src/clauses.rs

+6
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,12 @@ fn program_clauses_that_could_match<I: Interner>(
367367
return Err(Floundered);
368368
}
369369

370+
if let Some(well_known) = trait_datum.well_known {
371+
builtin_traits::add_builtin_assoc_program_clauses(
372+
db, builder, well_known, proj,
373+
);
374+
}
375+
370376
push_program_clauses_for_associated_type_values_in_impls_of(
371377
builder,
372378
trait_id,

chalk-solve/src/clauses/builtin_traits.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::{builder::ClauseBuilder, generalize};
22
use crate::{Interner, RustIrDatabase, TraitRef, WellKnownTrait};
3-
use chalk_ir::{Substitution, Ty};
3+
use chalk_ir::{ProjectionTy, Substitution, Ty};
44

55
mod clone;
66
mod copy;
@@ -40,14 +40,37 @@ pub fn add_builtin_program_clauses<I: Interner>(
4040
clone::add_clone_program_clauses(db, builder, &trait_ref, ty)
4141
}
4242
WellKnownTrait::FnOnceTrait | WellKnownTrait::FnMutTrait | WellKnownTrait::FnTrait => {
43-
fn_::add_fn_trait_program_clauses(db, builder, &trait_ref, ty)
43+
fn_::add_fn_trait_program_clauses(db, builder, trait_ref.trait_id, ty, false)
4444
}
4545
// Drop impls are provided explicitly
4646
WellKnownTrait::DropTrait => (),
4747
}
4848
});
4949
}
5050

51+
/// Like `add_builtin_program_clauses`, but for `DomainGoal::Normalize` involving
52+
/// a projection (e.g. `<fn(u8) as FnOnce<(u8,)>>::Output`)
53+
pub fn add_builtin_assoc_program_clauses<I: Interner>(
54+
db: &dyn RustIrDatabase<I>,
55+
builder: &mut ClauseBuilder<'_, I>,
56+
well_known: WellKnownTrait,
57+
proj: &ProjectionTy<I>,
58+
) {
59+
match well_known {
60+
WellKnownTrait::FnOnceTrait => {
61+
let interner = db.interner();
62+
let self_ty = proj
63+
.substitution
64+
.at(interner, 0)
65+
.assert_ty_ref(interner)
66+
.data(interner);
67+
let trait_id = db.well_known_trait_id(well_known).unwrap();
68+
fn_::add_fn_trait_program_clauses(db, builder, trait_id, self_ty, true);
69+
}
70+
_ => {}
71+
}
72+
}
73+
5174
/// Given a trait ref `T0: Trait` and a list of types `U0..Un`, pushes a clause of the form
5275
/// `Implemented(T0: Trait) :- Implemented(U0: Trait) .. Implemented(Un: Trait)`
5376
pub fn needs_impl_for_tys<I: Interner>(

chalk-solve/src/clauses/builtin_traits/fn_.rs

+54-9
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,46 @@
11
use crate::clauses::ClauseBuilder;
22
use crate::infer::instantiate::IntoBindersAndValue;
3+
use crate::rust_ir::WellKnownTrait;
34
use crate::{Interner, RustIrDatabase, TraitRef};
4-
use chalk_ir::{ApplicationTy, Binders, Substitution, Ty, TyData, TypeName, VariableKinds};
5+
use chalk_ir::{
6+
AliasTy, ApplicationTy, Binders, Normalize, ProjectionTy, Substitution, TraitId, Ty, TyData,
7+
TypeName, VariableKinds,
8+
};
59

6-
// Handles clauses for FnOnce/FnMut/Fn
10+
/// Handles clauses for FnOnce/FnMut/Fn.
11+
/// If `assoc_output` is `true`, we push a clause of the form
12+
/// `Normalize(<fn(A) -> B as FnOnce<(A,)>>::Output -> B) :- Implemented(fn(A) -> B as FnOnce<(A,)>`
13+
///
14+
/// If `assoc_output` is `false`, we push a clause of the form
15+
/// `Implemented(fn(A) -> B as FnOnce<(A,)>)`
716
pub fn add_fn_trait_program_clauses<I: Interner>(
817
db: &dyn RustIrDatabase<I>,
918
builder: &mut ClauseBuilder<'_, I>,
10-
trait_ref: &TraitRef<I>,
19+
trait_id: TraitId<I>,
1120
ty: &TyData<I>,
21+
assoc_output: bool,
1222
) {
1323
match ty {
1424
TyData::Function(fn_val) => {
1525
let interner = db.interner();
16-
let (binders, sub) = fn_val.into_binders_and_value(interner);
26+
let (binders, orig_sub) = fn_val.into_binders_and_value(interner);
27+
// Take all of the arguments except for the last one, which
28+
// represents the return type
29+
let arg_sub = Substitution::from(
30+
interner,
31+
orig_sub.iter(interner).take(orig_sub.len(interner) - 1),
32+
);
33+
let fn_output_ty = orig_sub
34+
.at(interner, orig_sub.len(interner) - 1)
35+
.assert_ty_ref(interner);
1736

1837
// We are constructing a reference to `FnOnce<Args>`, where
1938
// `Args` is a tuple of the function's argument types
2039
let tupled = Ty::new(
2140
interner,
2241
TyData::Apply(ApplicationTy {
23-
name: TypeName::Tuple(sub.len(interner)),
24-
substitution: sub.clone(),
42+
name: TypeName::Tuple(arg_sub.len(interner)),
43+
substitution: arg_sub.clone(),
2544
}),
2645
);
2746

@@ -31,16 +50,42 @@ pub fn add_fn_trait_program_clauses<I: Interner>(
3150
// Given a function type `fn(A1, A2, ..., AN)`, construct a `TraitRef`
3251
// of the form `fn(A1, A2, ..., AN): FnOnce<(A1, A2, ..., AN)>`
3352
let new_trait_ref = TraitRef {
34-
trait_id: trait_ref.trait_id,
35-
substitution: tupled_sub,
53+
trait_id,
54+
substitution: tupled_sub.clone(),
3655
};
3756

3857
// Functions types come with a binder, which we push so
3958
// that the `TraitRef` properly references any bound lifetimes
4059
// (e.g. `for<'a> fn(&'a u8): FnOnce<(&'b u8)>`)
4160
let bound_ref = Binders::new(VariableKinds::from(interner, binders), new_trait_ref);
4261
builder.push_binders(&bound_ref, |this, inner_trait| {
43-
this.push_fact(inner_trait);
62+
if assoc_output {
63+
//The `Output` type is defined on the `FnOnceTrait`
64+
let fn_once = db.trait_datum(trait_id);
65+
assert_eq!(fn_once.well_known, Some(WellKnownTrait::FnOnceTrait));
66+
let assoc_types = &fn_once.associated_ty_ids;
67+
if assoc_types.len() != 1 {
68+
panic!(
69+
"FnOnce trait should have exactly one associated type, found {:?}",
70+
assoc_types
71+
);
72+
}
73+
74+
// Construct `Normalize(<fn(A) -> B as FnOnce<(A,)>>::Output -> B)`
75+
let assoc_output_ty = assoc_types[0];
76+
let proj_ty = ProjectionTy {
77+
associated_ty_id: assoc_output_ty,
78+
substitution: tupled_sub,
79+
};
80+
let normalize = Normalize {
81+
alias: AliasTy::Projection(proj_ty),
82+
ty: fn_output_ty.clone(),
83+
};
84+
85+
this.push_clause(normalize, std::iter::once(inner_trait));
86+
} else {
87+
this.push_fact(inner_trait);
88+
}
4489
})
4590
}
4691
_ => {}

tests/test/functions.rs

+40-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use super::*;
22

33
#[test]
4-
fn function_implement_fn_once() {
4+
fn function_implement_fn_traits() {
55
test! {
66
program {
77
#[lang(fn_once)]
8-
trait FnOnce<Args> { }
8+
trait FnOnce<Args> {
9+
type Output;
10+
}
911

1012
#[lang(fn_mut)]
1113
trait FnMut<Args> where Self: FnOnce<Args> { }
@@ -32,6 +34,42 @@ fn function_implement_fn_once() {
3234
"Unique; substitution [], lifetime constraints []"
3335
}
3436

37+
goal {
38+
Normalize(<fn(u8) as FnOnce<(u8,)>>::Output -> ())
39+
} yields {
40+
"Unique; substitution [], lifetime constraints []"
41+
}
42+
43+
goal {
44+
Normalize(<fn(u8) -> bool as FnOnce<(u8,)>>::Output -> bool)
45+
} yields {
46+
"Unique; substitution [], lifetime constraints []"
47+
}
48+
49+
goal {
50+
Normalize(<fn(u8) -> bool as FnOnce<(u8,)>>::Output -> u8)
51+
} yields {
52+
"No possible solution"
53+
}
54+
55+
goal {
56+
forall<T, V> {
57+
Normalize(<fn(u8, V) -> T as FnOnce<(u8, V)>>::Output -> V)
58+
}
59+
} yields {
60+
"No possible solution"
61+
}
62+
63+
goal {
64+
forall<T, V> {
65+
exists<U> {
66+
Normalize(<fn(u8, V) -> T as FnOnce<(u8, V)>>::Output -> U)
67+
}
68+
}
69+
} yields {
70+
"Unique; substitution [?0 := !1_0], lifetime constraints []"
71+
}
72+
3573
goal {
3674
fn(u8, u32): FnOnce<(u8,u32)>
3775
} yields {

0 commit comments

Comments
 (0)