Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/hir-def/src/expr_store/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ pub(crate) fn lower_type_ref(
(store, source_map, type_ref)
}

pub(crate) fn lower_generic_params(
pub fn lower_generic_params(
db: &dyn DefDatabase,
module: ModuleId,
def: GenericDefId,
Expand Down
8 changes: 8 additions & 0 deletions crates/hir-ty/src/consteval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use crate::{
db::{AnonConstId, AnonConstLoc, GeneralConstId, HirDatabase},
display::DisplayTarget,
generics::Generics,
lower::LoweringMode,
mir::{MirEvalError, MirLowerError, pad16},
next_solver::{
Allocation, Const, ConstKind, Consts, DbInterner, DefaultAny, GenericArgs, ParamConst,
Expand Down Expand Up @@ -305,6 +306,7 @@ pub(crate) enum CreateConstError<'db> {
DoesNotResolve,
ConstHasGenerics,
UnderscoreExpr,
AnonConstInterningDisabled,
TypeMismatch {
#[expect(unused, reason = "will need this for diagnostics")]
actual: Ty<'db>,
Expand Down Expand Up @@ -355,6 +357,7 @@ pub(crate) fn create_anon_const<'a, 'db>(
expected_ty: Ty<'db>,
generics: &dyn Fn() -> &'a Generics<'db>,
create_var: Option<&mut dyn FnMut(Span) -> Const<'db>>,
lowering_mode: LoweringMode,
forbid_params_after: Option<u32>,
) -> Result<Const<'db>, CreateConstError<'db>> {
match &store[expr] {
Expand All @@ -374,6 +377,10 @@ pub(crate) fn create_anon_const<'a, 'db>(
konst
}
_ => {
let Some(token) = lowering_mode.allow_tracked_structs() else {
return Err(CreateConstError::AnonConstInterningDisabled);
};

let allow_using_generic_params = forbid_params_after.is_none();
let konst = AnonConstId::new(
interner.db,
Expand All @@ -383,6 +390,7 @@ pub(crate) fn create_anon_const<'a, 'db>(
ty: StoredEarlyBinder::bind(expected_ty.store()),
allow_using_generic_params,
},
token,
);
let args = if allow_using_generic_params {
GenericArgs::identity_for_item(interner, owner.generic_def(interner.db).into())
Expand Down
11 changes: 9 additions & 2 deletions crates/hir-ty/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
consteval::ConstEvalError,
dyn_compatibility::DynCompatibilityViolation,
layout::{Layout, LayoutError},
lower::{GenericDefaults, TypeAliasBounds},
lower::{GenericDefaults, TrackedStructToken, TypeAliasBounds},
mir::{BorrowckResult, MirBody, MirLowerError},
next_solver::{
Allocation, Clause, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredClauses,
Expand Down Expand Up @@ -421,13 +421,20 @@ pub struct AnonConstLoc {
pub(crate) allow_using_generic_params: bool,
}

#[salsa_macros::interned(debug, no_lifetime, revisions = usize::MAX)]
#[salsa_macros::interned(debug, no_lifetime, revisions = usize::MAX, constructor = new_)]
#[derive(PartialOrd, Ord)]
pub struct AnonConstId {
#[returns(ref)]
pub loc: AnonConstLoc,
}

impl AnonConstId {
pub(crate) fn new(db: &dyn DefDatabase, loc: AnonConstLoc, token: TrackedStructToken) -> Self {
_ = token;
AnonConstId::new_(db, loc)
}
}

impl HasModule for AnonConstId {
fn module(&self, db: &dyn DefDatabase) -> ModuleId {
self.loc(db).owner.module(db)
Expand Down
12 changes: 10 additions & 2 deletions crates/hir-ty/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ use crate::{
unify::resolve_completely::WriteBackCtxt,
},
lower::{
ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic,
ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, LoweringMode,
diagnostics::TyLoweringDiagnostic,
},
method_resolution::CandidateId,
next_solver::{
Expand All @@ -116,13 +117,14 @@ use cast::{CastCheck, CastError};

/// The entry point of type inference.
fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> InferenceResult {
infer_query_with_inspect(db, def, None)
infer_query_with_inspect(db, def, None, LoweringMode::Analysis)
}

pub fn infer_query_with_inspect<'db>(
db: &'db dyn HirDatabase,
def: DefWithBodyId,
inspect: Option<ObligationInspector<'db>>,
lowering_mode: LoweringMode,
) -> InferenceResult {
let _p = tracing::info_span!("infer_query").entered();
let resolver = def.resolver(db);
Expand All @@ -135,6 +137,7 @@ pub fn infer_query_with_inspect<'db>(
&body.store,
resolver,
true,
lowering_mode,
);

if let Some(inspect) = inspect {
Expand Down Expand Up @@ -202,6 +205,7 @@ fn infer_anon_const_query(db: &dyn HirDatabase, def: AnonConstId) -> InferenceRe
store,
resolver,
loc.allow_using_generic_params,
LoweringMode::Analysis,
);

ctx.infer_expr(
Expand Down Expand Up @@ -1236,6 +1240,7 @@ pub(crate) struct InferenceContext<'body, 'db> {
pub(crate) store_owner: ExpressionStoreOwnerId,
pub(crate) generic_def: GenericDefId,
pub(crate) store: &'body ExpressionStore,
pub(crate) lowering_mode: LoweringMode,
/// 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.
pub(crate) resolver: Resolver<'db>,
Expand Down Expand Up @@ -1335,6 +1340,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
store: &'body ExpressionStore,
resolver: Resolver<'db>,
allow_using_generic_params: bool,
lowering_mode: LoweringMode,
) -> Self {
let trait_env = db.trait_environment(generic_def);
let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), store_owner);
Expand Down Expand Up @@ -1369,6 +1375,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
vars_emitted_type_must_be_known_for: FxHashSet::default(),
deferred_call_resolutions: FxHashMap::default(),
defined_anon_consts: RefCell::new(ThinVec::new()),
lowering_mode,
}
}

Expand Down Expand Up @@ -1969,6 +1976,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
expected_ty,
&|| self.generics(),
Some(&mut |span| self.table.next_const_var(span)),
self.lowering_mode,
(!(allow_using_generic_params && self.allow_using_generic_params)).then_some(0),
);

Expand Down
4 changes: 2 additions & 2 deletions crates/hir-ty/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ pub use infer::{
};
pub use lower::{
GenericDefaults, GenericDefaultsRef, GenericPredicates, ImplTraits, LifetimeElisionKind,
TyDefId, TyLoweringContext, TyLoweringInferVarsCtx, TyLoweringResult, ValueTyDefId,
diagnostics::*,
LoweringMode, TyDefId, TyLoweringContext, TyLoweringInferVarsCtx, TyLoweringResult,
ValueTyDefId, diagnostics::*,
};
pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db};
pub use target_feature::TargetFeatures;
Expand Down
35 changes: 35 additions & 0 deletions crates/hir-ty/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,33 @@ pub trait TyLoweringInferVarsCtx<'db> {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LoweringMode {
Analysis,
Ide,
}

pub(crate) use self::tracked_struct_token::TrackedStructToken;
mod tracked_struct_token {
use super::LoweringMode;

/// A token that is required to construct tracked structs.
/// This exists to prevent one from accidentally creating a tracked struct outside of a query which may happen for some codepaths.
pub(crate) struct TrackedStructToken {
// #[non_exhaustive] doesn't work for us here, we want it module focused.
_private: (),
}

impl LoweringMode {
pub(crate) fn allow_tracked_structs(self) -> Option<TrackedStructToken> {
match self {
LoweringMode::Analysis => Some(TrackedStructToken { _private: () }),
LoweringMode::Ide => None,
}
}
}
}

pub struct TyLoweringContext<'db, 'a> {
pub db: &'db dyn HirDatabase,
pub(crate) interner: DbInterner<'db>,
Expand All @@ -211,6 +238,7 @@ pub struct TyLoweringContext<'db, 'a> {
generics: &'a OnceCell<Generics<'db>>,
in_binders: DebruijnIndex,
impl_trait_mode: ImplTraitLoweringState,
interning_mode: LoweringMode,
/// Tracks types with explicit `?Sized` bounds.
pub(crate) unsized_types: FxHashSet<Ty<'db>>,
pub(crate) diagnostics: ThinVec<TyLoweringDiagnostic>,
Expand Down Expand Up @@ -247,6 +275,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
store,
in_binders,
impl_trait_mode,
interning_mode: LoweringMode::Analysis,
unsized_types: FxHashSet::default(),
diagnostics: ThinVec::new(),
lifetime_elision,
Expand All @@ -261,6 +290,11 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
self.lifetime_elision = lifetime_elision;
}

pub(crate) fn with_interning_mode(mut self, interning_mode: LoweringMode) -> Self {
self.interning_mode = interning_mode;
self
}

pub(crate) fn with_debruijn<T>(
&mut self,
debruijn: DebruijnIndex,
Expand Down Expand Up @@ -384,6 +418,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
const_type,
&|| self.generics.get_or_init(|| generics(self.db, self.generic_def)),
create_var,
self.interning_mode,
self.forbid_params_after,
);

Expand Down
98 changes: 94 additions & 4 deletions crates/hir-ty/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
//! Trait solving using next trait solver.

use std::hash::Hash;
use std::{cell::OnceCell, hash::Hash};

use base_db::Crate;
use hir_def::{
AdtId, AssocItemId, HasModule, ImplId, Lookup, TraitId,
AdtId, AssocItemId, ExpressionStoreOwnerId, GenericDefId, HasModule, ImplId, Lookup, TraitId,
expr_store::ExpressionStore,
hir::generics::WherePredicate,
lang_item::LangItems,
nameres::DefMap,
resolver::Resolver,
signatures::{
ConstFlags, ConstSignature, EnumFlags, EnumSignature, FnFlags, FunctionSignature,
StructFlags, StructSignature, TraitFlags, TraitSignature, TypeAliasFlags,
Expand All @@ -16,17 +19,20 @@ use hir_def::{
use hir_expand::name::Name;
use intern::sym;
use rustc_type_ir::{
TypingMode,
TypeVisitableExt, TypingMode,
inherent::{BoundExistentialPredicates, IntoKind},
};

use crate::{
Span,
LifetimeElisionKind, Span, TyLoweringContext,
db::HirDatabase,
generics::Generics,
lower::LoweringMode,
next_solver::{
DbInterner, GenericArgs, ParamEnv, StoredClauses, Ty, TyKind,
infer::{
DbInternerInferExt, InferCtxt,
select::EvaluationResult,
traits::{Obligation, ObligationCause},
},
obligation_ctxt::ObligationCtxt,
Expand Down Expand Up @@ -153,6 +159,90 @@ pub fn implements_trait_unique_with_infcx<'db>(
infcx.predicate_must_hold_modulo_regions(&obligation)
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WherePredicateEvaluation {
Holds,
NotProven,
HasErrors,
NoObligations,
}

/// This should not be used in `hir-ty`, only in `hir`.
/// This is exposed to allow the IDE to evaluate arbitrary predicates.
pub fn where_predicate_must_hold<'db>(
db: &'db dyn HirDatabase,
resolver: &Resolver<'db>,
store: &ExpressionStore,
def: ExpressionStoreOwnerId,
generic_def: GenericDefId,
env: ParamEnvAndCrate<'db>,
predicate: &WherePredicate,
) -> WherePredicateEvaluation {
let interner = DbInterner::new_with(db, env.krate);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
let generics = OnceCell::<Generics<'db>>::new();
let mut ctx = TyLoweringContext::new(
Copy link
Copy Markdown
Contributor

@ChayimFriedman2 ChayimFriedman2 May 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with lowering here is that it may intern some things (blocks, anon consts, items). This amplifies the problem we already have by lowering in hir.

IMO the only solution is to have a mode for TyLoweringContext where it never interns anything.

View changes since the review

db,
resolver,
store,
def,
generic_def,
&generics,
LifetimeElisionKind::Infer,
)
.with_interning_mode(LoweringMode::Ide);
let clauses =
ctx.lower_where_predicate(predicate, false).map(|(clause, _)| clause).collect::<Vec<_>>();

if !ctx.diagnostics.is_empty()
|| clauses.iter().any(|clause| clause.as_predicate().references_error())
{
return WherePredicateEvaluation::HasErrors;
}

if clauses.is_empty() {
return if ctx.unsized_types.is_empty() {
WherePredicateEvaluation::HasErrors
} else {
WherePredicateEvaluation::NoObligations
};
}

let result = infcx.probe(|snapshot| {
let mut ocx = ObligationCtxt::new(&infcx);
for clause in clauses {
let obligation = Obligation::new(
interner,
ObligationCause::dummy(),
env.param_env,
clause.as_predicate(),
);
ocx.register_obligation(obligation);
}

let mut result = EvaluationResult::EvaluatedToOk;
for error in ocx.evaluate_obligations_error_on_ambiguity() {
if error.is_true_error() {
return EvaluationResult::EvaluatedToErr;
}
result = result.max(EvaluationResult::EvaluatedToAmbig);
}
if infcx.opaque_types_added_in_snapshot(snapshot) {
result.max(EvaluationResult::EvaluatedToOkModuloOpaqueTypes)
} else if infcx.region_constraints_added_in_snapshot(snapshot) {
result.max(EvaluationResult::EvaluatedToOkModuloRegions)
} else {
result
}
});

if result.must_apply_modulo_regions() {
WherePredicateEvaluation::Holds
} else {
WherePredicateEvaluation::NotProven
}
}

pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id: ImplId) -> bool {
let self_ty = db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip();
let self_ty = self_ty.kind();
Expand Down
Loading
Loading