Skip to content

Commit 15ef5f5

Browse files
committed
Auto merge of #14641 - lowr:fix/obligation-for-value-path, r=Veykril
Register obligations during path inference Fixes #14635 When we infer path expressions that resolve to some generic item, we need to consider their generic bounds. For example, when we resolve a path `Into::into` to `fn into<?0, ?1>` (note that `?0` is the self type of trait ref), we should register an obligation `?0: Into<?1>` or else their relationship would be lost. Relevant part in rustc is [`add_required_obligations_with_code()`] that's called in [`instantiate_value_path()`]. [`instantiate_value_path()`]: https://github.com/rust-lang/rust/blob/3462f79e94f466a56ddaccfcdd3a3d44dd1dda9f/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs#L1052 [`add_required_obligations_with_code()`]: https://github.com/rust-lang/rust/blob/3462f79e94f466a56ddaccfcdd3a3d44dd1dda9f/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs#L1411
2 parents 65ac9f4 + 12ba5ca commit 15ef5f5

File tree

7 files changed

+142
-51
lines changed

7 files changed

+142
-51
lines changed

crates/hir-ty/src/builder.rs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use crate::{
1818
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
1919
to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
2020
GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
21-
ValueTyDefId,
2221
};
2322

2423
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -362,21 +361,4 @@ impl TyBuilder<Binders<Ty>> {
362361
pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> {
363362
TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def))
364363
}
365-
366-
pub fn value_ty(
367-
db: &dyn HirDatabase,
368-
def: ValueTyDefId,
369-
parent_subst: Option<Substitution>,
370-
) -> TyBuilder<Binders<Ty>> {
371-
let poly_value_ty = db.value_ty(def);
372-
let id = match def.to_generic_def_id() {
373-
Some(id) => id,
374-
None => {
375-
// static items
376-
assert!(parent_subst.is_none());
377-
return TyBuilder::new_empty(poly_value_ty);
378-
}
379-
};
380-
TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_value_ty)
381-
}
382364
}

crates/hir-ty/src/chalk_db.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -803,17 +803,17 @@ pub(crate) fn adt_variance_query(
803803
)
804804
}
805805

806+
/// Returns instantiated predicates.
806807
pub(super) fn convert_where_clauses(
807808
db: &dyn HirDatabase,
808809
def: GenericDefId,
809810
substs: &Substitution,
810811
) -> Vec<chalk_ir::QuantifiedWhereClause<Interner>> {
811-
let generic_predicates = db.generic_predicates(def);
812-
let mut result = Vec::with_capacity(generic_predicates.len());
813-
for pred in generic_predicates.iter() {
814-
result.push(pred.clone().substitute(Interner, substs));
815-
}
816-
result
812+
db.generic_predicates(def)
813+
.iter()
814+
.cloned()
815+
.map(|pred| pred.substitute(Interner, substs))
816+
.collect()
817817
}
818818

