1use std::{cell::OnceCell, hash::Hash};
4
5use base_db::Crate;
6use hir_def::{
7 AdtId, AssocItemId, ExpressionStoreOwnerId, GenericDefId, HasModule, ImplId, Lookup, TraitId,
8 expr_store::ExpressionStore,
9 hir::generics::WherePredicate,
10 lang_item::LangItems,
11 nameres::DefMap,
12 resolver::Resolver,
13 signatures::{
14 ConstFlags, ConstSignature, EnumFlags, EnumSignature, FnFlags, FunctionSignature,
15 StructFlags, StructSignature, TraitFlags, TraitSignature, TypeAliasFlags,
16 TypeAliasSignature, UnionSignature,
17 },
18};
19use hir_expand::name::Name;
20use intern::sym;
21use rustc_type_ir::{
22 TypeVisitableExt, TypingMode,
23 inherent::{BoundExistentialPredicates, IntoKind, Ty as _},
24};
25
26use crate::{
27 LifetimeElisionKind, LifetimeLoweringMode, Span, TyLoweringContext,
28 db::HirDatabase,
29 generics::Generics,
30 lower::LoweringMode,
31 next_solver::{
32 DbInterner, GenericArgs, ParamEnv, StoredClauses, Ty, TyKind,
33 infer::{
34 DbInternerInferExt, InferCtxt,
35 select::EvaluationResult,
36 traits::{Obligation, ObligationCause},
37 },
38 obligation_ctxt::ObligationCtxt,
39 },
40};
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
44pub struct ParamEnvAndCrate<'db> {
45 pub param_env: ParamEnv<'db>,
46 pub krate: Crate,
47}
48
49impl<'db> ParamEnvAndCrate<'db> {
50 #[inline]
51 pub fn store(self) -> StoredParamEnvAndCrate {
52 StoredParamEnvAndCrate { param_env: self.param_env.clauses.store(), krate: self.krate }
53 }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq, Hash)]
57pub struct StoredParamEnvAndCrate {
58 param_env: StoredClauses,
59 pub krate: Crate,
60}
61
62impl StoredParamEnvAndCrate {
63 #[inline]
64 pub fn param_env<'db>(&self, _db: &'db dyn HirDatabase) -> ParamEnv<'db> {
65 ParamEnv { clauses: self.param_env.as_ref() }
66 }
67
68 #[inline]
69 pub fn as_ref<'db>(&self, db: &'db dyn HirDatabase) -> ParamEnvAndCrate<'db> {
70 ParamEnvAndCrate { param_env: self.param_env(db), krate: self.krate }
71 }
72}
73
74pub fn structurally_normalize_ty<'db>(
76 infcx: &InferCtxt<'db>,
77 ty: Ty<'db>,
78 env: ParamEnv<'db>,
79) -> Ty<'db> {
80 let TyKind::Alias(..) = ty.kind() else { return ty };
81 let mut ocx = ObligationCtxt::new(infcx);
82 let ty = ocx.structurally_normalize_ty(&ObligationCause::dummy(), env, ty).unwrap_or(ty);
83 ty.replace_infer_with_error(infcx.interner)
84}
85
86#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Update)]
87pub enum FnTrait {
88 FnOnce,
91 FnMut,
92 Fn,
93
94 AsyncFnOnce,
95 AsyncFnMut,
96 AsyncFn,
97}
98
99impl FnTrait {
100 pub fn method_name(self) -> Name {
101 match self {
102 FnTrait::FnOnce => Name::new_symbol_root(sym::call_once),
103 FnTrait::FnMut => Name::new_symbol_root(sym::call_mut),
104 FnTrait::Fn => Name::new_symbol_root(sym::call),
105 FnTrait::AsyncFnOnce => Name::new_symbol_root(sym::async_call_once),
106 FnTrait::AsyncFnMut => Name::new_symbol_root(sym::async_call_mut),
107 FnTrait::AsyncFn => Name::new_symbol_root(sym::async_call),
108 }
109 }
110
111 pub fn get_id(self, lang_items: &LangItems) -> Option<TraitId> {
112 match self {
113 FnTrait::FnOnce => lang_items.FnOnce,
114 FnTrait::FnMut => lang_items.FnMut,
115 FnTrait::Fn => lang_items.Fn,
116 FnTrait::AsyncFnOnce => lang_items.AsyncFnOnce,
117 FnTrait::AsyncFnMut => lang_items.AsyncFnMut,
118 FnTrait::AsyncFn => lang_items.AsyncFn,
119 }
120 }
121}
122
123pub fn implements_trait_unique<'db>(
125 ty: Ty<'db>,
126 db: &'db dyn HirDatabase,
127 env: ParamEnvAndCrate<'db>,
128 trait_: TraitId,
129) -> bool {
130 implements_trait_unique_with_infcx(db, env, trait_, &mut |infcx| {
131 infcx.fill_rest_fresh_args(Span::Dummy, trait_.into(), [ty.into()])
132 })
133}
134
135pub fn implements_trait_unique_with_args<'db>(
137 db: &'db dyn HirDatabase,
138 env: ParamEnvAndCrate<'db>,
139 trait_: TraitId,
140 args: GenericArgs<'db>,
141) -> bool {
142 implements_trait_unique_with_infcx(db, env, trait_, &mut |_| args)
143}
144
145pub fn implements_trait_unique_with_infcx<'db>(
146 db: &'db dyn HirDatabase,
147 env: ParamEnvAndCrate<'db>,
148 trait_: TraitId,
149 create_args: &mut dyn FnMut(&InferCtxt<'db>) -> GenericArgs<'db>,
150) -> bool {
151 let interner = DbInterner::new_with(db, env.krate);
152 let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
154
155 let args = create_args(&infcx);
156 let trait_ref = rustc_type_ir::TraitRef::new_from_args(interner, trait_.into(), args);
157 if trait_ref.self_ty().is_ty_error() {
158 return false;
159 }
160
161 let obligation = Obligation::new(interner, ObligationCause::dummy(), env.param_env, trait_ref);
162 infcx.predicate_must_hold_modulo_regions(&obligation)
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
166pub enum WherePredicateEvaluation {
167 Holds,
168 NotProven,
169 HasErrors,
170 NoObligations,
171}
172
173pub fn where_predicate_must_hold<'db>(
176 db: &'db dyn HirDatabase,
177 resolver: &Resolver<'db>,
178 store: &'db ExpressionStore,
179 def: ExpressionStoreOwnerId,
180 generic_def: GenericDefId,
181 env: ParamEnvAndCrate<'db>,
182 predicate: &WherePredicate,
183) -> WherePredicateEvaluation {
184 let interner = DbInterner::new_with(db, env.krate);
185 let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
186 let generics = OnceCell::<Generics<'db>>::new();
187 let mut ctx = TyLoweringContext::new(
188 db,
189 resolver,
190 store,
191 def,
192 generic_def,
193 &generics,
194 LifetimeElisionKind::Infer,
195 LifetimeLoweringMode::Bound,
196 )
197 .with_interning_mode(LoweringMode::Ide);
198 let clauses =
199 ctx.lower_where_predicate(predicate, false).map(|(clause, _)| clause).collect::<Vec<_>>();
200
201 if !ctx.diagnostics.is_empty()
202 || clauses.iter().any(|clause| clause.as_predicate().references_error())
203 {
204 return WherePredicateEvaluation::HasErrors;
205 }
206
207 if clauses.is_empty() {
208 return if ctx.unsized_types.is_empty() {
209 WherePredicateEvaluation::HasErrors
210 } else {
211 WherePredicateEvaluation::NoObligations
212 };
213 }
214
215 let result = infcx.probe(|snapshot| {
216 let mut ocx = ObligationCtxt::new(&infcx);
217 for clause in clauses {
218 let obligation = Obligation::new(
219 interner,
220 ObligationCause::dummy(),
221 env.param_env,
222 clause.as_predicate(),
223 );
224 ocx.register_obligation(obligation);
225 }
226
227 let mut result = EvaluationResult::EvaluatedToOk;
228 for error in ocx.evaluate_obligations_error_on_ambiguity() {
229 if error.is_true_error() {
230 return EvaluationResult::EvaluatedToErr;
231 }
232 result = result.max(EvaluationResult::EvaluatedToAmbig);
233 }
234 if infcx.opaque_types_added_in_snapshot(snapshot) {
235 result.max(EvaluationResult::EvaluatedToOkModuloOpaqueTypes)
236 } else if infcx.region_constraints_added_in_snapshot(snapshot) {
237 result.max(EvaluationResult::EvaluatedToOkModuloRegions)
238 } else {
239 result
240 }
241 });
242
243 if result.must_apply_modulo_regions() {
244 WherePredicateEvaluation::Holds
245 } else {
246 WherePredicateEvaluation::NotProven
247 }
248}
249
250pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id: ImplId) -> bool {
251 let self_ty = db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip();
252 let self_ty = self_ty.kind();
253 let impl_allowed = match self_ty {
254 TyKind::Tuple(_)
255 | TyKind::FnDef(_, _)
256 | TyKind::Array(_, _)
257 | TyKind::Never
258 | TyKind::RawPtr(_, _)
259 | TyKind::Ref(_, _, _)
260 | TyKind::Slice(_)
261 | TyKind::Str
262 | TyKind::Bool
263 | TyKind::Char
264 | TyKind::Int(_)
265 | TyKind::Uint(_)
266 | TyKind::Float(_) => def_map.is_rustc_coherence_is_core(),
267
268 TyKind::Adt(adt_def, _) => adt_def.def_id().module(db).krate(db) == def_map.krate(),
269 TyKind::Dynamic(it, _) => it
270 .principal_def_id()
271 .is_some_and(|trait_id| trait_id.0.module(db).krate(db) == def_map.krate()),
272
273 _ => true,
274 };
275 impl_allowed || {
276 let rustc_has_incoherent_inherent_impls = match self_ty {
277 TyKind::Tuple(_)
278 | TyKind::FnDef(_, _)
279 | TyKind::Array(_, _)
280 | TyKind::Never
281 | TyKind::RawPtr(_, _)
282 | TyKind::Ref(_, _, _)
283 | TyKind::Slice(_)
284 | TyKind::Str
285 | TyKind::Bool
286 | TyKind::Char
287 | TyKind::Int(_)
288 | TyKind::Uint(_)
289 | TyKind::Float(_) => true,
290
291 TyKind::Adt(adt_def, _) => match adt_def.def_id() {
292 hir_def::AdtId::StructId(id) => StructSignature::of(db, id)
293 .flags
294 .contains(StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
295 hir_def::AdtId::UnionId(id) => UnionSignature::of(db, id)
296 .flags
297 .contains(StructFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
298 hir_def::AdtId::EnumId(it) => EnumSignature::of(db, it)
299 .flags
300 .contains(EnumFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS),
301 },
302 TyKind::Dynamic(it, _) => it.principal_def_id().is_some_and(|trait_id| {
303 TraitSignature::of(db, trait_id.0)
304 .flags
305 .contains(TraitFlags::RUSTC_HAS_INCOHERENT_INHERENT_IMPLS)
306 }),
307
308 _ => false,
309 };
310 let items = impl_id.impl_items(db);
311 rustc_has_incoherent_inherent_impls
312 && !items.items.is_empty()
313 && items.items.iter().all(|&(_, assoc)| match assoc {
314 AssocItemId::FunctionId(it) => FunctionSignature::of(db, it)
315 .flags
316 .contains(FnFlags::RUSTC_ALLOW_INCOHERENT_IMPL),
317 AssocItemId::ConstId(it) => ConstSignature::of(db, it)
318 .flags
319 .contains(ConstFlags::RUSTC_ALLOW_INCOHERENT_IMPL),
320 AssocItemId::TypeAliasId(it) => TypeAliasSignature::of(db, it)
321 .flags
322 .contains(TypeAliasFlags::RUSTC_ALLOW_INCOHERENT_IMPL),
323 })
324 }
325}
326
327pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool {
335 let Some(impl_trait) = db.impl_trait(impl_) else {
336 return true;
338 };
339
340 let local_crate = impl_.lookup(db).container.krate(db);
341 let is_local = |tgt_crate| tgt_crate == local_crate;
342
343 let trait_ref = impl_trait.instantiate_identity().skip_norm_wip();
344 let trait_id = trait_ref.def_id.0;
345 if is_local(trait_id.module(db).krate(db)) {
346 return true;
348 }
349
350 let unwrap_fundamental = |mut ty: Ty<'db>| {
351 loop {
353 match ty.kind() {
354 TyKind::Ref(_, referenced, _) => ty = referenced,
355 TyKind::Adt(adt_def, subs) => {
356 let AdtId::StructId(s) = adt_def.def_id() else {
357 break ty;
358 };
359 let struct_signature = StructSignature::of(db, s);
360 if struct_signature.flags.contains(StructFlags::FUNDAMENTAL) {
361 let next = subs.types().next();
362 match next {
363 Some(it) => ty = it,
364 None => break ty,
365 }
366 } else {
367 break ty;
368 }
369 }
370 _ => break ty,
371 }
372 }
373 };
374 let is_not_orphan = trait_ref.args.types().any(|ty| match unwrap_fundamental(ty).kind() {
379 TyKind::Adt(adt_def, _) => is_local(adt_def.def_id().module(db).krate(db)),
380 TyKind::Error(_) => true,
381 TyKind::Dynamic(it, _) => {
382 it.principal_def_id().is_some_and(|trait_id| is_local(trait_id.0.module(db).krate(db)))
383 }
384 _ => false,
385 });
386 #[allow(clippy::let_and_return)]
387 is_not_orphan
388}