diff options
| author | bors <bors@rust-lang.org> | 2022-09-12 14:24:57 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-09-12 14:24:57 +0000 |
| commit | b1a4ba3e84f021dd3e8eeaaca0eecaa758f8d32c (patch) | |
| tree | bcfc0d481aaba023760fbfa61bd362c3b25765be | |
| parent | b54d22d96a0ee459536702172f2b5557de7d74f8 (diff) | |
| parent | efb56160c91f0c1db511423fad8cc09fdd73d618 (diff) | |
| download | rust-b1a4ba3e84f021dd3e8eeaaca0eecaa758f8d32c.tar.gz rust-b1a4ba3e84f021dd3e8eeaaca0eecaa758f8d32c.zip | |
Auto merge of #13223 - lowr:fix/hir-proj-normalization, r=flodiebold
fix: handle lifetime variables in projection normalization Fixes #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.
| -rw-r--r-- | crates/hir-ty/src/db.rs | 8 | ||||
| -rw-r--r-- | crates/hir-ty/src/lib.rs | 14 | ||||
| -rw-r--r-- | crates/hir-ty/src/traits.rs | 17 | ||||
| -rw-r--r-- | crates/hir/src/lib.rs | 29 | ||||
| -rw-r--r-- | crates/ide/src/inlay_hints.rs | 68 |
5 files changed, 94 insertions, 42 deletions
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index b385b1cafae..dd5639f00d2 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -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( diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index a82a331d4b8..de4a5446e57 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -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. diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index 77afeb3217d..aaff894b34f 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -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; @@ -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. @@ -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, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 5fa4c151624..7dd891c86e8 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -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; @@ -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 { diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index d1b1d2c331a..93fcd7cad7a 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -1688,6 +1688,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( r#" |