819819
pub(super) fn generic_predicate_to_inline_bound(

crates/hir-ty/src/infer.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
use std::sync::Arc;
1717
use std::{convert::identity, ops::Index};
1818

19-
use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
19+
use chalk_ir::{
20+
cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety,
21+
Scalar, TypeFlags,
22+
};
2023
use either::Either;
2124
use hir_def::{
2225
body::Body,
@@ -798,7 +801,10 @@ impl<'a> InferenceContext<'a> {
798801
self.table.insert_type_vars_shallow(ty)
799802
}
800803

801-
fn insert_type_vars(&mut self, ty: Ty) -> Ty {
804+
fn insert_type_vars<T>(&mut self, ty: T) -> T
805+
where
806+
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
807+
{
802808
self.table.insert_type_vars(ty)
803809
}
804810

@@ -875,7 +881,10 @@ impl<'a> InferenceContext<'a> {
875881
/// type annotation (e.g. from a let type annotation, field type or function
876882
/// call). `make_ty` handles this already, but e.g. for field types we need
877883
/// to do it as well.
878-
fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
884+
fn normalize_associated_types_in<T>(&mut self, ty: T) -> T
885+
where
886+
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
887+
{
879888
self.table.normalize_associated_types_in(ty)
880889
}
881890

crates/hir-ty/src/infer/path.rs

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use chalk_ir::cast::Cast;
44
use hir_def::{
55
path::{Path, PathSegment},
66
resolver::{ResolveValueResult, TypeNs, ValueNs},
7-
AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup,
7+
AdtId, AssocItemId, EnumVariantId, GenericDefId, ItemContainerId, Lookup,
88
};
99
use hir_expand::name::Name;
1010
use stdx::never;
@@ -13,22 +13,33 @@ use crate::{
1313
builder::ParamKind,
1414
consteval,
1515
method_resolution::{self, VisibleFromModule},
16+
to_chalk_trait_id,
1617
utils::generics,
1718
InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
1819
ValueTyDefId,
1920
};
2021

2122
use super::{ExprOrPatId, InferenceContext, TraitRef};
2223

23-
impl<'a> InferenceContext<'a> {
24+
impl InferenceContext<'_> {
2425
pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
25-
let ty = self.resolve_value_path(path, id)?;
26-
let ty = self.insert_type_vars(ty);
26+
let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? {
27+
ValuePathResolution::GenericDef(value_def, generic_def, substs) => {
28+
(value_def, generic_def, substs)
29+
}
30+
ValuePathResolution::NonGeneric(ty) => return Some(ty),
31+
};
32+
let substs = self.insert_type_vars(substs);
33+
let substs = self.normalize_associated_types_in(substs);
34+
35+
self.add_required_obligations_for_value_path(generic_def, &substs);
36+
37+
let ty = self.db.value_ty(value_def).substitute(Interner, &substs);
2738
let ty = self.normalize_associated_types_in(ty);
2839
Some(ty)
2940
}
3041

31-
fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
42+
fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<ValuePathResolution> {
3243
let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
3344
let last = path.segments().last()?;
3445

@@ -56,9 +67,9 @@ impl<'a> InferenceContext<'a> {
5667
}
5768
};
5869

59-
let typable: ValueTyDefId = match value {
70+
let value_def = match value {
6071
ValueNs::LocalBinding(pat) => match self.result.type_of_binding.get(pat) {
61-
Some(ty) => return Some(ty.clone()),
72+
Some(ty) => return Some(ValuePathResolution::NonGeneric(ty.clone())),
6273
None => {
6374
never!("uninferred pattern?");
6475
return None;
@@ -82,36 +93,81 @@ impl<'a> InferenceContext<'a> {
8293
let substs = generics.placeholder_subst(self.db);
8394
let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
8495
if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() {
85-
let ty = self.db.value_ty(struct_id.into()).substitute(Interner, &substs);
86-
return Some(ty);
96+
return Some(ValuePathResolution::GenericDef(
97+
struct_id.into(),
98+
struct_id.into(),
99+
substs.clone(),
100+
));
87101
} else {
88102
// FIXME: report error, invalid Self reference
89103
return None;
90104
}
91105
}
92-
ValueNs::GenericParam(it) => return Some(self.db.const_param_ty(it)),
106+
ValueNs::GenericParam(it) => {
107+
return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty(it)))
108+
}
93109
};
94110

95111
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
96-
let substs = ctx.substs_from_path(path, typable, true);
112+
let substs = ctx.substs_from_path(path, value_def, true);
97113
let substs = substs.as_slice(Interner);
98114
let parent_substs = self_subst.or_else(|| {
99-
let generics = generics(self.db.upcast(), typable.to_generic_def_id()?);
115+
let generics = generics(self.db.upcast(), value_def.to_generic_def_id()?);
100116
let parent_params_len = generics.parent_generics()?.len();
101117
let parent_args = &substs[substs.len() - parent_params_len..];
102118
Some(Substitution::from_iter(Interner, parent_args))
103119
});
104120
let parent_substs_len = parent_substs.as_ref().map_or(0, |s| s.len(Interner));
105121
let mut it = substs.iter().take(substs.len() - parent_substs_len).cloned();
106-
let ty = TyBuilder::value_ty(self.db, typable, parent_substs)
122+
123+
let Some(generic_def) = value_def.to_generic_def_id() else {
124+
// `value_def` is the kind of item that can never be generic (i.e. statics, at least
125+
// currently). We can just skip the binders to get its type.
126+
let (ty, binders) = self.db.value_ty(value_def).into_value_and_skipped_binders();
127+
stdx::always!(
128+
parent_substs.is_none() && binders.is_empty(Interner),
129+
"non-empty binders for non-generic def",
130+
);
131+
return Some(ValuePathResolution::NonGeneric(ty));
132+
};
133+
let builder = TyBuilder::subst_for_def(self.db, generic_def, parent_substs);
134+
let substs = builder
107135
.fill(|x| {
108136
it.next().unwrap_or_else(|| match x {
109137
ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner),
110138
ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()),
111139
})
112140
})
113141
.build();
114-
Some(ty)
142+
143+
Some(ValuePathResolution::GenericDef(value_def, generic_def, substs))
144+
}
145+
146+
fn add_required_obligations_for_value_path(&mut self, def: GenericDefId, subst: &Substitution) {
147+
let predicates = self.db.generic_predicates(def);
148+
for predicate in predicates.iter() {
149+
let (predicate, binders) =
150+
predicate.clone().substitute(Interner, &subst).into_value_and_skipped_binders();
151+
// Quantified where clauses are not yet handled.
152+
stdx::always!(binders.is_empty(Interner));
153+
self.push_obligation(predicate.cast(Interner));
154+
}
155+
156+
// We need to add `Self: Trait` obligation when `def` is a trait assoc item.
157+
let container = match def {
158+
GenericDefId::FunctionId(id) => id.lookup(self.db.upcast()).container,
159+
GenericDefId::ConstId(id) => id.lookup(self.db.upcast()).container,
160+
_ => return,
161+
};
162+
163+
if let ItemContainerId::TraitId(trait_) = container {
164+
let param_len = generics(self.db.upcast(), def).len_self();
165+
let parent_subst =
166+
Substitution::from_iter(Interner, subst.iter(Interner).skip(param_len));
167+
let trait_ref =
168+
TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: parent_subst };
169+
self.push_obligation(trait_ref.cast(Interner));
170+
}
115171
}
116172

117173
fn resolve_assoc_item(
@@ -307,3 +363,10 @@ impl<'a> InferenceContext<'a> {
307363
Some((ValueNs::EnumVariantId(variant), subst.clone()))
308364
}
309365
}
366+
367+
enum ValuePathResolution {
368+
// It's awkward to wrap a single ID in two enums, but we need both and this saves fallible
369+
// conversion between them + `unwrap()`.
370+
GenericDef(ValueTyDefId, GenericDefId, Substitution),
371+
NonGeneric(Ty),
372+
}

crates/hir-ty/src/infer/unify.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,10 @@ impl<'a> InferenceTable<'a> {
231231
/// type annotation (e.g. from a let type annotation, field type or function
232232
/// call). `make_ty` handles this already, but e.g. for field types we need
233233
/// to do it as well.
234-
pub(crate) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
234+
pub(crate) fn normalize_associated_types_in<T>(&mut self, ty: T) -> T
235+
where
236+
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
237+
{
235238
fold_tys(
236239
ty,
237240
|ty, _| match ty.kind(Interner) {
@@ -720,7 +723,10 @@ impl<'a> InferenceTable<'a> {
720723
}
721724
}
722725

723-
pub(super) fn insert_type_vars(&mut self, ty: Ty) -> Ty {
726+
pub(super) fn insert_type_vars<T>(&mut self, ty: T) -> T
727+
where
728+
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
729+
{
724730
fold_tys_and_consts(
725731
ty,
726732
|x, _| match x {

crates/hir-ty/src/method_resolution.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -742,9 +742,8 @@ fn find_matching_impl(
742742
actual_trait_ref: TraitRef,
743743
) -> Option<(Arc<ImplData>, Substitution)> {
744744
let db = table.db;
745-
loop {
746-
let impl_ = impls.next()?;
747-
let r = table.run_in_snapshot(|table| {
745+
impls.find_map(|impl_| {
746+
table.run_in_snapshot(|table| {
748747
let impl_data = db.impl_data(impl_);
749748
let impl_substs =
750749
TyBuilder::subst_for_def(db, impl_, None).fill_with_inference_vars(table).build();
@@ -762,11 +761,8 @@ fn find_matching_impl(
762761
.map(|b| b.cast(Interner));
763762
let goal = crate::Goal::all(Interner, wcs);
764763
table.try_obligation(goal).map(|_| (impl_data, table.resolve_completely(impl_substs)))
765-
});
766-
if r.is_some() {
767-
break r;
768-
}
769-
}
764+
})
765+
})
770766
}
771767

772768
fn is_inherent_impl_coherent(

crates/hir-ty/src/tests/traits.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4375,3 +4375,38 @@ fn derive_macro_bounds() {
43754375
"#,
43764376
);
43774377
}
4378+
4379+
#[test]
4380+
fn trait_obligations_should_be_registered_during_path_inference() {
4381+
check_types(
4382+
r#"
4383+
//- minicore: fn, from
4384+
struct S<T>(T);
4385+
fn map<T, U, F: FnOnce(T) -> S<U>>(_: T, _: F) -> U { loop {} }
4386+
4387+
fn test(v: S<i32>) {
4388+
let res = map(v, Into::into);
4389+
//^^^ i32
4390+
}
4391+
"#,
4392+
);
4393+
}
4394+
4395+
#[test]
4396+
fn fn_obligation_should_be_registered_during_path_inference() {
4397+
check_types(
4398+
r#"
4399+
//- minicore: fn, from
4400+
struct S<T>(T);
4401+
impl<T> S<T> {
4402+
fn foo<U: Into<S<T>>>(_: U) -> Self { loop {} }
4403+
}
4404+
fn map<T, U, F: FnOnce(T) -> U>(_: T, _: F) -> U { loop {} }
4405+
4406+
fn test(v: S<i32>) {
4407+
let res = map(v, S::foo);
4408+
//^^^ S<i32>
4409+
}
4410+
"#,
4411+
);
4412+
}

0 commit comments

Comments
 (0)