Skip to content

Commit 9c768da

Browse files
committed
Fix built-in Fn impls when generics are involved
There were two problems: - the FnDef impl was using a wrong substitution for the FnDef (calling `builder.substitution_in_scope()`, which wasn't even necessarily the same number of parameters -- not sure what the intention was there) - when looking for `Normalize` clauses, the self type wasn't generalized (so bound variables were handled wrongly). (This lead to crashes when trying to integrate it in rust-analyzer: rust-lang/rust-analyzer#4982)
1 parent b8a90d8 commit 9c768da

File tree

3 files changed

+84
-11
lines changed

3 files changed

+84
-11
lines changed

chalk-solve/src/clauses/builtin_traits.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,19 @@ pub fn add_builtin_assoc_program_clauses<I: Interner>(
5555
well_known: WellKnownTrait,
5656
self_ty: Ty<I>,
5757
) -> Result<(), Floundered> {
58-
match well_known {
59-
WellKnownTrait::FnOnce => {
60-
fn_family::add_fn_trait_program_clauses(db, builder, well_known, self_ty)?;
58+
// If `self_ty` contains bound vars, we want to universally quantify them.
59+
// `Generalize` collects them for us.
60+
let generalized = generalize::Generalize::apply(db.interner(), &self_ty);
61+
62+
builder.push_binders(&generalized, |builder, self_ty| {
63+
match well_known {
64+
WellKnownTrait::FnOnce => {
65+
fn_family::add_fn_trait_program_clauses(db, builder, well_known, self_ty)?;
66+
}
67+
_ => {}
6168
}
62-
_ => {}
63-
}
64-
Ok(())
69+
Ok(())
70+
})
6571
}
6672

6773
/// Given a trait ref `T0: Trait` and a list of types `U0..Un`, pushes a clause of the form

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

+4-5
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ fn push_clauses_for_apply<I: Interner>(
6161
self_ty: Ty<I>,
6262
inputs_and_output: &Binders<FnDefInputsAndOutputDatum<I>>,
6363
) {
64+
eprintln!(
65+
"push_clauses_for_apply({:?}, {:?}, {:#?}, {:?})",
66+
well_known, trait_id, self_ty, inputs_and_output
67+
);
6468
let interner = db.interner();
6569
builder.push_binders(inputs_and_output, |builder, inputs_and_output| {
6670
let arg_sub = inputs_and_output
@@ -102,11 +106,6 @@ pub fn add_fn_trait_program_clauses<I: Interner>(
102106
let bound = fn_def_datum
103107
.binders
104108
.substitute(builder.interner(), &apply.substitution);
105-
let self_ty = ApplicationTy {
106-
name: apply.name,
107-
substitution: builder.substitution_in_scope(),
108-
}
109-
.intern(interner);
110109
push_clauses_for_apply(
111110
db,
112111
builder,

tests/test/fn_def.rs

+68
Original file line numberDiff line numberDiff line change
@@ -96,5 +96,73 @@ fn fn_def_implements_fn_traits() {
9696
} yields {
9797
"Unique"
9898
}
99+
100+
goal {
101+
bar: Fn<(i32,)>
102+
} yields {
103+
"Unique"
104+
}
105+
106+
goal {
107+
Normalize(<bar as FnOnce<(i32,)>>::Output -> ())
108+
} yields {
109+
"Unique"
110+
}
111+
112+
goal {
113+
baz: Fn<(i32,)>
114+
} yields {
115+
"Unique"
116+
}
117+
118+
goal {
119+
Normalize(<baz as FnOnce<(i32,)>>::Output -> u8)
120+
} yields {
121+
"Unique"
122+
}
123+
}
124+
}
125+
126+
#[test]
127+
fn generic_fn_implements_fn_traits() {
128+
test! {
129+
program {
130+
#[lang(fn_once)]
131+
trait FnOnce<Args> {
132+
type Output;
133+
}
134+
135+
#[lang(fn_mut)]
136+
trait FnMut<Args> where Self: FnOnce<Args> { }
137+
138+
#[lang(fn)]
139+
trait Fn<Args> where Self: FnMut<Args> { }
140+
141+
fn foo<T>(t: T) -> T;
142+
}
143+
144+
goal {
145+
exists<T> { foo<T>: Fn<(T,)> }
146+
} yields {
147+
"Unique"
148+
}
149+
150+
goal {
151+
forall<T> { foo<T>: Fn<(T,)> }
152+
} yields {
153+
"Unique"
154+
}
155+
156+
goal {
157+
exists<T> { Normalize(<foo<T> as FnOnce<(T,)>>::Output -> T) }
158+
} yields[SolverChoice::recursive()] {
159+
"Unique"
160+
}
161+
162+
goal {
163+
forall<T> { Normalize(<foo<T> as FnOnce<(T,)>>::Output -> T) }
164+
} yields {
165+
"Unique"
166+
}
99167
}
100168
}

0 commit comments

Comments
 (0)