Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #22141 from ChayimFriedman2/more-port
fix: Port and refactor more stuff to match rustc
| -rw-r--r-- | crates/hir-def/src/lang_item.rs | 35 | ||||
| -rw-r--r-- | crates/hir-def/src/unstable_features.rs | 1 | ||||
| -rw-r--r-- | crates/hir-ty/src/dyn_compatibility.rs | 14 | ||||
| -rw-r--r-- | crates/hir-ty/src/generics.rs | 5 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 62 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/cast.rs | 7 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/closure.rs | 14 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/coerce.rs | 6 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/expr.rs | 419 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/opaques.rs | 2 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/path.rs | 44 | ||||
| -rw-r--r-- | crates/hir-ty/src/infer/unify.rs | 182 | ||||
| -rw-r--r-- | crates/hir-ty/src/lib.rs | 115 | ||||
| -rw-r--r-- | crates/hir-ty/src/next_solver/fulfill.rs | 19 | ||||
| -rw-r--r-- | crates/hir-ty/src/next_solver/infer/mod.rs | 18 | ||||
| -rw-r--r-- | crates/hir-ty/src/next_solver/infer/snapshot/mod.rs | 6 | ||||
| -rw-r--r-- | crates/hir-ty/src/next_solver/infer/traits.rs | 8 | ||||
| -rw-r--r-- | crates/hir-ty/src/next_solver/ty.rs | 4 | ||||
| -rw-r--r-- | crates/hir-ty/src/traits.rs | 97 | ||||
| -rw-r--r-- | crates/hir/src/lib.rs | 18 | ||||
| -rw-r--r-- | crates/intern/src/symbol/symbols.rs | 1 |
21 files changed, 477 insertions, 600 deletions
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs index 6071ed2981..adc445c2a8 100644 --- a/crates/hir-def/src/lang_item.rs +++ b/crates/hir-def/src/lang_item.rs @@ -7,8 +7,8 @@ use intern::{Symbol, sym}; use stdx::impl_from; use crate::{ - AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, MacroId, - ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, + AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, + ItemContainerId, MacroId, ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, attrs::AttrFlags, db::DefDatabase, nameres::{DefMap, assoc::TraitItems, crate_def_map, crate_local_def_map}, @@ -102,6 +102,8 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt lang_items.fill_non_lang_core_items(db, crate_def_map); } + lang_items.resolve_manually(db); + if lang_items.is_empty() { None } else { Some(Box::new(lang_items)) } } @@ -190,6 +192,23 @@ fn resolve_core_macro( current.scope.makro(&Name::new_symbol_root(name)) } +impl LangItems { + fn resolve_manually(&mut self, db: &dyn DefDatabase) { + (|| { + let into_future_into_future = self.IntoFutureIntoFuture?; + let ItemContainerId::TraitId(into_future) = into_future_into_future.loc(db).container + else { + return None; + }; + self.IntoFuture = Some(into_future); + self.IntoFutureOutput = into_future + .trait_items(db) + .associated_type_by_name(&Name::new_symbol_root(sym::Output)); + Some(()) + })(); + } +} + #[salsa::tracked(returns(as_deref))] pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option<Box<[TraitId]>> { let mut traits = Vec::new(); @@ -221,6 +240,10 @@ macro_rules! language_item_table { @non_lang_core_macros: $( core::$($non_lang_macro_module:ident)::*, $non_lang_macro:ident, $non_lang_macro_field:ident; )* + + @resolve_manually: + + $( $resolve_manually:ident, $resolve_manually_type:ident; )* ) => { #[allow(non_snake_case)] // FIXME: Should we remove this? #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] @@ -235,6 +258,9 @@ macro_rules! language_item_table { $( pub $non_lang_macro_field: Option<MacroId>, )* + $( + pub $resolve_manually: Option<$resolve_manually_type>, + )* } impl LangItems { @@ -247,6 +273,7 @@ macro_rules! language_item_table { $( self.$lang_item = self.$lang_item.or(other.$lang_item); )* $( self.$non_lang_trait = self.$non_lang_trait.or(other.$non_lang_trait); )* $( self.$non_lang_macro_field = self.$non_lang_macro_field.or(other.$non_lang_macro_field); )* + $( self.$resolve_manually = self.$resolve_manually.or(other.$resolve_manually); )* } fn assign_lang_item(&mut self, name: Symbol, target: LangItemTarget) { @@ -538,4 +565,8 @@ language_item_table! { LangItems => core::marker, CoercePointee, CoercePointeeDerive; core::marker, Copy, CopyDerive; core::clone, Clone, CloneDerive; + + @resolve_manually: + IntoFuture, TraitId; + IntoFutureOutput, TypeAliasId; } diff --git a/crates/hir-def/src/unstable_features.rs b/crates/hir-def/src/unstable_features.rs index 559726fe9b..f581f02617 100644 --- a/crates/hir-def/src/unstable_features.rs +++ b/crates/hir-def/src/unstable_features.rs @@ -92,4 +92,5 @@ define_unstable_features! { ref_pat_eat_one_layer_2024_structural, deref_patterns, mut_ref, + type_changing_struct_update, } diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs index ee7527446a..0cf7f650f0 100644 --- a/crates/hir-ty/src/dyn_compatibility.rs +++ b/crates/hir-ty/src/dyn_compatibility.rs @@ -21,11 +21,14 @@ use crate::{ db::{HirDatabase, InternedOpaqueTyId}, lower::{GenericPredicates, associated_ty_item_bounds}, next_solver::{ - AliasTy, Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, Goal, ParamEnv, - ParamTy, SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, infer::DbInternerInferExt, + AliasTy, Binder, Clause, Clauses, DbInterner, EarlyBinder, GenericArgs, ParamEnv, ParamTy, + SolverDefId, TraitPredicate, TraitRef, Ty, TypingMode, + infer::{ + DbInternerInferExt, + traits::{Obligation, ObligationCause}, + }, mk_param, }, - traits::next_trait_solve_in_ctxt, }; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -474,12 +477,11 @@ fn receiver_is_dispatchable<'db>( // Receiver: DispatchFromDyn<Receiver[Self => U]> let predicate = TraitRef::new(interner, dispatch_from_dyn_did.into(), [receiver_ty, unsized_receiver_ty]); - let goal = Goal::new(interner, param_env, predicate); + let obligation = Obligation::new(interner, ObligationCause::dummy(), param_env, predicate); let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); // the receiver is dispatchable iff the obligation holds - let res = next_trait_solve_in_ctxt(&infcx, goal); - res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes)) + infcx.predicate_must_hold_modulo_regions(&obligation) } fn receiver_for_self_ty<'db>( diff --git a/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs index 822942eec3..b041281844 100644 --- a/crates/hir-ty/src/generics.rs +++ b/crates/hir-ty/src/generics.rs @@ -131,11 +131,6 @@ impl<'db> Generics<'db> { self.parent_generics().map_or(0, Generics::len) } - /// Returns numbers of generic parameters excluding those from parent. - pub(crate) fn len_self(&self) -> usize { - self.params.len() - } - pub(crate) fn len_lifetimes_self(&self) -> usize { self.params.len_lifetimes() } diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 54f334b66d..fd0612e066 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -35,8 +35,8 @@ use base_db::{Crate, FxIndexMap}; use either::Either; use hir_def::{ AdtId, AssocItemId, AttrDefId, ConstId, ConstParamId, DefWithBodyId, ExpressionStoreOwnerId, - FieldId, FunctionId, GenericDefId, GenericParamId, HasModule, ItemContainerId, LocalFieldId, - Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, TypeOrConstParamId, VariantId, + FieldId, FunctionId, GenericDefId, GenericParamId, HasModule, LocalFieldId, Lookup, TraitId, + TupleFieldId, TupleId, TypeOrConstParamId, VariantId, attrs::AttrFlags, expr_store::{Body, ExpressionStore, HygieneId, RootExprOrigin, path::Path}, hir::{BindingId, ExprId, ExprOrPatId, LabelId, PatId}, @@ -49,7 +49,6 @@ use hir_def::{ }; use hir_expand::{mod_path::ModPath, name::Name}; use indexmap::IndexSet; -use intern::sym; use la_arena::ArenaMap; use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; @@ -83,8 +82,8 @@ use crate::{ }, method_resolution::CandidateId, next_solver::{ - AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region, - StoredGenericArgs, StoredTy, StoredTys, Ty, TyKind, Tys, + AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArgs, Region, StoredGenericArgs, + StoredTy, StoredTys, Ty, TyKind, Tys, abi::Safety, infer::{InferCtxt, ObligationInspector, traits::ObligationCause}, }, @@ -1240,7 +1239,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { db.trait_environment(ExpressionStoreOwnerId::VariantFields(variant_id)) } }; - let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), Some(owner)); + let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), owner); let types = crate::next_solver::default_types(db); InferenceContext { result: InferenceResult::new(types.types.error), @@ -1791,10 +1790,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.table.insert_type_vars(ty) } - fn unify(&mut self, ty1: Ty<'db>, ty2: Ty<'db>) -> bool { - self.table.unify(ty1, ty2) - } - /// Attempts to returns the deeply last field of nested structures, but /// does not apply any normalization in its search. Returns the same type /// if input `ty` is not a structure at all. @@ -1871,12 +1866,8 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.table.shallow_resolve(ty) } - fn resolve_associated_type( - &mut self, - inner_ty: Ty<'db>, - assoc_ty: Option<TypeAliasId>, - ) -> Ty<'db> { - self.resolve_associated_type_with_params(inner_ty, assoc_ty, &[]) + pub(crate) fn resolve_vars_if_possible<T: TypeFoldable<DbInterner<'db>>>(&self, t: T) -> T { + self.table.resolve_vars_if_possible(t) } fn demand_eqtype( @@ -1974,30 +1965,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { ty.unwrap_or_else(|| self.expr_ty(e)) } - fn resolve_associated_type_with_params( - &mut self, - inner_ty: Ty<'db>, - assoc_ty: Option<TypeAliasId>, - // FIXME(GATs): these are args for the trait ref, args for assoc type itself should be - // handled when we support them. - params: &[GenericArg<'db>], - ) -> Ty<'db> { - match assoc_ty { - Some(res_assoc_ty) => { - let alias = Ty::new_alias( - self.interner(), - AliasTy::new( - self.interner(), - AliasTyKind::Projection { def_id: res_assoc_ty.into() }, - iter::once(inner_ty.into()).chain(params.iter().copied()), - ), - ); - self.table.try_structurally_resolve_type(alias) - } - None => self.err_ty(), - } - } - fn resolve_variant( &mut self, node: ExprOrPatId, @@ -2323,19 +2290,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { } } - fn resolve_output_on(&self, trait_: TraitId) -> Option<TypeAliasId> { - trait_.trait_items(self.db).associated_type_by_name(&Name::new_symbol_root(sym::Output)) - } - - fn resolve_future_future_output(&self) -> Option<TypeAliasId> { - let ItemContainerId::TraitId(trait_) = - self.lang_items.IntoFutureIntoFuture?.lookup(self.db).container - else { - return None; - }; - self.resolve_output_on(trait_) - } - fn resolve_boxed_box(&self) -> Option<AdtId> { let struct_ = self.lang_items.OwnedBox?; Some(struct_.into()) @@ -2489,7 +2443,7 @@ impl<'db> Expectation<'db> { fn only_has_type(&self, table: &mut unify::InferenceTable<'db>) -> Option<Ty<'db>> { match self { - Expectation::HasType(t) => Some(table.shallow_resolve(*t)), + Expectation::HasType(t) => Some(table.resolve_vars_if_possible(*t)), Expectation::Castable(_) | Expectation::RValueLikeUnsized(_) | Expectation::None => { None } diff --git a/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs index 09855766d8..d23a32d81b 100644 --- a/crates/hir-ty/src/infer/cast.rs +++ b/crates/hir-ty/src/infer/cast.rs @@ -147,7 +147,8 @@ impl<'db> CastCheck<'db> { return Ok(()); } - if !self.cast_ty.has_infer_types() && !ctx.table.is_sized(self.cast_ty) { + if !self.cast_ty.has_infer_types() && !ctx.table.type_is_sized_modulo_regions(self.cast_ty) + { return Err(InferenceDiagnostic::CastToUnsized { expr: self.expr, cast_ty: self.cast_ty.store(), @@ -199,7 +200,7 @@ impl<'db> CastCheck<'db> { // array-ptr-cast CastTy::Ptr(t, m) => { let t = ctx.table.try_structurally_resolve_type(t); - if !ctx.table.is_sized(t) { + if !ctx.table.type_is_sized_modulo_regions(t) { return Err(CastError::IllegalCast); } self.check_ref_cast(ctx, inner_ty, mutbl, t, m) @@ -520,7 +521,7 @@ fn pointer_kind<'db>( ) -> Result<Option<PointerKind<'db>>, ()> { let ty = ctx.table.try_structurally_resolve_type(ty); - if ctx.table.is_sized(ty) { + if ctx.table.type_is_sized_modulo_regions(ty) { return Ok(Some(PointerKind::Thin)); } diff --git a/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 0d2de9b984..a53ab7daaa 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/crates/hir-ty/src/infer/closure.rs @@ -440,8 +440,7 @@ impl<'db> InferenceContext<'_, 'db> { .eq(inferred_fnptr_sig, generalized_fnptr_sig) .map(|infer_ok| self.table.register_infer_ok(infer_ok)); - let resolved_sig = - self.table.infer_ctxt.resolve_vars_if_possible(generalized_fnptr_sig); + let resolved_sig = self.resolve_vars_if_possible(generalized_fnptr_sig); if resolved_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { expected_sig = Some(resolved_sig.fn_sig(self.interner())); @@ -531,7 +530,7 @@ impl<'db> InferenceContext<'_, 'db> { &self, projection: PolyProjectionPredicate<'db>, ) -> Option<PolyFnSig<'db>> { - let projection = self.table.infer_ctxt.resolve_vars_if_possible(projection); + let projection = self.resolve_vars_if_possible(projection); let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); debug!(?arg_param_ty); @@ -576,7 +575,7 @@ impl<'db> InferenceContext<'_, 'db> { &mut self, projection: PolyProjectionPredicate<'db>, ) -> Option<PolyFnSig<'db>> { - let projection = self.table.infer_ctxt.resolve_vars_if_possible(projection); + let projection = self.resolve_vars_if_possible(projection); let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); debug!(?arg_param_ty); @@ -822,11 +821,8 @@ impl<'db> InferenceContext<'_, 'db> { .eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?; all_obligations.extend(obligations); - let inputs = supplied_sig - .inputs() - .iter() - .copied() - .map(|ty| table.infer_ctxt.resolve_vars_if_possible(ty)); + let inputs = + supplied_sig.inputs().iter().copied().map(|ty| table.resolve_vars_if_possible(ty)); expected_sigs.liberated_sig = table.interner().mk_fn_sig( inputs, diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index db912c0eb6..962fc752a5 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -152,10 +152,8 @@ where let snapshot = self.infcx().start_snapshot(); let result = f(self); match result { - Ok(_) => {} - Err(_) => { - self.infcx().rollback_to(snapshot); - } + Ok(_) => self.infcx().commit_from(snapshot), + Err(_) => self.infcx().rollback_to(snapshot), } result } diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index e84a03a2e7..73d81ad16e 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -4,11 +4,12 @@ use std::{iter::repeat_with, mem}; use either::Either; use hir_def::{ - FieldId, GenericDefId, ItemContainerId, Lookup, TupleFieldId, TupleId, + AdtId, FieldId, TupleFieldId, TupleId, VariantId, expr_store::path::{GenericArgs as HirGenericArgs, Path}, hir::{ Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId, - InlineAsmKind, LabelId, Literal, Pat, PatId, RecordSpread, Statement, UnaryOp, + InlineAsmKind, LabelId, Literal, Pat, PatId, RecordLitField, RecordSpread, Statement, + UnaryOp, }, resolver::ValueNs, signatures::VariantFields, @@ -16,27 +17,27 @@ use hir_def::{ use hir_def::{FunctionId, hir::ClosureKind}; use hir_expand::name::Name; use rustc_ast_ir::Mutability; +use rustc_hash::FxHashMap; use rustc_type_ir::{ InferTy, Interner, inherent::{AdtDef, GenericArgs as _, IntoKind, Ty as _}, }; +use stdx::never; use syntax::ast::RangeOp; use tracing::debug; use crate::{ Adjust, Adjustment, CallableDefId, Rawness, consteval, - generics::generics, infer::{AllowTwoPhase, BreakableKind, coerce::CoerceMany, find_continuable, pat::PatOrigin}, - lower::{GenericPredicates, lower_mutability}, + lower::lower_mutability, method_resolution::{self, CandidateId, MethodCallee, MethodError}, next_solver::{ - ClauseKind, FnSig, GenericArg, GenericArgs, TraitRef, Ty, TyKind, TypeError, + ClauseKind, FnSig, GenericArg, GenericArgs, Ty, TyKind, TypeError, infer::{ BoundRegionConversionTime, InferOk, traits::{Obligation, ObligationCause}, }, obligation_ctxt::ObligationCtxt, - util::clauses_as_obligations, }, }; @@ -60,17 +61,41 @@ impl<'db> InferenceContext<'_, 'db> { ) -> Ty<'db> { let ty = self.infer_expr_inner(tgt_expr, expected, is_read); if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - let could_unify = self.unify(ty, expected_ty); - if !could_unify { - self.result.type_mismatches.get_or_insert_default().insert( - tgt_expr.into(), - TypeMismatch { expected: expected_ty.store(), actual: ty.store() }, - ); - } + _ = self.demand_eqtype(tgt_expr.into(), expected_ty, ty); } ty } + pub(crate) fn infer_expr_suptype_coerce_never( + &mut self, + expr: ExprId, + expected: &Expectation<'db>, + is_read: ExprIsRead, + ) -> Ty<'db> { + let ty = self.infer_expr_inner(expr, expected, is_read); + if ty.is_never() { + if let Some(adjustments) = self.result.expr_adjustments.get(&expr) { + return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &**adjustments { + target.as_ref() + } else { + self.err_ty() + }; + } + + if let Some(target) = expected.only_has_type(&mut self.table) { + self.coerce(expr, ty, target, AllowTwoPhase::No, ExprIsRead::Yes) + .expect("never-to-any coercion should always succeed") + } else { + ty + } + } else { + if let Some(expected_ty) = expected.only_has_type(&mut self.table) { + _ = self.demand_suptype(expr.into(), expected_ty, ty); + } + ty + } + } + pub(crate) fn infer_expr_no_expect( &mut self, tgt_expr: ExprId, @@ -283,13 +308,7 @@ impl<'db> InferenceContext<'_, 'db> { } } else { if let Some(expected_ty) = expected.only_has_type(&mut self.table) { - let could_unify = self.unify(ty, expected_ty); - if !could_unify { - self.result.type_mismatches.get_or_insert_default().insert( - expr.into(), - TypeMismatch { expected: expected_ty.store(), actual: ty.store() }, - ); - } + _ = self.demand_eqtype(expr.into(), ty, expected_ty); } ty } @@ -579,80 +598,10 @@ impl<'db> InferenceContext<'_, 'db> { self.types.types.never } Expr::RecordLit { path, fields, spread, .. } => { - let (ty, def_id) = self.resolve_variant(tgt_expr.into(), path, false); - - if let Some(t) = expected.only_has_type(&mut self.table) { - self.unify(ty, t); - } - - let substs = ty.as_adt().map(|(_, s)| s).unwrap_or(self.types.empty.generic_args); - if let Some(variant) = def_id { - self.write_variant_resolution(tgt_expr.into(), variant); - } - match def_id { - _ if fields.is_empty() => {} - Some(def) => { - let field_types = self.db.field_types(def); - let variant_data = def.fields(self.db); - let visibilities = VariantFields::field_visibilities(self.db, def); - for field in fields.iter() { - let field_def = { - match variant_data.field(&field.name) { - Some(local_id) => { - if !visibilities[local_id] - .is_visible_from(self.db, self.resolver.module()) - { - self.push_diagnostic( - InferenceDiagnostic::NoSuchField { - field: field.expr.into(), - private: Some(local_id), - variant: def, - }, - ); - } - Some(local_id) - } - None => { - self.push_diagnostic(InferenceDiagnostic::NoSuchField { - field: field.expr.into(), - private: None, - variant: def, - }); - None - } - } - }; - let field_ty = field_def.map_or(self.err_ty(), |it| { - field_types[it].get().instantiate(self.interner(), &substs) - }); - - // Field type might have some unknown types - // FIXME: we may want to emit a single type variable for all instance of type fields? - let field_ty = self.insert_type_vars(field_ty); - self.infer_expr_coerce( - field.expr, - &Expectation::has_type(field_ty), - ExprIsRead::Yes, - ); - } - } - None => { - for field in fields.iter() { - // Field projections don't constitute reads. - self.infer_expr_coerce(field.expr, &Expectation::None, ExprIsRead::No); - } - } - } - if let RecordSpread::Expr(expr) = *spread { - self.infer_expr_coerce_never(expr, &Expectation::has_type(ty), ExprIsRead::Yes); - } - ty + self.infer_record_expr(tgt_expr, expected, path, fields, *spread) } Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected), - Expr::Await { expr } => { - let inner_ty = self.infer_expr_inner(*expr, &Expectation::none(), ExprIsRead::Yes); - self.resolve_associated_type(inner_ty, self.resolve_future_future_output()) - } + Expr::Await { expr } => self.infer_await_expr(*expr), Expr::Cast { expr, type_ref } => { let cast_ty = self.make_body_ty(*type_ref); let expr_ty = @@ -1019,6 +968,243 @@ impl<'db> InferenceContext<'_, 'db> { ty } + fn infer_await_expr(&mut self, awaitee: ExprId) -> Ty<'db> { + let awaitee_ty = self.infer_expr_no_expect(awaitee, ExprIsRead::Yes); + let (Some(into_future), Some(into_future_output)) = + (self.lang_items.IntoFuture, self.lang_items.IntoFutureOutput) + else { + return self.types.types.error; + }; + self.table.register_bound(awaitee_ty, into_future, ObligationCause::new()); + // Do not eagerly normalize. + Ty::new_projection(self.interner(), into_future_output.into(), [awaitee_ty]) + } + + fn infer_record_expr( + &mut self, + expr: ExprId, + expected: &Expectation<'db>, + path: &Path, + fields: &[RecordLitField], + base_expr: RecordSpread, + ) -> Ty<'db> { + // Find the relevant variant + let (adt_ty, Some(variant)) = self.resolve_variant(expr.into(), path, false) else { + // FIXME: Emit an error. + for field in fields { + self.infer_expr_no_expect(field.expr, ExprIsRead::Yes); + } + + return self.types.types.error; + }; + self.write_variant_resolution(expr.into(), variant); + + // Prohibit struct expressions when non-exhaustive flag is set. + if self.has_applicable_non_exhaustive(variant.into()) { + // FIXME: Emit an error. + } + + self.check_record_expr_fields(adt_ty, expected, expr, variant, fields, base_expr); + + self.require_type_is_sized(adt_ty); + adt_ty + } + + fn check_record_expr_fields( + &mut self, + adt_ty: Ty<'db>, + expected: &Expectation<'db>, + expr: ExprId, + variant: VariantId, + hir_fields: &[RecordLitField], + base_expr: RecordSpread, + ) { + let interner = self.interner(); + + let adt_ty = self.table.try_structurally_resolve_type(adt_ty); + let adt_ty_hint = expected.only_has_type(&mut self.table).and_then(|expected| { + self.infcx() + .fudge_inference_if_ok(|| { + let mut ocx = ObligationCtxt::new(self.infcx()); + ocx.sup(&ObligationCause::new(), self.table.param_env, expected, adt_ty)?; + if !ocx.try_evaluate_obligations().is_empty() { + return Err(TypeError::Mismatch); + } + Ok(self.resolve_vars_if_possible(adt_ty)) + }) + .ok() + }); + if let Some(adt_ty_hint) = adt_ty_hint { + // re-link the variables that the fudging above can create. + _ = self.demand_eqtype(expr.into(), adt_ty_hint, adt_ty); + } + + let TyKind::Adt(adt, args) = adt_ty.kind() else { + never!("non-ADT passed to check_struct_expr_fields"); + return; + }; + let adt_id = adt.def_id().0; + + let variant_fields = variant.fields(self.db); + let variant_field_tys = self.db.field_types(variant); + let variant_field_vis = VariantFields::field_visibilities(self.db, variant); + let mut remaining_fields = variant_fields + .fields() + .iter() + .map(|(i, field)| (field.name.clone(), i)) + .collect::<FxHashMap<_, _>>(); + + let mut seen_fields = FxHashMap::default(); + + // Type-check each field. + for field in hir_fields { + let name = &field.name; + let field_type = if let Some(i) = remaining_fields.remove(name) { + seen_fields.insert(name, i); + + if !self.resolver.is_visible(self.db, variant_field_vis[i]) { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: field.expr.into(), + private: Some(i), + variant, + }); + } + + variant_field_tys[i].get().instantiate(interner, args) + } else { + if let Some(field_idx) = seen_fields.get(&name) { + // FIXME: Emit an error: duplicate field. + variant_field_tys[*field_idx].get().instantiate(interner, args) + } else { + self.push_diagnostic(InferenceDiagnostic::NoSuchField { + field: field.expr.into(), + private: None, + variant, + }); + self.types.types.error + } + }; + + // Check that the expected field type is WF. Otherwise, we emit no use-site error + // in the case of coercions for non-WF fields, which leads to incorrect error + // tainting. See issue #126272. + self.table.register_wf_obligation(field_type.into(), ObligationCause::new()); + + // Make sure to give a type to the field even if there's + // an error, so we can continue type-checking. + self.infer_expr_coerce(field.expr, &Expectation::has_type(field_type), ExprIsRead::Yes); + } + + // Make sure the programmer specified correct number of fields. + if matches!(adt_id, AdtId::UnionId(_)) && hir_fields.len() != 1 { + // FIXME: Emit an error: unions must specify exactly one field. + } + + match base_expr { + RecordSpread::FieldDefaults => { + let mut missing_mandatory_fields = Vec::new(); + let mut missing_optional_fields = Vec::new(); + for (field_idx, field) in variant_fields.fields().iter() { + if remaining_fields.remove(&field.name).is_some() { + if field.default_value.is_none() { + missing_mandatory_fields.push(field_idx); + } else { + missing_optional_fields.push(field_idx); + } + } + } + if !missing_mandatory_fields.is_empty() { + // FIXME: Emit an error: missing fields. + } + } + RecordSpread::Expr(base_expr) => { + // FIXME: We are currently creating two branches here in order to maintain + // consistency. But they should be merged as much as possible. + if self.features.type_changing_struct_update { + if matches!(adt_id, AdtId::StructId(_)) { + // Make some fresh generic parameters for our ADT type. + let fresh_args = self.table.fresh_args_for_item(adt_id.into()); + // We do subtyping on the FRU fields first, so we can + // learn exactly what types we expect the base expr + // needs constrained to be compatible with the struct + // type we expect from the expectation value. + for (field_idx, field) in variant_fields.fields().iter() { + let fru_ty = variant_field_tys[field_idx] + .get() + .instantiate(interner, fresh_args); + if remaining_fields.remove(&field.name).is_some() { + let target_ty = + variant_field_tys[field_idx].get().instantiate(interner, args); + let cause = ObligationCause::new(); + match self.table.at(&cause).sup(target_ty, fru_ty) { + Ok(InferOk { obligations, value: () }) => { + self.table.register_predicates(obligations) + } + Err(_) => { + never!( + "subtyping remaining fields of type changing FRU \ + failed: {target_ty:?} != {fru_ty:?}: {:?}", + field.name, + ); + } + } + } + } + // The use of fresh args that we have subtyped against + // our base ADT type's fields allows us to guide inference + // along so that, e.g. + // ``` + // MyStruct<'a, F1, F2, const C: usize> { + // f: F1, + // // Other fields that reference `'a`, `F2`, and `C` + // } + // + // let x = MyStruct { + // f: 1usize, + // ..other_struct + // }; + // ``` + // will have the `other_struct` expression constrained to + // `MyStruct<'a, _, F2, C>`, as opposed to just `_`... + // This is important to allow coercions to happen in + // `other_struct` itself. See `coerce-in-base-expr.rs`. + let fresh_base_ty = Ty::new_adt(self.interner(), adt_id, fresh_args); + self.infer_expr_suptype_coerce_never( + base_expr, + &Expectation::has_type(self.resolve_vars_if_possible(fresh_base_ty)), + ExprIsRead::Yes, + ); + } else { + // Check the base_expr, regardless of a bad expected adt_ty, so we can get + // type errors on that expression, too. + self.infer_expr_no_expect(base_expr, ExprIsRead::Yes); + // FIXME: Emit an error: functional update syntax on non-struct. + } + } else { + self.infer_expr_suptype_coerce_never( + base_expr, + &Expectation::has_type(adt_ty), + ExprIsRead::Yes, + ); + if !matches!(adt_id, AdtId::StructId(_)) { + // FIXME: Emit an error: functional update syntax on non-struct. + } + } + } + RecordSpread::None => { + if !matches!(adt_id, AdtId::UnionId(_)) + && !remaining_fields.is_empty() + //~ non_exhaustive already reported, which will only happen for extern modules + && !self.has_applicable_non_exhaustive(adt_id.into()) + { + debug!(?remaining_fields); + + // FIXME: Emit an error: missing fields. + } + } + } + } + fn demand_scrutinee_type( &mut self, scrut: ExprId, @@ -1255,7 +1441,7 @@ impl<'db> InferenceContext<'_, 'db> { // NB: this should *not* coerce. // tail calls don't support any coercions except lifetimes ones (like `&'static u8 -> &'a u8`). - self.unify(call_expr_ty, ret_ty); + _ = self.demand_eqtype(expr.into(), call_expr_ty, ret_ty); } None => { // FIXME: diagnose `become` outside of functions @@ -1553,7 +1739,7 @@ impl<'db> InferenceContext<'_, 'db> { MethodCallee { def_id, args, sig } } - fn check_call( + fn infer_method_call_as_call( &mut self, tgt_expr: ExprId, args: &[ExprId], @@ -1564,7 +1750,14 @@ impl<'db> InferenceContext<'_, 'db> { is_varargs: bool, expected: &Expectation<'db>, ) -> Ty<'db> { - self.register_obligations_for_call(callee_ty); + if let TyKind::FnDef(def_id, args) = callee_ty.kind() { + let def_id = match def_id.0 { + CallableDefId::FunctionId(it) => it.into(), + CallableDefId::StructId(it) => it.into(), + CallableDefId::EnumVariantId(it) => it.loc(self.db).parent.into(), + }; + self.add_required_obligations_for_value_path(def_id, args); + } self.check_call_arguments( tgt_expr, @@ -1670,7 +1863,7 @@ impl<'db> InferenceContext<'_, 'db> { }), }; match recovered { - Some((callee_ty, sig, strip_first)) => self.check_call( + Some((callee_ty, sig, strip_first)) => self.infer_method_call_as_call( tgt_expr, args, callee_ty, @@ -1958,40 +2151,6 @@ impl<'db> InferenceContext<'_, 'db> { if !args_count_matches {} } - fn register_obligations_for_call(&mut self, callable_ty: Ty<'db>) { - let callable_ty = self.table.try_structurally_resolve_type(callable_ty); - if let TyKind::FnDef(fn_def, parameters) = callable_ty.kind() { - let generic_predicates = GenericPredicates::query_all( - self.db, - GenericDefId::from_callable(self.db, fn_def.0), - ); - let param_env = self.table.param_env; - self.table.register_predicates(clauses_as_obligations( - generic_predicates.iter_instantiated(self.interner(), parameters.as_slice()), - ObligationCause::new(), - param_env, - )); - // add obligation for trait implementation, if this is a trait method - match fn_def.0 { - CallableDefId::FunctionId(f) => { - if let ItemContainerId::TraitId(trait_) = f.lookup(self.db).container { - // construct a TraitRef - let trait_params_len = generics(self.db, trait_.into()).len(); - let substs = - GenericArgs::new_from_slice(¶meters.as_slice()[..trait_params_len]); - self.table.register_predicate(Obligation::new( - self.interner(), - ObligationCause::new(), - self.table.param_env, - TraitRef::new_from_args(self.interner(), trait_.into(), substs), - )); - } - } - CallableDefId::StructId(_) | CallableDefId::EnumVariantId(_) => {} - } - } - } - pub(super) fn with_breakable_ctx<T>( &mut self, kind: BreakableKind, diff --git a/crates/hir-ty/src/infer/opaques.rs b/crates/hir-ty/src/infer/opaques.rs index a39288721b..02bce22d6d 100644 --- a/crates/hir-ty/src/infer/opaques.rs +++ b/crates/hir-ty/src/infer/opaques.rs @@ -68,7 +68,7 @@ impl<'db> InferenceContext<'_, 'db> { mut opaque_types: Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)>, ) { for entry in opaque_types.iter_mut() { - *entry = self.table.infer_ctxt.resolve_vars_if_possible(*entry); + *entry = self.resolve_vars_if_possible(*entry); } debug!(?opaque_types); diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 835721942a..0687d56024 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -12,13 +12,11 @@ use stdx::never; use crate::{ InferenceDiagnostic, ValueTyDefId, - generics::generics, infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, lower::{GenericPredicates, LifetimeElisionKind}, method_resolution::{self, CandidateId, MethodError}, next_solver::{ - GenericArg, GenericArgs, TraitRef, Ty, - infer::traits::{Obligation, ObligationCause}, + GenericArg, GenericArgs, TraitRef, Ty, infer::traits::ObligationCause, util::clauses_as_obligations, }, }; @@ -226,7 +224,7 @@ impl<'db> InferenceContext<'_, 'db> { } } - fn add_required_obligations_for_value_path( + pub(super) fn add_required_obligations_for_value_path( &mut self, def: GenericDefId, subst: GenericArgs<'db>, @@ -239,25 +237,6 @@ impl<'db> InferenceContext<'_, 'db> { ObligationCause::new(), param_env, )); - - // We need to add `Self: Trait` obligation when `def` is a trait assoc item. - let container = match def { - GenericDefId::FunctionId(id) => id.lookup(self.db).container, - GenericDefId::ConstId(id) => id.lookup(self.db).container, - _ => return, - }; - - if let ItemContainerId::TraitId(trait_) = container { - let parent_len = generics(self.db, def).parent_generics().map_or(0, |g| g.len_self()); - let parent_subst = GenericArgs::new_from_slice(&subst.as_slice()[..parent_len]); - let trait_ref = TraitRef::new_from_args(interner, trait_.into(), parent_subst); - self.table.register_predicate(Obligation::new( - interner, - ObligationCause::new(), - param_env, - trait_ref, - )); - } } fn resolve_trait_assoc_item( @@ -334,25 +313,14 @@ impl<'db> InferenceContext<'_, 'db> { let impl_substs = self.table.fresh_args_for_item(impl_id.into()); let impl_self_ty = self.db.impl_self_ty(impl_id).instantiate(self.interner(), impl_substs); - self.unify(impl_self_ty, ty); + _ = self.demand_eqtype(id, impl_self_ty, ty); impl_substs } ItemContainerId::TraitId(trait_) => { // we're picking this method - let args = GenericArgs::fill_rest( - self.interner(), - trait_.into(), - [ty.into()], - |_, id, _| self.table.next_var_for_param(id), - ); - let trait_ref = TraitRef::new_from_args(self.interner(), trait_.into(), args); - self.table.register_predicate(Obligation::new( - self.interner(), - ObligationCause::new(), - self.table.param_env, - trait_ref, - )); - args + GenericArgs::fill_rest(self.interner(), trait_.into(), [ty.into()], |_, id, _| { + self.table.next_var_for_param(id) + }) } ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => { never!("assoc item contained in module/extern block"); diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index b0f916b8c0..c28fea0ff1 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -3,10 +3,10 @@ use std::fmt; use base_db::Crate; -use hir_def::{AdtId, ExpressionStoreOwnerId, GenericParamId, TraitId}; +use hir_def::{ExpressionStoreOwnerId, GenericParamId, TraitId}; use rustc_hash::FxHashSet; use rustc_type_ir::{ - TyVid, TypeFoldable, TypeVisitableExt, UpcastFrom, + TyVid, TypeFoldable, TypeVisitableExt, inherent::{Const as _, GenericArg as _, IntoKind, Ty as _}, solve::Certainty, }; @@ -15,23 +15,20 @@ use smallvec::SmallVec; use crate::{ db::HirDatabase, next_solver::{ - Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Goal, + Canonical, ClauseKind, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, ParamEnv, Predicate, PredicateKind, Region, SolverDefId, Term, TraitRef, Ty, TyKind, TypingMode, fulfill::{FulfillmentCtxt, NextSolverError}, infer::{ - DbInternerInferExt, InferCtxt, InferOk, InferResult, - at::{At, ToTrace}, + DbInternerInferExt, InferCtxt, InferOk, + at::At, snapshot::CombinedSnapshot, traits::{Obligation, ObligationCause, PredicateObligation}, }, inspect::{InspectConfig, InspectGoal, ProofTreeVisitor}, obligation_ctxt::ObligationCtxt, }, - traits::{ - NextTraitSolveResult, ParamEnvAndCrate, next_trait_solve_canonical_in_ctxt, - next_trait_solve_in_ctxt, - }, + traits::ParamEnvAndCrate, }; struct NestedObligationsForSelfTy<'a, 'db> { @@ -133,11 +130,6 @@ pub(crate) struct InferenceTable<'db> { pub(super) diverging_type_vars: FxHashSet<Ty<'db>>, } -pub(crate) struct InferenceTableSnapshot<'db> { - ctxt_snapshot: CombinedSnapshot, - obligations: FulfillmentCtxt<'db>, -} - impl<'db> InferenceTable<'db> { /// Inside hir-ty you should use this for inference only, and always pass `owner`. /// Outside it, always pass `owner = None`. @@ -145,14 +137,10 @@ impl<'db> InferenceTable<'db> { db: &'db dyn HirDatabase, trait_env: ParamEnv<'db>, krate: Crate, - owner: Option<ExpressionStoreOwnerId>, + owner: ExpressionStoreOwnerId, ) -> Self { let interner = DbInterner::new_with(db, krate); - let typing_mode = match owner { - Some(owner) => TypingMode::typeck_for_body(interner, owner.into()), - // IDE things wants to reveal opaque types. - None => TypingMode::PostAnalysis, - }; + let typing_mode = TypingMode::typeck_for_body(interner, owner.into()); let infer_ctxt = interner.infer_ctxt().build(typing_mode); InferenceTable { db, @@ -172,6 +160,10 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.type_is_copy_modulo_regions(self.param_env, ty) } + pub(crate) fn type_is_sized_modulo_regions(&self, ty: Ty<'db>) -> bool { + self.infer_ctxt.type_is_sized_modulo_regions(self.param_env, ty) + } + pub(crate) fn type_is_use_cloned_modulo_regions(&self, ty: Ty<'db>) -> bool { self.infer_ctxt.type_is_use_cloned_modulo_regions(self.param_env, ty) } @@ -253,23 +245,6 @@ impl<'db> InferenceTable<'db> { self.diverging_type_vars.insert(ty); } - pub(crate) fn canonicalize<T>(&mut self, t: T) -> rustc_type_ir::Canonical<DbInterner<'db>, T> - where - T: TypeFoldable<DbInterner<'db>>, - { - // try to resolve obligations before canonicalizing, since this might - // result in new knowledge about variables - self.select_obligations_where_possible(); - self.infer_ctxt.canonicalize_response(t) - } - - pub(crate) fn normalize_alias_ty(&mut self, alias: Ty<'db>) -> Ty<'db> { - self.infer_ctxt - .at(&ObligationCause::new(), self.param_env) - .structurally_normalize_ty(alias, &mut self.fulfillment_cx) - .unwrap_or(alias) - } - pub(crate) fn next_ty_var(&self) -> Ty<'db> { self.infer_ctxt.next_ty_var() } @@ -313,17 +288,6 @@ impl<'db> InferenceTable<'db> { value.fold_with(&mut resolve_completely::Resolver::new(self, true, &mut goals)) } - /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. - pub(crate) fn unify<T: ToTrace<'db>>(&mut self, ty1: T, ty2: T) -> bool { - self.try_unify(ty1, ty2).map(|infer_ok| self.register_infer_ok(infer_ok)).is_ok() - } - - /// Unify two relatable values (e.g. `Ty`) and return new trait goals arising from it, so the - /// caller needs to deal with them. - pub(crate) fn try_unify<T: ToTrace<'db>>(&mut self, t1: T, t2: T) -> InferResult<'db, ()> { - self.at(&ObligationCause::new()).eq(t1, t2) - } - pub(crate) fn at<'a>(&'a self, cause: &'a ObligationCause) -> At<'a, 'db> { self.infer_ctxt.at(cause, self.param_env) } @@ -332,6 +296,10 @@ impl<'db> InferenceTable<'db> { self.infer_ctxt.shallow_resolve(ty) } + pub(crate) fn resolve_vars_if_possible<T: TypeFoldable<DbInterner<'db>>>(&self, t: T) -> T { + self.infer_ctxt.resolve_vars_if_possible(t) + } + pub(crate) fn resolve_vars_with_obligations<T>(&mut self, t: T) -> T where T: rustc_type_ir::TypeFoldable<DbInterner<'db>>, @@ -380,16 +348,13 @@ impl<'db> InferenceTable<'db> { // FIXME: Err if it still contain infer vars. } - pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot<'db> { - let ctxt_snapshot = self.infer_ctxt.start_snapshot(); - let obligations = self.fulfillment_cx.clone(); - InferenceTableSnapshot { ctxt_snapshot, obligations } + pub(crate) fn snapshot(&mut self) -> CombinedSnapshot { + self.infer_ctxt.start_snapshot() } #[tracing::instrument(skip_all)] - pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot<'db>) { - self.infer_ctxt.rollback_to(snapshot.ctxt_snapshot); - self.fulfillment_cx = snapshot.obligations; + pub(crate) fn rollback_to(&mut self, snapshot: CombinedSnapshot) { + self.infer_ctxt.rollback_to(snapshot); } pub(crate) fn commit_if_ok<T, E>( @@ -399,51 +364,12 @@ impl<'db> InferenceTable<'db> { let snapshot = self.snapshot(); let result = f(self); match result { - Ok(_) => {} - Err(_) => { - self.rollback_to(snapshot); - } + Ok(_) => self.infer_ctxt.commit_from(snapshot), + Err(_) => self.rollback_to(snapshot), } result } - /// Checks an obligation without registering it. Useful mostly to check - /// whether a trait *might* be implemented before deciding to 'lock in' the - /// choice (during e.g. method resolution or deref). - #[tracing::instrument(level = "debug", skip(self))] - pub(crate) fn try_obligation(&mut self, predicate: Predicate<'db>) -> NextTraitSolveResult { - let goal = Goal { param_env: self.param_env, predicate }; - let canonicalized = self.canonicalize(goal); - - next_trait_solve_canonical_in_ctxt(&self.infer_ctxt, canonicalized) - } - - pub(crate) fn register_obligation(&mut self, predicate: Predicate<'db>) { - let goal = Goal { param_env: self.param_env, predicate }; - self.register_obligation_in_env(goal) - } - - #[tracing::instrument(level = "debug", skip(self))] - fn register_obligation_in_env(&mut self, goal: Goal<'db, Predicate<'db>>) { - let result = next_trait_solve_in_ctxt(&self.infer_ctxt, goal); - tracing::debug!(?result); - match result { - Ok((_, Certainty::Yes)) => {} - Err(rustc_type_ir::solve::NoSolution) => {} - Ok((_, Certainty::Maybe { .. })) => { - self.fulfillment_cx.register_predicate_obligation( - &self.infer_ctxt, - Obligation::new( - self.interner(), - ObligationCause::new(), - goal.param_env, - goal.predicate, - ), - ); - } - } - } - pub(crate) fn register_bound(&mut self, ty: Ty<'db>, def_id: TraitId, cause: ObligationCause) { if !ty.references_non_lt_error() { let trait_ref = TraitRef::new(self.interner(), def_id.into(), [ty]); @@ -533,70 +459,6 @@ impl<'db> InferenceTable<'db> { pub(super) fn insert_const_vars_shallow(&mut self, c: Const<'db>) -> Const<'db> { if c.is_ct_error() { self.next_const_var() } else { c } } - - /// Check if given type is `Sized` or not - pub(crate) fn is_sized(&mut self, ty: Ty<'db>) -> bool { - fn short_circuit_trivial_tys(ty: Ty<'_>) -> Option<bool> { - match ty.kind() { - TyKind::Bool - | TyKind::Char - | TyKind::Int(_) - | TyKind::Uint(_) - | TyKind::Float(_) - | TyKind::Ref(..) - | TyKind::RawPtr(..) - | TyKind::Never - | TyKind::FnDef(..) - | TyKind::Array(..) - | TyKind::FnPtr(..) => Some(true), - TyKind::Slice(..) | TyKind::Str | TyKind::Dynamic(..) => Some(false), - _ => None, - } - } - - let mut ty = ty; - ty = self.try_structurally_resolve_type(ty); - if let Some(sized) = short_circuit_trivial_tys(ty) { - return sized; - } - - { - let mut structs = SmallVec::<[_; 8]>::new(); - // Must use a loop here and not recursion because otherwise users will conduct completely - // artificial examples of structs that have themselves as the tail field and complain r-a crashes. - while let Some((AdtId::StructId(id), subst)) = ty.as_adt() { - let struct_data = id.fields(self.db); - if let Some((last_field, _)) = struct_data.fields().iter().next_back() { - let last_field_ty = self.db.field_types(id.into())[last_field] - .get() - .instantiate(self.interner(), subst); - if structs.contains(&ty) { - // A struct recursively contains itself as a tail field somewhere. - return true; // Don't overload the users with too many errors. - } - structs.push(ty); - // Structs can have DST as its last field and such cases are not handled - // as unsized by the chalk, so we do this manually. - ty = last_field_ty; - ty = self.try_structurally_resolve_type(ty); - if let Some(sized) = short_circuit_trivial_tys(ty) { - return sized; - } - } else { - break; - }; - } - } - - let Some(sized) = self.interner().lang_items().Sized else { - return false; - }; - let sized_pred = Predicate::upcast_from( - TraitRef::new(self.interner(), sized.into(), [ty]), - self.interner(), - ); - self.try_obligation(sized_pred).certain() - } } impl fmt::Debug for InferenceTable<'_> { diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index c6c7856c8c..2973b970f3 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -71,7 +71,7 @@ use macros::GenericTypeVisitable; use mir::{MirEvalError, VTableMap}; use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet}; use rustc_type_ir::{ - BoundVarIndexKind, TypeSuperVisitable, TypeVisitableExt, UpcastFrom, + BoundVarIndexKind, TypeSuperVisitable, TypeVisitableExt, inherent::{IntoKind, Ty as _}, }; use syntax::ast::{ConstArg, make}; @@ -80,12 +80,17 @@ use traits::FnTrait; use crate::{ db::HirDatabase, display::{DisplayTarget, HirDisplay}, - infer::unify::InferenceTable, lower::SupertraitsInfo, next_solver::{ AliasTy, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, Canonical, - CanonicalVarKind, CanonicalVarKinds, ClauseKind, Const, ConstKind, DbInterner, FnSig, - GenericArgs, PolyFnSig, Predicate, Region, RegionKind, TraitRef, Ty, TyKind, Tys, abi, + CanonicalVarKind, CanonicalVarKinds, ClauseKind, Const, ConstKind, DbInterner, GenericArgs, + PolyFnSig, Region, RegionKind, TraitRef, Ty, TyKind, TypingMode, + abi::Safety, + infer::{ + DbInternerInferExt, + traits::{Obligation, ObligationCause}, + }, + obligation_ctxt::ObligationCtxt, }, }; @@ -525,68 +530,64 @@ pub fn associated_type_shorthand_candidates( /// To be used from `hir` only. pub fn callable_sig_from_fn_trait<'db>( self_ty: Ty<'db>, - trait_env: ParamEnvAndCrate<'db>, + param_env: ParamEnvAndCrate<'db>, db: &'db dyn HirDatabase, ) -> Option<(FnTrait, PolyFnSig<'db>)> { - let mut table = InferenceTable::new(db, trait_env.param_env, trait_env.krate, None); - let lang_items = table.interner().lang_items(); + let ParamEnvAndCrate { param_env, krate } = param_env; + let interner = DbInterner::new_with(db, krate); + let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); + let lang_items = interner.lang_items(); + let cause = ObligationCause::dummy(); + + let impls_trait = |trait_: FnTrait| { + let mut ocx = ObligationCtxt::new(&infcx); + let tupled_args = infcx.next_ty_var(); + let args = GenericArgs::new_from_slice(&[self_ty.into(), tupled_args.into()]); + let trait_id = trait_.get_id(lang_items)?; + let trait_ref = TraitRef::new_from_args(interner, trait_id.into(), args); + let obligation = Obligation::new(interner, cause.clone(), param_env, trait_ref); + ocx.register_obligation(obligation); + if !ocx.try_evaluate_obligations().is_empty() { + return None; + } + let tupled_args = + infcx.resolve_vars_if_possible(tupled_args).replace_infer_with_error(interner); + if tupled_args.is_tuple() { Some(tupled_args) } else { None } + }; - let fn_once_trait = FnTrait::FnOnce.get_id(lang_items)?; + let (trait_, args) = 'find_trait: { + for trait_ in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { + if let Some(args) = impls_trait(trait_) { + break 'find_trait (trait_, args); + } + } + return None; + }; + + let fn_once_trait = lang_items.FnOnce?; let output_assoc_type = fn_once_trait .trait_items(db) .associated_type_by_name(&Name::new_symbol_root(sym::Output))?; - - // Register two obligations: - // - Self: FnOnce<?args_ty> - // - <Self as FnOnce<?args_ty>>::Output == ?ret_ty - let args_ty = table.next_ty_var(); - let args = GenericArgs::new_from_slice(&[self_ty.into(), args_ty.into()]); - let trait_ref = TraitRef::new_from_args(table.interner(), fn_once_trait.into(), args); - let projection = Ty::new_alias( - table.interner(), - AliasTy::new_from_args( - table.interner(), + let output_projection = Ty::new_alias( + interner, + AliasTy::new( + interner, rustc_type_ir::Projection { def_id: output_assoc_type.into() }, - args, + [self_ty, args], ), ); - - let pred = Predicate::upcast_from(trait_ref, table.interner()); - if !table.try_obligation(pred).no_solution() { - table.register_obligation(pred); - let return_ty = table.normalize_alias_ty(projection); - for fn_x in [FnTrait::Fn, FnTrait::FnMut, FnTrait::FnOnce] { - let fn_x_trait = fn_x.get_id(lang_items)?; - let trait_ref = TraitRef::new_from_args(table.interner(), fn_x_trait.into(), args); - if !table - .try_obligation(Predicate::upcast_from(trait_ref, table.interner())) - .no_solution() - { - let ret_ty = table.resolve_completely(return_ty); - let args_ty = table.resolve_completely(args_ty); - let TyKind::Tuple(params) = args_ty.kind() else { - return None; - }; - let inputs_and_output = Tys::new_from_iter( - table.interner(), - params.iter().chain(std::iter::once(ret_ty)), - ); - - return Some(( - fn_x, - Binder::dummy(FnSig { - inputs_and_output, - c_variadic: false, - safety: abi::Safety::Safe, - abi: FnAbi::RustCall, - }), - )); - } - } - unreachable!("It should at least implement FnOnce at this point"); - } else { - None - } + let mut ocx = ObligationCtxt::new(&infcx); + let ret = ocx.structurally_normalize_ty(&cause, param_env, output_projection).ok()?; + let ret = ret.replace_infer_with_error(interner); + + let sig = Binder::dummy(interner.mk_fn_sig( + args.tuple_fields(), + ret, + false, + Safety::Safe, + FnAbi::Rust, + )); + Some((trait_, sig)) } struct ParamCollector { diff --git a/crates/hir-ty/src/next_solver/fulfill.rs b/crates/hir-ty/src/next_solver/fulfill.rs index 6739795a00..cd0cb59760 100644 --- a/crates/hir-ty/src/next_solver/fulfill.rs +++ b/crates/hir-ty/src/next_solver/fulfill.rs @@ -44,7 +44,6 @@ pub struct FulfillmentCtxt<'db> { /// outside of this snapshot leads to subtle bugs if the snapshot /// gets rolled back. Because of this we explicitly check that we only /// use the context in exactly this snapshot. - #[expect(unused)] usable_in_snapshot: usize, try_evaluate_obligations_scratch: PendingObligations<'db>, } @@ -120,24 +119,22 @@ impl<'db> FulfillmentCtxt<'db> { } impl<'db> FulfillmentCtxt<'db> { - #[tracing::instrument(level = "trace", skip(self, _infcx))] + #[tracing::instrument(level = "trace", skip(self, infcx))] pub(crate) fn register_predicate_obligation( &mut self, - _infcx: &InferCtxt<'db>, + infcx: &InferCtxt<'db>, obligation: PredicateObligation<'db>, ) { - // FIXME: See the comment in `try_evaluate_obligations()`. - // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); self.obligations.register(obligation, None); } pub(crate) fn register_predicate_obligations( &mut self, - _infcx: &InferCtxt<'db>, + infcx: &InferCtxt<'db>, obligations: impl IntoIterator<Item = PredicateObligation<'db>>, ) { - // FIXME: See the comment in `try_evaluate_obligations()`. - // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); obligations.into_iter().for_each(|obligation| self.obligations.register(obligation, None)); } @@ -157,11 +154,7 @@ impl<'db> FulfillmentCtxt<'db> { &mut self, infcx: &InferCtxt<'db>, ) -> Vec<NextSolverError<'db>> { - // FIXME(next-solver): We should bring this assertion back. Currently it panics because - // there are places which use `InferenceTable` and open a snapshot and register obligations - // and select. They should use a different `ObligationCtxt` instead. Then we'll be also able - // to not put the obligations queue in `InferenceTable`'s snapshots. - // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); self.try_evaluate_obligations_scratch.clear(); let mut errors = Vec::new(); loop { diff --git a/crates/hir-ty/src/next_solver/infer/mod.rs b/crates/hir-ty/src/next_solver/infer/mod.rs index de21c5442b..1eacc295c9 100644 --- a/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/crates/hir-ty/src/next_solver/infer/mod.rs @@ -58,7 +58,7 @@ pub mod relate; pub mod resolve; pub mod select; pub(crate) mod snapshot; -pub(crate) mod traits; +pub mod traits; mod type_variable; mod unify_key; @@ -494,8 +494,7 @@ impl<'db> InferCtxt<'db> { /// check::<&'_ T>(); /// } /// ``` - #[expect(dead_code, reason = "this is used in rustc")] - fn predicate_must_hold_considering_regions( + pub fn predicate_must_hold_considering_regions( &self, obligation: &PredicateObligation<'db>, ) -> bool { @@ -507,8 +506,10 @@ impl<'db> InferCtxt<'db> { /// not entirely accurate if inference variables are involved. /// /// This version ignores all outlives constraints. - #[expect(dead_code, reason = "this is used in rustc")] - fn predicate_must_hold_modulo_regions(&self, obligation: &PredicateObligation<'db>) -> bool { + pub fn predicate_must_hold_modulo_regions( + &self, + obligation: &PredicateObligation<'db>, + ) -> bool { self.evaluate_obligation(obligation).must_apply_modulo_regions() } @@ -610,6 +611,13 @@ impl<'db> InferCtxt<'db> { traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id) } + pub fn type_is_sized_modulo_regions(&self, param_env: ParamEnv<'db>, ty: Ty<'db>) -> bool { + let Some(sized_def_id) = self.interner.lang_items().Sized else { + return true; + }; + traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, sized_def_id) + } + pub fn type_is_use_cloned_modulo_regions(&self, param_env: ParamEnv<'db>, ty: Ty<'db>) -> bool { let ty = self.resolve_vars_if_possible(ty); diff --git a/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs b/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs index 705aa43fb1..39c8a37adb 100644 --- a/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs +++ b/crates/hir-ty/src/next_solver/infer/snapshot/mod.rs @@ -47,7 +47,7 @@ impl<'db> InferCtxt<'db> { UndoLogs::<UndoLog<'db>>::num_open_snapshots(&self.inner.borrow_mut().undo_log) } - pub(crate) fn start_snapshot(&self) -> CombinedSnapshot { + pub fn start_snapshot(&self) -> CombinedSnapshot { debug!("start_snapshot()"); let mut inner = self.inner.borrow_mut(); @@ -60,7 +60,7 @@ impl<'db> InferCtxt<'db> { } #[instrument(skip(self, snapshot), level = "debug")] - pub(crate) fn rollback_to(&self, snapshot: CombinedSnapshot) { + pub fn rollback_to(&self, snapshot: CombinedSnapshot) { let CombinedSnapshot { undo_snapshot, region_constraints_snapshot, universe } = snapshot; self.universe.set(universe); @@ -71,7 +71,7 @@ impl<'db> InferCtxt<'db> { } #[instrument(skip(self, snapshot), level = "debug")] - fn commit_from(&self, snapshot: CombinedSnapshot) { + pub fn commit_from(&self, snapshot: CombinedSnapshot) { let CombinedSnapshot { undo_snapshot, region_constraints_snapshot: _, universe: _ } = snapshot; diff --git a/crates/hir-ty/src/next_solver/infer/traits.rs b/crates/hir-ty/src/next_solver/infer/traits.rs index dde6234836..5b875d2960 100644 --- a/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/crates/hir-ty/src/next_solver/infer/traits.rs @@ -185,12 +185,12 @@ impl<'db> PredicateObligation<'db> { impl<'db, O> Obligation<'db, O> { pub fn new( - tcx: DbInterner<'db>, + interner: DbInterner<'db>, cause: ObligationCause, param_env: ParamEnv<'db>, predicate: impl Upcast<DbInterner<'db>, O>, ) -> Obligation<'db, O> { - Self::with_depth(tcx, cause, 0, param_env, predicate) + Self::with_depth(interner, cause, 0, param_env, predicate) } /// We often create nested obligations without setting the correct depth. @@ -202,13 +202,13 @@ impl<'db, O> Obligation<'db, O> { } pub fn with_depth( - tcx: DbInterner<'db>, + interner: DbInterner<'db>, cause: ObligationCause, recursion_depth: usize, param_env: ParamEnv<'db>, predicate: impl Upcast<DbInterner<'db>, O>, ) -> Obligation<'db, O> { - let predicate = predicate.upcast(tcx); + let predicate = predicate.upcast(interner); Obligation { cause, param_env, recursion_depth, predicate } } diff --git a/crates/hir-ty/src/next_solver/ty.rs b/crates/hir-ty/src/next_solver/ty.rs index c953e79602..0fd02eb8ca 100644 --- a/crates/hir-ty/src/next_solver/ty.rs +++ b/crates/hir-ty/src/next_solver/ty.rs @@ -626,6 +626,10 @@ impl<'db> Ty<'db> { } } + pub fn is_tuple(self) -> bool { + matches!(self.kind(), TyKind::Tuple(_)) + } + pub fn as_tuple(self) -> Option<Tys<'db>> { match self.kind() { TyKind::Tuple(tys) => Some(tys), diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index 878696c721..0dc834ddcc 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -15,18 +15,15 @@ use hir_def::{ }; use hir_expand::name::Name; use intern::sym; -use rustc_next_trait_solver::solve::{HasChanged, SolverDelegateEvalExt}; use rustc_type_ir::{ TypingMode, - inherent::{AdtDef, BoundExistentialPredicates, IntoKind, Span as _}, - solve::Certainty, + inherent::{AdtDef, BoundExistentialPredicates, IntoKind}, }; use crate::{ db::HirDatabase, next_solver::{ - Canonical, DbInterner, GenericArgs, Goal, ParamEnv, Predicate, SolverContext, Span, - StoredClauses, Ty, TyKind, + DbInterner, GenericArgs, ParamEnv, StoredClauses, Ty, TyKind, infer::{ DbInternerInferExt, InferCtxt, traits::{Obligation, ObligationCause}, @@ -79,91 +76,6 @@ pub fn structurally_normalize_ty<'db>( ty.replace_infer_with_error(infcx.interner) } -#[derive(Clone, Debug, PartialEq)] -pub enum NextTraitSolveResult { - Certain, - Uncertain, - NoSolution, -} - -impl NextTraitSolveResult { - pub fn no_solution(&self) -> bool { - matches!(self, NextTraitSolveResult::NoSolution) - } - - pub fn certain(&self) -> bool { - matches!(self, NextTraitSolveResult::Certain) - } - - pub fn uncertain(&self) -> bool { - matches!(self, NextTraitSolveResult::Uncertain) - } -} - -pub fn next_trait_solve_canonical_in_ctxt<'db>( - infer_ctxt: &InferCtxt<'db>, - goal: Canonical<'db, Goal<'db, Predicate<'db>>>, -) -> NextTraitSolveResult { - infer_ctxt.probe(|_| { - let context = <&SolverContext<'db>>::from(infer_ctxt); - - tracing::info!(?goal); - - let (goal, var_values) = context.instantiate_canonical(&goal); - tracing::info!(?var_values); - - let res = context.evaluate_root_goal(goal, Span::dummy(), None); - - let obligation = Obligation { - cause: ObligationCause::dummy(), - param_env: goal.param_env, - recursion_depth: 0, - predicate: goal.predicate, - }; - infer_ctxt.inspect_evaluated_obligation(&obligation, &res, || { - Some(context.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1) - }); - - let res = res.map(|r| (r.has_changed, r.certainty)); - - tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); - - match res { - Err(_) => NextTraitSolveResult::NoSolution, - Ok((_, Certainty::Yes)) => NextTraitSolveResult::Certain, - Ok((_, Certainty::Maybe { .. })) => NextTraitSolveResult::Uncertain, - } - }) -} - -/// Solve a trait goal using next trait solver. -pub fn next_trait_solve_in_ctxt<'db, 'a>( - infer_ctxt: &'a InferCtxt<'db>, - goal: Goal<'db, Predicate<'db>>, -) -> Result<(HasChanged, Certainty), rustc_type_ir::solve::NoSolution> { - tracing::info!(?goal); - - let context = <&SolverContext<'db>>::from(infer_ctxt); - - let res = context.evaluate_root_goal(goal, Span::dummy(), None); - - let obligation = Obligation { - cause: ObligationCause::dummy(), - param_env: goal.param_env, - recursion_depth: 0, - predicate: goal.predicate, - }; - infer_ctxt.inspect_evaluated_obligation(&obligation, &res, || { - Some(context.evaluate_root_goal_for_proof_tree(goal, Span::dummy()).1) - }); - - let res = res.map(|r| (r.has_changed, r.certainty)); - - tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res); - - res -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Update)] pub enum FnTrait { // Warning: Order is important. If something implements `x` it should also implement @@ -235,10 +147,9 @@ fn implements_trait_unique_impl<'db>( let args = create_args(&infcx); let trait_ref = rustc_type_ir::TraitRef::new_from_args(interner, trait_.into(), args); - let goal = Goal::new(interner, env.param_env, trait_ref); - let result = crate::traits::next_trait_solve_in_ctxt(&infcx, goal); - matches!(result, Ok((_, Certainty::Yes))) + let obligation = Obligation::new(interner, ObligationCause::dummy(), env.param_env, trait_ref); + infcx.predicate_must_hold_modulo_regions(&obligation) } pub fn is_inherent_impl_coherent(db: &dyn HirDatabase, def_map: &DefMap, impl_id: ImplId) -> bool { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index ecd11fb5d7..11598f2a10 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -6561,21 +6561,13 @@ impl<'db> TypeNs<'db> { ); let trait_ref = hir_ty::next_solver::TraitRef::new_from_args(infcx.interner, trait_.id.into(), args); - - let pred_kind = rustc_type_ir::Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(rustc_type_ir::TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )); - let predicate = hir_ty::next_solver::Predicate::new(infcx.interner, pred_kind); - let goal = hir_ty::next_solver::Goal::new( + let obligation = hir_ty::next_solver::infer::traits::Obligation::new( infcx.interner, - hir_ty::next_solver::ParamEnv::empty(), - predicate, + hir_ty::next_solver::infer::traits::ObligationCause::dummy(), + self.env.param_env, + trait_ref, ); - let res = hir_ty::traits::next_trait_solve_in_ctxt(&infcx, goal); - res.map_or(false, |res| matches!(res.1, rustc_type_ir::solve::Certainty::Yes)) + infcx.predicate_must_hold_modulo_regions(&obligation) } pub fn is_bool(&self) -> bool { diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index 8dcc73d81f..25c2e3f733 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/crates/intern/src/symbol/symbols.rs @@ -583,4 +583,5 @@ define_symbols! { ref_pat_eat_one_layer_2024_structural, deref_patterns, mut_ref, + type_changing_struct_update, } |