Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer.rs')
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 124 |
1 files changed, 109 insertions, 15 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 991acda14b..a4fcded42b 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -33,9 +33,10 @@ use std::{cell::OnceCell, convert::identity, iter}; use base_db::Crate; use either::Either; use hir_def::{ - AdtId, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId, - ItemContainerId, LocalFieldId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, - expr_store::{Body, ExpressionStore, HygieneId, path::Path}, + AdtId, AnonConstId, AssocItemId, ConstId, ConstParamId, DefWithBodyId, ExpressionStoreOwner, + FieldId, FunctionId, GenericDefId, GenericParamId, ItemContainerId, LocalFieldId, Lookup, + TraitId, TupleFieldId, TupleId, TypeAliasId, TypeOrConstParamId, VariantId, + expr_store::{Body, ConstExprOrigin, ExpressionStore, HygieneId, path::Path}, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId}, lang_item::LangItems, layout::Integer, @@ -105,7 +106,7 @@ pub fn infer_query_with_inspect<'db>( let _p = tracing::info_span!("infer_query").entered(); let resolver = def.resolver(db); let body = db.body(def); - let mut ctx = InferenceContext::new(db, def, &body, resolver); + let mut ctx = InferenceContext::new(db, ExpressionStoreOwner::Body(def), &body, resolver); if let Some(inspect) = inspect { ctx.table.infer_ctxt.attach_obligation_inspector(inspect); @@ -179,6 +180,68 @@ fn infer_cycle_result(db: &dyn HirDatabase, _: salsa::Id, _: DefWithBodyId) -> I } } +/// Infer types for all const expressions in an item's signature. +/// +/// This handles const expressions that appear in type positions within a generic +/// item's signature, such as array lengths (`[T; N]`) and const generic arguments +/// (`Foo<{ expr }>`). Each root expression is inferred independently within +/// a shared `InferenceContext`, accumulating results into a single `InferenceResult`. +fn infer_signature_query(db: &dyn HirDatabase, def: GenericDefId) -> InferenceResult { + let _p = tracing::info_span!("infer_signature_query").entered(); + let (_, store) = db.generic_params_and_store(def); + let mut roots = store.signature_const_expr_roots_with_origins().peekable(); + let Some(&(first, _)) = roots.peek() else { + return InferenceResult::new(crate::next_solver::default_types(db).types.error); + }; + + let resolver = def.resolver(db); + let owner = ExpressionStoreOwner::Signature(def); + + // Construct a synthetic `Body` to satisfy `InferenceContext`. + // The `body_expr` is set to the first root as a placeholder; we infer + // each root expression individually below rather than calling `infer_body`. + let body = Body { + // FIXME: Get rid of this clone + store: (*store).clone(), + params: Box::new([]), + self_param: None, + body_expr: first, + }; + + let mut ctx = InferenceContext::new(db, owner, &body, resolver); + + for (root_expr, origin) in roots { + let expected = match origin { + // Array lengths are always `usize`. + ConstExprOrigin::ArrayLength => Expectation::has_type(ctx.types.types.usize), + // Const parameter default: look up the param's declared type. + ConstExprOrigin::ConstParam(local_id) => Expectation::has_type(db.const_param_ty_ns( + ConstParamId::from_unchecked(TypeOrConstParamId { parent: def, local_id }), + )), + // Path const generic args: determining the expected type requires + // path resolution. + // FIXME + ConstExprOrigin::GenericArgsPath => Expectation::None, + }; + ctx.infer_expr(root_expr, &expected, ExprIsRead::Yes); + } + + ctx.type_inference_fallback(); + ctx.table.select_obligations_where_possible(); + ctx.resolve_all() +} + +fn infer_signature_cycle_result( + db: &dyn HirDatabase, + _: salsa::Id, + _: GenericDefId, +) -> InferenceResult { + InferenceResult { + has_errors: true, + ..InferenceResult::new(Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed)) + } +} + /// Binding modes inferred for patterns. /// <https://doc.rust-lang.org/reference/patterns.html#binding-modes> #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] @@ -555,9 +618,37 @@ impl InferenceResult { pub fn for_body(db: &dyn HirDatabase, def: DefWithBodyId) -> InferenceResult { infer_query(db, def) } + + /// Infer types for all const expressions in an item's signature. + /// + /// Returns an `InferenceResult` containing type information for array lengths, + /// const generic arguments, and other const expressions appearing in type + /// positions within the item's signature. + #[salsa::tracked(returns(ref), cycle_result = infer_signature_cycle_result)] + pub fn for_signature(db: &dyn HirDatabase, def: GenericDefId) -> InferenceResult { + infer_signature_query(db, def) + } } impl InferenceResult { + /// Look up inference results for a specific anonymous const in a signature. + /// + /// This delegates to [`Self::for_signature`] on the anon const's owner. + /// The returned `InferenceResult` contains types for *all* expressions in + /// the owner's signature store, not just this anon const's sub-tree. + /// Callers should index into it with `loc.expr` to get the root expression's + /// type. + // FIXME: This function doesn't make sense in that we can't return a full inference result here + // as the anon const is just part of an inference result. + pub fn for_anon_const(db: &dyn HirDatabase, id: AnonConstId) -> &InferenceResult { + match id.lookup(db).owner { + ExpressionStoreOwner::Signature(generic_def_id) => { + Self::for_signature(db, generic_def_id) + } + ExpressionStoreOwner::Body(def_with_body_id) => Self::for_body(db, def_with_body_id), + } + } + fn new(error_ty: Ty<'_>) -> Self { Self { method_resolutions: Default::default(), @@ -754,7 +845,7 @@ impl InferenceResult { #[derive(Clone, Debug)] pub(crate) struct InferenceContext<'body, 'db> { pub(crate) db: &'db dyn HirDatabase, - pub(crate) owner: DefWithBodyId, + pub(crate) owner: ExpressionStoreOwner, pub(crate) body: &'body Body, /// Generally you should not resolve things via this resolver. Instead create a TyLoweringContext /// and resolve the path via its methods. This will ensure proper error reporting. @@ -855,12 +946,18 @@ fn find_continuable<'a, 'db>( impl<'body, 'db> InferenceContext<'body, 'db> { fn new( db: &'db dyn HirDatabase, - owner: DefWithBodyId, + owner: ExpressionStoreOwner, body: &'body Body, resolver: Resolver<'db>, ) -> Self { - let trait_env = db.trait_environment_for_body(owner); - let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), Some(owner)); + let trait_env = match owner { + ExpressionStoreOwner::Signature(generic_def_id) => db.trait_environment(generic_def_id), + ExpressionStoreOwner::Body(def_with_body_id) => { + db.trait_environment_for_body(def_with_body_id) + } + }; + let table = + unify::InferenceTable::new(db, trait_env, resolver.krate(), owner.as_def_with_body()); let types = crate::next_solver::default_types(db); InferenceContext { result: InferenceResult::new(types.types.error), @@ -878,12 +975,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { return_coercion: None, db, owner, - generic_def: match owner { - DefWithBodyId::FunctionId(it) => it.into(), - DefWithBodyId::StaticId(it) => it.into(), - DefWithBodyId::ConstId(it) => it.into(), - DefWithBodyId::VariantId(it) => it.lookup(db).parent.into(), - }, + generic_def: owner.generic_def(db), body, traits_in_scope: resolver.traits_in_scope(db), resolver, @@ -908,7 +1000,9 @@ impl<'body, 'db> InferenceContext<'body, 'db> { fn target_features(&self) -> (&TargetFeatures<'db>, TargetFeatureIsSafeInTarget) { let (target_features, target_feature_is_safe) = self.target_features.get_or_init(|| { let target_features = match self.owner { - DefWithBodyId::FunctionId(id) => TargetFeatures::from_fn(self.db, id), + ExpressionStoreOwner::Body(DefWithBodyId::FunctionId(id)) => { + TargetFeatures::from_fn(self.db, id) + } _ => TargetFeatures::default(), }; let target_feature_is_safe = match &self.krate().workspace_data(self.db).target { |