Skip to content

Commit

Permalink
Auto merge of rust-lang#13223 - lowr:fix/hir-proj-normalization, r=fl…
Browse files Browse the repository at this point in the history
…odiebold

fix: handle lifetime variables in projection normalization

Fixes rust-lang#12674

The problem is that we've been skipping the binders of normalized projections assuming they should be empty, but the assumption is unfortunately wrong. We may get back lifetime variables and should handle them before returning them as normalized projections. For those who are curious why we get those even though we treat all lifetimes as 'static, [this comment in chalk](https://github.com/rust-lang/chalk/blob/d875af0ff196dd6430b5f5fd87a640fa5ab59d1e/chalk-solve/src/infer/unify.rs#L888-L908) may be interesting.

I thought using `InferenceTable` would be cleaner than the other ways as it already has the methods for canonicalization, normalizing projection, and resolving variables, so moved goal building and trait solving logic to a new `HirDatabase` query. I made it transparent query as the query itself doesn't do much work but the eventual call to `HirDatabase::trait_solve_query()` does.
  • Loading branch information
bors committed Sep 12, 2022
2 parents b54d22d + efb5616 commit b1a4ba3
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 42 deletions.
8 changes: 8 additions & 0 deletions crates/hir-ty/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
id: chalk_db::AssociatedTyValueId,
) -> Arc<chalk_db::AssociatedTyValue>;

#[salsa::invoke(crate::traits::normalize_projection_query)]
#[salsa::transparent]
fn normalize_projection(
&self,
projection: crate::ProjectionTy,
env: Arc<crate::TraitEnvironment>,
) -> Option<crate::Ty>;

#[salsa::invoke(trait_solve_wait)]
#[salsa::transparent]
fn trait_solve(
Expand Down
14 changes: 0 additions & 14 deletions crates/hir-ty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,20 +196,6 @@ pub(crate) fn make_binders<T: HasInterner<Interner = Interner>>(
make_binders_with_count(db, usize::MAX, generics, value)
}

// FIXME: get rid of this
pub fn make_canonical<T: HasInterner<Interner = Interner>>(
value: T,
kinds: impl IntoIterator<Item = TyVariableKind>,
) -> Canonical<T> {
let kinds = kinds.into_iter().map(|tk| {
chalk_ir::CanonicalVarKind::new(
chalk_ir::VariableKind::Ty(tk),
chalk_ir::UniverseIndex::ROOT,
)
});
Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) }
}

// FIXME: get rid of this, just replace it by FnPointer
/// A function signature as seen by type inference: Several parameter types and
/// one return type.
Expand Down
17 changes: 14 additions & 3 deletions crates/hir-ty/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Trait solving using Chalk.

use std::env::var;
use std::{env::var, sync::Arc};

use chalk_ir::GoalData;
use chalk_recursive::Cache;
Expand All @@ -12,8 +12,9 @@ use stdx::panic_context;
use syntax::SmolStr;

use crate::{
db::HirDatabase, AliasEq, AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment,
Interner, Solution, TraitRefExt, Ty, TyKind, WhereClause,
db::HirDatabase, infer::unify::InferenceTable, AliasEq, AliasTy, Canonical, DomainGoal, Goal,
Guidance, InEnvironment, Interner, ProjectionTy, Solution, TraitRefExt, Ty, TyKind,
WhereClause,
};

/// This controls how much 'time' we give the Chalk solver before giving up.
Expand Down Expand Up @@ -64,6 +65,16 @@ impl TraitEnvironment {
}
}

pub(crate) fn normalize_projection_query(
db: &dyn HirDatabase,
projection: ProjectionTy,
env: Arc<TraitEnvironment>,
) -> Option<Ty> {
let mut table = InferenceTable::new(db, env.clone());
let ty = table.normalize_projection_ty(projection);
Some(table.resolve_completely(ty))
}

/// Solve a trait goal using Chalk.
pub(crate) fn trait_solve_query(
db: &dyn HirDatabase,
Expand Down
29 changes: 4 additions & 25 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,9 @@ use hir_ty::{
primitive::UintTy,
subst_prefix,
traits::FnTrait,
AliasEq, AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast,
ClosureId, DebruijnIndex, GenericArgData, InEnvironment, Interner, ParamKind,
QuantifiedWhereClause, Scalar, Solution, Substitution, TraitEnvironment, TraitRefExt, Ty,
TyBuilder, TyDefId, TyExt, TyKind, TyVariableKind, WhereClause,
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId,
GenericArgData, Interner, ParamKind, QuantifiedWhereClause, Scalar, Substitution,
TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyDefId, TyExt, TyKind, WhereClause,
};
use itertools::Itertools;
use nameres::diagnostics::DefDiagnosticKind;
Expand Down Expand Up @@ -2892,28 +2891,8 @@ impl Type {
}
})
.build();
let goal = hir_ty::make_canonical(
InEnvironment::new(
&self.env.env,
AliasEq {
alias: AliasTy::Projection(projection),
ty: TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0))
.intern(Interner),
}
.cast(Interner),
),
[TyVariableKind::General].into_iter(),
);

match db.trait_solve(self.env.krate, goal)? {
Solution::Unique(s) => s
.value
.subst
.as_slice(Interner)
.first()
.map(|ty| self.derived(ty.assert_ty_ref(Interner).clone())),
Solution::Ambig(_) => None,
}
db.normalize_projection(projection, self.env.clone()).map(|ty| self.derived(ty))
}

pub fn is_copy(&self, db: &dyn HirDatabase) -> bool {
Expand Down
68 changes: 68 additions & 0 deletions crates/ide/src/inlay_hints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1687,6 +1687,74 @@ fn main() {
);
}

#[test]
fn iterator_hint_regression_issue_12674() {
// Ensure we don't crash while solving the projection type of iterators.
check_expect(
InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
r#"
//- minicore: iterators
struct S<T>(T);
impl<T> S<T> {
fn iter(&self) -> Iter<'_, T> { loop {} }
}
struct Iter<'a, T: 'a>(&'a T);
impl<'a, T> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> { loop {} }
}
struct Container<'a> {
elements: S<&'a str>,
}
struct SliceIter<'a, T>(&'a T);
impl<'a, T> Iterator for SliceIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> { loop {} }
}
fn main(a: SliceIter<'_, Container>) {
a
.filter_map(|c| Some(c.elements.iter().filter_map(|v| Some(v))))
.map(|e| e);
}
"#,
expect![[r#"
[
InlayHint {
range: 484..554,
kind: ChainingHint,
label: [
"impl Iterator<Item = impl Iterator<Item = &&str>>",
],
tooltip: Some(
HoverRanged(
FileId(
0,
),
484..554,
),
),
},
InlayHint {
range: 484..485,
kind: ChainingHint,
label: [
"SliceIter<Container>",
],
tooltip: Some(
HoverRanged(
FileId(
0,
),
484..485,
),
),
},
]
"#]],
);
}

#[test]
fn infer_call_method_return_associated_types_with_generic() {
check_types(
Expand Down

0 comments on commit b1a4ba3

Please sign in to comment.