mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-12-23 08:48:08 +00:00
Properly support opaques
By letting the solver take control of them (reveal them when needed and define them when needed), by providing them in the `TypingMode` plus few helpers.
This commit is contained in:
parent
b4de9df2cd
commit
537b31b55a
28 changed files with 742 additions and 500 deletions
|
|
@ -33,6 +33,7 @@ trivias = "trivias"
|
|||
thir = "thir"
|
||||
jod = "jod"
|
||||
tructure = "tructure"
|
||||
taits = "taits"
|
||||
|
||||
[default.extend-identifiers]
|
||||
anc = "anc"
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ pub fn autoderef<'db>(
|
|||
env: Arc<TraitEnvironment<'db>>,
|
||||
ty: Canonical<'db, Ty<'db>>,
|
||||
) -> impl Iterator<Item = Ty<'db>> + use<'db> {
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let mut table = InferenceTable::new(db, env, None);
|
||||
let ty = table.instantiate_canonical(ty);
|
||||
let mut autoderef = Autoderef::new_no_tracking(&mut table, ty);
|
||||
let mut v = Vec::new();
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ pub(crate) mod diagnostics;
|
|||
mod expr;
|
||||
mod fallback;
|
||||
mod mutability;
|
||||
mod opaques;
|
||||
mod pat;
|
||||
mod path;
|
||||
pub(crate) mod unify;
|
||||
|
|
@ -31,8 +32,7 @@ use base_db::Crate;
|
|||
use either::Either;
|
||||
use hir_def::{
|
||||
AdtId, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId,
|
||||
ImplId, ItemContainerId, LocalFieldId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId,
|
||||
VariantId,
|
||||
ItemContainerId, LocalFieldId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId,
|
||||
expr_store::{Body, ExpressionStore, HygieneId, path::Path},
|
||||
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId},
|
||||
lang_item::{LangItem, LangItemTarget, lang_item},
|
||||
|
|
@ -44,11 +44,11 @@ use hir_def::{
|
|||
use hir_expand::{mod_path::ModPath, name::Name};
|
||||
use indexmap::IndexSet;
|
||||
use intern::sym;
|
||||
use la_arena::{ArenaMap, Entry};
|
||||
use la_arena::ArenaMap;
|
||||
use rustc_ast_ir::Mutability;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustc_type_ir::{
|
||||
AliasTyKind, Flags, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor,
|
||||
AliasTyKind, TypeFoldable,
|
||||
inherent::{AdtDef, IntoKind, Region as _, SliceLike, Ty as _},
|
||||
};
|
||||
use stdx::never;
|
||||
|
|
@ -61,7 +61,6 @@ use crate::{
|
|||
coerce::{CoerceMany, DynamicCoerceMany},
|
||||
diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext},
|
||||
expr::ExprIsRead,
|
||||
unify::InferenceTable,
|
||||
},
|
||||
lower::{
|
||||
ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic,
|
||||
|
|
@ -69,10 +68,7 @@ use crate::{
|
|||
mir::MirSpan,
|
||||
next_solver::{
|
||||
AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region, Ty, TyKind,
|
||||
Tys,
|
||||
abi::Safety,
|
||||
fold::fold_tys,
|
||||
infer::traits::{Obligation, ObligationCause},
|
||||
Tys, abi::Safety, infer::traits::ObligationCause,
|
||||
},
|
||||
traits::FnTrait,
|
||||
utils::TargetFeatureIsSafeInTarget,
|
||||
|
|
@ -132,6 +128,8 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
|
|||
|
||||
ctx.infer_mut_body();
|
||||
|
||||
ctx.handle_opaque_type_uses();
|
||||
|
||||
ctx.type_inference_fallback();
|
||||
|
||||
// Comment from rustc:
|
||||
|
|
@ -148,6 +146,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
|
|||
|
||||
ctx.infer_closures();
|
||||
|
||||
ctx.table.select_obligations_where_possible();
|
||||
|
||||
ctx.handle_opaque_type_uses();
|
||||
|
||||
Arc::new(ctx.resolve_all())
|
||||
}
|
||||
|
||||
|
|
@ -454,7 +456,7 @@ pub struct InferenceResult<'db> {
|
|||
/// unresolved or missing subpatterns or subpatterns of mismatched types.
|
||||
pub(crate) type_of_pat: ArenaMap<PatId, Ty<'db>>,
|
||||
pub(crate) type_of_binding: ArenaMap<BindingId, Ty<'db>>,
|
||||
pub(crate) type_of_rpit: ArenaMap<ImplTraitIdx<'db>, Ty<'db>>,
|
||||
pub(crate) type_of_opaque: FxHashMap<InternedOpaqueTyId, Ty<'db>>,
|
||||
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch<'db>>,
|
||||
/// Whether there are any type-mismatching errors in the result.
|
||||
// FIXME: This isn't as useful as initially thought due to us falling back placeholders to
|
||||
|
|
@ -499,7 +501,7 @@ impl<'db> InferenceResult<'db> {
|
|||
type_of_expr: Default::default(),
|
||||
type_of_pat: Default::default(),
|
||||
type_of_binding: Default::default(),
|
||||
type_of_rpit: Default::default(),
|
||||
type_of_opaque: Default::default(),
|
||||
type_mismatches: Default::default(),
|
||||
has_errors: Default::default(),
|
||||
error_ty,
|
||||
|
|
@ -640,8 +642,14 @@ impl<'db> InferenceResult<'db> {
|
|||
// This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please.
|
||||
pub fn return_position_impl_trait_types(
|
||||
&self,
|
||||
db: &'db dyn HirDatabase,
|
||||
) -> impl Iterator<Item = (ImplTraitIdx<'db>, Ty<'db>)> {
|
||||
self.type_of_rpit.iter().map(|(k, v)| (k, *v))
|
||||
self.type_of_opaque.iter().filter_map(move |(&id, &ty)| {
|
||||
let ImplTraitId::ReturnTypeImplTrait(_, rpit_idx) = id.loc(db) else {
|
||||
return None;
|
||||
};
|
||||
Some((rpit_idx, ty))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -707,6 +715,7 @@ struct InternedStandardTypes<'db> {
|
|||
|
||||
re_static: Region<'db>,
|
||||
re_error: Region<'db>,
|
||||
re_erased: Region<'db>,
|
||||
|
||||
empty_args: GenericArgs<'db>,
|
||||
empty_tys: Tys<'db>,
|
||||
|
|
@ -742,6 +751,7 @@ impl<'db> InternedStandardTypes<'db> {
|
|||
|
||||
re_static,
|
||||
re_error: Region::error(interner),
|
||||
re_erased: Region::new_erased(interner),
|
||||
|
||||
empty_args: GenericArgs::new_from_iter(interner, []),
|
||||
empty_tys: Tys::new_from_iter(interner, []),
|
||||
|
|
@ -848,11 +858,6 @@ fn find_continuable<'a, 'db>(
|
|||
}
|
||||
}
|
||||
|
||||
enum ImplTraitReplacingMode<'db> {
|
||||
ReturnPosition(FxHashSet<Ty<'db>>),
|
||||
TypeAlias,
|
||||
}
|
||||
|
||||
impl<'body, 'db> InferenceContext<'body, 'db> {
|
||||
fn new(
|
||||
db: &'db dyn HirDatabase,
|
||||
|
|
@ -861,7 +866,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
|||
resolver: Resolver<'db>,
|
||||
) -> Self {
|
||||
let trait_env = db.trait_environment_for_body(owner);
|
||||
let table = unify::InferenceTable::new(db, trait_env);
|
||||
let table = unify::InferenceTable::new(db, trait_env, Some(owner));
|
||||
let types = InternedStandardTypes::new(table.interner());
|
||||
InferenceContext {
|
||||
result: InferenceResult::new(types.error),
|
||||
|
|
@ -952,7 +957,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
|||
// `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you
|
||||
// used this function for another workaround, mention it here. If you really need this function and believe that
|
||||
// there is no problem in it being `pub(crate)`, remove this comment.
|
||||
pub(crate) fn resolve_all(self) -> InferenceResult<'db> {
|
||||
fn resolve_all(self) -> InferenceResult<'db> {
|
||||
let InferenceContext {
|
||||
mut table, mut result, tuple_field_accesses_rev, diagnostics, ..
|
||||
} = self;
|
||||
|
|
@ -967,7 +972,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
|||
type_of_expr,
|
||||
type_of_pat,
|
||||
type_of_binding,
|
||||
type_of_rpit,
|
||||
type_of_opaque,
|
||||
type_mismatches,
|
||||
has_errors,
|
||||
error_ty: _,
|
||||
|
|
@ -999,11 +1004,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
|||
*has_errors = *has_errors || ty.references_non_lt_error();
|
||||
}
|
||||
type_of_binding.shrink_to_fit();
|
||||
for ty in type_of_rpit.values_mut() {
|
||||
*ty = table.resolve_completely(*ty);
|
||||
*has_errors = *has_errors || ty.references_non_lt_error();
|
||||
}
|
||||
type_of_rpit.shrink_to_fit();
|
||||
type_of_opaque.shrink_to_fit();
|
||||
|
||||
*has_errors |= !type_mismatches.is_empty();
|
||||
|
||||
|
|
@ -1084,9 +1085,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
|||
LifetimeElisionKind::for_const(self.interner(), id.loc(self.db).container),
|
||||
);
|
||||
|
||||
// Constants might be defining usage sites of TAITs.
|
||||
self.make_tait_coercion_table(iter::once(return_ty));
|
||||
|
||||
self.return_ty = return_ty;
|
||||
}
|
||||
|
||||
|
|
@ -1098,9 +1096,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
|||
LifetimeElisionKind::Elided(self.types.re_static),
|
||||
);
|
||||
|
||||
// Statics might be defining usage sites of TAITs.
|
||||
self.make_tait_coercion_table(iter::once(return_ty));
|
||||
|
||||
self.return_ty = return_ty;
|
||||
}
|
||||
|
||||
|
|
@ -1138,16 +1133,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
|||
let ty = self.process_user_written_ty(ty);
|
||||
self.write_binding_ty(self_param, ty);
|
||||
}
|
||||
let mut tait_candidates = FxHashSet::default();
|
||||
for (ty, pat) in param_tys.zip(&*self.body.params) {
|
||||
let ty = self.process_user_written_ty(ty);
|
||||
|
||||
self.infer_top_pat(*pat, ty, None);
|
||||
if ty.flags().intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER)) {
|
||||
tait_candidates.insert(ty);
|
||||
}
|
||||
}
|
||||
let return_ty = match data.ret_type {
|
||||
self.return_ty = match data.ret_type {
|
||||
Some(return_ty) => {
|
||||
let return_ty = self.with_ty_lowering(
|
||||
&data.store,
|
||||
|
|
@ -1158,45 +1149,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
|||
ctx.lower_ty(return_ty)
|
||||
},
|
||||
);
|
||||
let return_ty = self.insert_type_vars(return_ty);
|
||||
if let Some(rpits) = self.db.return_type_impl_traits(func) {
|
||||
let mut mode = ImplTraitReplacingMode::ReturnPosition(FxHashSet::default());
|
||||
let result = self.insert_inference_vars_for_impl_trait(return_ty, &mut mode);
|
||||
if let ImplTraitReplacingMode::ReturnPosition(taits) = mode {
|
||||
tait_candidates.extend(taits);
|
||||
}
|
||||
let rpits = (*rpits).as_ref().skip_binder();
|
||||
for (id, _) in rpits.impl_traits.iter() {
|
||||
if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
|
||||
never!("Missed RPIT in `insert_inference_vars_for_rpit`");
|
||||
e.insert(self.types.error);
|
||||
}
|
||||
}
|
||||
result
|
||||
} else {
|
||||
return_ty
|
||||
}
|
||||
self.process_user_written_ty(return_ty)
|
||||
}
|
||||
None => self.types.unit,
|
||||
};
|
||||
|
||||
self.return_ty = self.process_user_written_ty(return_ty);
|
||||
self.return_coercion = Some(CoerceMany::new(self.return_ty));
|
||||
|
||||
// Functions might be defining usage sites of TAITs.
|
||||
// To define an TAITs, that TAIT must appear in the function's signatures.
|
||||
// So, it suffices to check for params and return types.
|
||||
fold_tys(self.interner(), self.return_ty, |ty| {
|
||||
match ty.kind() {
|
||||
TyKind::Alias(AliasTyKind::Opaque, _) | TyKind::Infer(..) => {
|
||||
tait_candidates.insert(self.return_ty);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ty
|
||||
});
|
||||
|
||||
self.make_tait_coercion_table(tait_candidates.iter().copied());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -1204,193 +1162,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
|||
self.table.interner()
|
||||
}
|
||||
|
||||
fn insert_inference_vars_for_impl_trait<T>(
|
||||
&mut self,
|
||||
t: T,
|
||||
mode: &mut ImplTraitReplacingMode<'db>,
|
||||
) -> T
|
||||
where
|
||||
T: TypeFoldable<DbInterner<'db>>,
|
||||
{
|
||||
fold_tys(self.interner(), t, |ty| {
|
||||
let ty = self.table.try_structurally_resolve_type(ty);
|
||||
let opaque_ty_id = match ty.kind() {
|
||||
TyKind::Alias(AliasTyKind::Opaque, alias_ty) => alias_ty.def_id.expect_opaque_ty(),
|
||||
_ => return ty,
|
||||
};
|
||||
let (impl_traits, idx) = match self.db.lookup_intern_impl_trait_id(opaque_ty_id) {
|
||||
// We don't replace opaque types from other kind with inference vars
|
||||
// because `insert_inference_vars_for_impl_traits` for each kinds
|
||||
// and unreplaced opaque types of other kind are resolved while
|
||||
// inferencing because of `tait_coercion_table`.
|
||||
ImplTraitId::ReturnTypeImplTrait(def, idx) => {
|
||||
if matches!(mode, ImplTraitReplacingMode::TypeAlias) {
|
||||
// RPITs don't have `tait_coercion_table`, so use inserted inference
|
||||
// vars for them.
|
||||
if let Some(ty) = self.result.type_of_rpit.get(idx) {
|
||||
return *ty;
|
||||
}
|
||||
return ty;
|
||||
}
|
||||
(self.db.return_type_impl_traits(def), idx)
|
||||
}
|
||||
ImplTraitId::TypeAliasImplTrait(def, idx) => {
|
||||
if let ImplTraitReplacingMode::ReturnPosition(taits) = mode {
|
||||
// Gather TAITs while replacing RPITs because TAITs inside RPITs
|
||||
// may not visited while replacing TAITs
|
||||
taits.insert(ty);
|
||||
return ty;
|
||||
}
|
||||
(self.db.type_alias_impl_traits(def), idx)
|
||||
}
|
||||
};
|
||||
let Some(impl_traits) = impl_traits else {
|
||||
return ty;
|
||||
};
|
||||
let bounds =
|
||||
(*impl_traits).as_ref().map_bound(|its| its.impl_traits[idx].predicates.as_slice());
|
||||
let var = match self.result.type_of_rpit.entry(idx) {
|
||||
Entry::Occupied(entry) => return *entry.get(),
|
||||
Entry::Vacant(entry) => *entry.insert(self.table.next_ty_var()),
|
||||
};
|
||||
for clause in bounds.iter_identity_copied() {
|
||||
let clause = self.insert_inference_vars_for_impl_trait(clause, mode);
|
||||
self.table.register_predicate(Obligation::new(
|
||||
self.interner(),
|
||||
ObligationCause::new(),
|
||||
self.table.trait_env.env,
|
||||
clause,
|
||||
));
|
||||
}
|
||||
var
|
||||
})
|
||||
}
|
||||
|
||||
/// The coercion of a non-inference var into an opaque type should fail,
|
||||
/// but not in the defining sites of the TAITs.
|
||||
/// In such cases, we insert an proxy inference var for each TAIT,
|
||||
/// and coerce into it instead of TAIT itself.
|
||||
///
|
||||
/// The inference var stretagy is effective because;
|
||||
///
|
||||
/// - It can still unify types that coerced into TAITs
|
||||
/// - We are pushing `impl Trait` bounds into it
|
||||
///
|
||||
/// This function inserts a map that maps the opaque type to that proxy inference var.
|
||||
fn make_tait_coercion_table(&mut self, tait_candidates: impl Iterator<Item = Ty<'db>>) {
|
||||
struct TypeAliasImplTraitCollector<'a, 'db> {
|
||||
db: &'a dyn HirDatabase,
|
||||
table: &'a mut InferenceTable<'db>,
|
||||
assocs: FxHashMap<InternedOpaqueTyId, (ImplId, Ty<'db>)>,
|
||||
non_assocs: FxHashMap<InternedOpaqueTyId, Ty<'db>>,
|
||||
}
|
||||
|
||||
impl<'db> TypeVisitor<DbInterner<'db>> for TypeAliasImplTraitCollector<'_, 'db> {
|
||||
type Result = ();
|
||||
|
||||
fn visit_ty(&mut self, ty: Ty<'db>) {
|
||||
let ty = self.table.try_structurally_resolve_type(ty);
|
||||
|
||||
if let TyKind::Alias(AliasTyKind::Opaque, alias_ty) = ty.kind()
|
||||
&& let id = alias_ty.def_id.expect_opaque_ty()
|
||||
&& let ImplTraitId::TypeAliasImplTrait(alias_id, _) =
|
||||
self.db.lookup_intern_impl_trait_id(id)
|
||||
{
|
||||
let loc = self.db.lookup_intern_type_alias(alias_id);
|
||||
match loc.container {
|
||||
ItemContainerId::ImplId(impl_id) => {
|
||||
self.assocs.insert(id, (impl_id, ty));
|
||||
}
|
||||
ItemContainerId::ModuleId(..) | ItemContainerId::ExternBlockId(..) => {
|
||||
self.non_assocs.insert(id, ty);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
ty.super_visit_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
let mut collector = TypeAliasImplTraitCollector {
|
||||
db: self.db,
|
||||
table: &mut self.table,
|
||||
assocs: FxHashMap::default(),
|
||||
non_assocs: FxHashMap::default(),
|
||||
};
|
||||
for ty in tait_candidates {
|
||||
ty.visit_with(&mut collector);
|
||||
}
|
||||
|
||||
// Non-assoc TAITs can be define-used everywhere as long as they are
|
||||
// in function signatures or const types, etc
|
||||
let mut taits = collector.non_assocs;
|
||||
|
||||
// assoc TAITs(ATPITs) can be only define-used inside their impl block.
|
||||
// They cannot be define-used in inner items like in the following;
|
||||
//
|
||||
// ```
|
||||
// impl Trait for Struct {
|
||||
// type Assoc = impl Default;
|
||||
//
|
||||
// fn assoc_fn() -> Self::Assoc {
|
||||
// let foo: Self::Assoc = true; // Allowed here
|
||||
//
|
||||
// fn inner() -> Self::Assoc {
|
||||
// false // Not allowed here
|
||||
// }
|
||||
//
|
||||
// foo
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
let impl_id = match self.owner {
|
||||
DefWithBodyId::FunctionId(it) => {
|
||||
let loc = self.db.lookup_intern_function(it);
|
||||
if let ItemContainerId::ImplId(impl_id) = loc.container {
|
||||
Some(impl_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
DefWithBodyId::ConstId(it) => {
|
||||
let loc = self.db.lookup_intern_const(it);
|
||||
if let ItemContainerId::ImplId(impl_id) = loc.container {
|
||||
Some(impl_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(impl_id) = impl_id {
|
||||
taits.extend(collector.assocs.into_iter().filter_map(|(id, (impl_, ty))| {
|
||||
if impl_ == impl_id { Some((id, ty)) } else { None }
|
||||
}));
|
||||
}
|
||||
|
||||
let tait_coercion_table: FxHashMap<_, _> = taits
|
||||
.into_iter()
|
||||
.filter_map(|(id, ty)| {
|
||||
if let ImplTraitId::TypeAliasImplTrait(..) = self.db.lookup_intern_impl_trait_id(id)
|
||||
{
|
||||
let ty = self.insert_inference_vars_for_impl_trait(
|
||||
ty,
|
||||
&mut ImplTraitReplacingMode::TypeAlias,
|
||||
);
|
||||
Some((id, ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !tait_coercion_table.is_empty() {
|
||||
self.table.tait_coercion_table = Some(tait_coercion_table);
|
||||
}
|
||||
}
|
||||
|
||||
fn infer_body(&mut self) {
|
||||
match self.return_coercion {
|
||||
Some(_) => self.infer_return(self.body.body_expr),
|
||||
|
|
@ -2006,12 +1777,15 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
|
|||
Some(struct_.into())
|
||||
}
|
||||
|
||||
fn get_traits_in_scope(&self) -> Either<FxHashSet<TraitId>, &FxHashSet<TraitId>> {
|
||||
let mut b_traits = self.resolver.traits_in_scope_from_block_scopes().peekable();
|
||||
fn get_traits_in_scope<'a>(
|
||||
resolver: &Resolver<'db>,
|
||||
traits_in_scope: &'a FxHashSet<TraitId>,
|
||||
) -> Either<FxHashSet<TraitId>, &'a FxHashSet<TraitId>> {
|
||||
let mut b_traits = resolver.traits_in_scope_from_block_scopes().peekable();
|
||||
if b_traits.peek().is_some() {
|
||||
Either::Left(self.traits_in_scope.iter().copied().chain(b_traits).collect())
|
||||
Either::Left(traits_in_scope.iter().copied().chain(b_traits).collect())
|
||||
} else {
|
||||
Either::Right(&self.traits_in_scope)
|
||||
Either::Right(traits_in_scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,8 +60,7 @@ use crate::{
|
|||
next_solver::{
|
||||
Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, CallableIdWrapper,
|
||||
Canonical, ClauseKind, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed,
|
||||
GenericArgs, PolyFnSig, PredicateKind, Region, RegionKind, SolverDefId, TraitRef, Ty,
|
||||
TyKind,
|
||||
GenericArgs, PolyFnSig, PredicateKind, Region, RegionKind, TraitRef, Ty, TyKind,
|
||||
infer::{
|
||||
InferCtxt, InferOk, InferResult,
|
||||
relate::RelateResult,
|
||||
|
|
@ -223,24 +222,6 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> {
|
|||
}
|
||||
}
|
||||
|
||||
// If we are coercing into a TAIT, coerce into its proxy inference var, instead.
|
||||
// FIXME(next-solver): This should not be here. This is not how rustc does thing, and it also not allows us
|
||||
// to normalize opaques defined in our scopes. Instead, we should properly register
|
||||
// `TypingMode::Analysis::defining_opaque_types_and_generators`, and rely on the solver to reveal
|
||||
// them for us (we'll also need some global-like registry for the values, something we cannot
|
||||
// really implement, therefore we can really support only RPITs and ITIAT or the new `#[define_opaque]`
|
||||
// TAIT, not the old global TAIT).
|
||||
let mut b = b;
|
||||
if let Some(tait_table) = &self.table.tait_coercion_table
|
||||
&& let TyKind::Alias(rustc_type_ir::Opaque, opaque_ty) = b.kind()
|
||||
&& let SolverDefId::InternedOpaqueTyId(opaque_ty_id) = opaque_ty.def_id
|
||||
&& !matches!(a.kind(), TyKind::Infer(..) | TyKind::Alias(rustc_type_ir::Opaque, _))
|
||||
&& let Some(ty) = tait_table.get(&opaque_ty_id)
|
||||
{
|
||||
b = self.table.shallow_resolve(*ty);
|
||||
}
|
||||
let b = b;
|
||||
|
||||
// Coercing *from* an unresolved inference variable means that
|
||||
// we have no information about the source type. This will always
|
||||
// ultimately fall back to some form of subtyping.
|
||||
|
|
@ -1528,7 +1509,7 @@ fn coerce<'db>(
|
|||
env: Arc<TraitEnvironment<'db>>,
|
||||
tys: &Canonical<'db, (Ty<'db>, Ty<'db>)>,
|
||||
) -> Result<(Vec<Adjustment<'db>>, Ty<'db>), TypeError<DbInterner<'db>>> {
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let mut table = InferenceTable::new(db, env, None);
|
||||
let interner = table.interner();
|
||||
let ((ty1_with_vars, ty2_with_vars), vars) = table.infer_ctxt.instantiate_canonical(tys);
|
||||
|
||||
|
|
|
|||
|
|
@ -1458,10 +1458,11 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
) -> Ty<'db> {
|
||||
let coerce_ty = expected.coercion_target_type(&mut self.table);
|
||||
let g = self.resolver.update_to_inner_scope(self.db, self.owner, expr);
|
||||
let prev_env = block_id.map(|block_id| {
|
||||
let prev_state = block_id.map(|block_id| {
|
||||
let prev_env = self.table.trait_env.clone();
|
||||
TraitEnvironment::with_block(&mut self.table.trait_env, block_id);
|
||||
prev_env
|
||||
let prev_block = self.table.infer_ctxt.interner.block.replace(block_id);
|
||||
(prev_env, prev_block)
|
||||
});
|
||||
|
||||
let (break_ty, ty) =
|
||||
|
|
@ -1576,8 +1577,9 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
}
|
||||
});
|
||||
self.resolver.reset_to_guard(g);
|
||||
if let Some(prev_env) = prev_env {
|
||||
if let Some((prev_env, prev_block)) = prev_state {
|
||||
self.table.trait_env = prev_env;
|
||||
self.table.infer_ctxt.interner.block = prev_block;
|
||||
}
|
||||
|
||||
break_ty.unwrap_or(ty)
|
||||
|
|
@ -1689,10 +1691,11 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
// work out while people are typing
|
||||
let canonicalized_receiver = self.canonicalize(receiver_ty);
|
||||
let resolved = method_resolution::lookup_method(
|
||||
self.db,
|
||||
&canonicalized_receiver,
|
||||
self.table.trait_env.clone(),
|
||||
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
|
||||
&mut self.table,
|
||||
Self::get_traits_in_scope(&self.resolver, &self.traits_in_scope)
|
||||
.as_ref()
|
||||
.left_or_else(|&it| it),
|
||||
VisibleFromModule::Filter(self.resolver.module()),
|
||||
name,
|
||||
);
|
||||
|
|
@ -1844,10 +1847,11 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
let canonicalized_receiver = self.canonicalize(receiver_ty);
|
||||
|
||||
let resolved = method_resolution::lookup_method(
|
||||
self.db,
|
||||
&canonicalized_receiver,
|
||||
self.table.trait_env.clone(),
|
||||
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
|
||||
&mut self.table,
|
||||
Self::get_traits_in_scope(&self.resolver, &self.traits_in_scope)
|
||||
.as_ref()
|
||||
.left_or_else(|&it| it),
|
||||
VisibleFromModule::Filter(self.resolver.module()),
|
||||
method_name,
|
||||
);
|
||||
|
|
@ -1892,9 +1896,10 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
|
||||
let assoc_func_with_same_name = method_resolution::iterate_method_candidates(
|
||||
&canonicalized_receiver,
|
||||
self.db,
|
||||
self.table.trait_env.clone(),
|
||||
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
|
||||
&mut self.table,
|
||||
Self::get_traits_in_scope(&self.resolver, &self.traits_in_scope)
|
||||
.as_ref()
|
||||
.left_or_else(|&it| it),
|
||||
VisibleFromModule::Filter(self.resolver.module()),
|
||||
Some(method_name),
|
||||
method_resolution::LookupMode::Path,
|
||||
|
|
|
|||
147
crates/hir-ty/src/infer/opaques.rs
Normal file
147
crates/hir-ty/src/infer/opaques.rs
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
//! Defining opaque types via inference.
|
||||
|
||||
use rustc_type_ir::{TypeVisitableExt, fold_regions};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use crate::{
|
||||
infer::InferenceContext,
|
||||
next_solver::{
|
||||
EarlyBinder, OpaqueTypeKey, SolverDefId, TypingMode,
|
||||
infer::{opaque_types::OpaqueHiddenType, traits::ObligationCause},
|
||||
},
|
||||
};
|
||||
|
||||
impl<'db> InferenceContext<'_, 'db> {
|
||||
/// This takes all the opaque type uses during HIR typeck. It first computes
|
||||
/// the concrete hidden type by iterating over all defining uses.
|
||||
///
|
||||
/// A use during HIR typeck is defining if all non-lifetime arguments are
|
||||
/// unique generic parameters and the hidden type does not reference any
|
||||
/// inference variables.
|
||||
///
|
||||
/// It then uses these defining uses to guide inference for all other uses.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(super) fn handle_opaque_type_uses(&mut self) {
|
||||
// We clone the opaques instead of stealing them here as they are still used for
|
||||
// normalization in the next generation trait solver.
|
||||
let opaque_types: Vec<_> = self.table.infer_ctxt.clone_opaque_types();
|
||||
|
||||
self.compute_definition_site_hidden_types(opaque_types);
|
||||
}
|
||||
}
|
||||
|
||||
#[expect(unused, reason = "rustc has this")]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum UsageKind<'db> {
|
||||
None,
|
||||
NonDefiningUse(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>),
|
||||
UnconstrainedHiddenType(OpaqueHiddenType<'db>),
|
||||
HasDefiningUse(OpaqueHiddenType<'db>),
|
||||
}
|
||||
|
||||
impl<'db> UsageKind<'db> {
|
||||
fn merge(&mut self, other: UsageKind<'db>) {
|
||||
match (&*self, &other) {
|
||||
(UsageKind::HasDefiningUse(_), _) | (_, UsageKind::None) => unreachable!(),
|
||||
(UsageKind::None, _) => *self = other,
|
||||
// When mergining non-defining uses, prefer earlier ones. This means
|
||||
// the error happens as early as possible.
|
||||
(
|
||||
UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..),
|
||||
UsageKind::NonDefiningUse(..),
|
||||
) => {}
|
||||
// When merging unconstrained hidden types, we prefer later ones. This is
|
||||
// used as in most cases, the defining use is the final return statement
|
||||
// of our function, and other uses with defining arguments are likely not
|
||||
// intended to be defining.
|
||||
(
|
||||
UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..),
|
||||
UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse(_),
|
||||
) => *self = other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> InferenceContext<'_, 'db> {
|
||||
fn compute_definition_site_hidden_types(
|
||||
&mut self,
|
||||
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);
|
||||
}
|
||||
debug!(?opaque_types);
|
||||
|
||||
let interner = self.interner();
|
||||
let TypingMode::Analysis { defining_opaque_types_and_generators } =
|
||||
self.table.infer_ctxt.typing_mode()
|
||||
else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
for def_id in defining_opaque_types_and_generators {
|
||||
let def_id = match def_id {
|
||||
SolverDefId::InternedOpaqueTyId(it) => it,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
// We do actually need to check this the second pass (we can't just
|
||||
// store this), because we can go from `UnconstrainedHiddenType` to
|
||||
// `HasDefiningUse` (because of fallback)
|
||||
let mut usage_kind = UsageKind::None;
|
||||
for &(opaque_type_key, hidden_type) in &opaque_types {
|
||||
if opaque_type_key.def_id != def_id.into() {
|
||||
continue;
|
||||
}
|
||||
|
||||
usage_kind.merge(self.consider_opaque_type_use(opaque_type_key, hidden_type));
|
||||
|
||||
if let UsageKind::HasDefiningUse(..) = usage_kind {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let UsageKind::HasDefiningUse(ty) = usage_kind {
|
||||
for &(opaque_type_key, hidden_type) in &opaque_types {
|
||||
if opaque_type_key.def_id != def_id.into() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let expected =
|
||||
EarlyBinder::bind(ty.ty).instantiate(interner, opaque_type_key.args);
|
||||
self.demand_eqtype(expected, hidden_type.ty);
|
||||
}
|
||||
|
||||
self.result.type_of_opaque.insert(def_id, ty.ty);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
self.result.type_of_opaque.insert(def_id, self.types.error);
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self), ret)]
|
||||
fn consider_opaque_type_use(
|
||||
&self,
|
||||
opaque_type_key: OpaqueTypeKey<'db>,
|
||||
hidden_type: OpaqueHiddenType<'db>,
|
||||
) -> UsageKind<'db> {
|
||||
// We ignore uses of the opaque if they have any inference variables
|
||||
// as this can frequently happen with recursive calls.
|
||||
//
|
||||
// See `tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs`.
|
||||
if hidden_type.ty.has_non_region_infer() {
|
||||
return UsageKind::UnconstrainedHiddenType(hidden_type);
|
||||
}
|
||||
|
||||
let cause = ObligationCause::new();
|
||||
let at = self.table.infer_ctxt.at(&cause, self.table.trait_env.env);
|
||||
let hidden_type = match at.deeply_normalize(hidden_type) {
|
||||
Ok(hidden_type) => hidden_type,
|
||||
Err(_errors) => OpaqueHiddenType { ty: self.types.error },
|
||||
};
|
||||
let hidden_type = fold_regions(self.interner(), hidden_type, |_, _| self.types.re_erased);
|
||||
UsageKind::HasDefiningUse(hidden_type)
|
||||
}
|
||||
}
|
||||
|
|
@ -310,9 +310,10 @@ impl<'db> InferenceContext<'_, 'db> {
|
|||
let mut not_visible = None;
|
||||
let res = method_resolution::iterate_method_candidates(
|
||||
&canonical_ty,
|
||||
self.db,
|
||||
self.table.trait_env.clone(),
|
||||
self.get_traits_in_scope().as_ref().left_or_else(|&it| it),
|
||||
&mut self.table,
|
||||
Self::get_traits_in_scope(&self.resolver, &self.traits_in_scope)
|
||||
.as_ref()
|
||||
.left_or_else(|&it| it),
|
||||
VisibleFromModule::Filter(self.resolver.module()),
|
||||
Some(name),
|
||||
method_resolution::LookupMode::Path,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
use std::fmt;
|
||||
|
||||
use hir_def::{AdtId, GenericParamId, lang_item::LangItem};
|
||||
use hir_def::{AdtId, DefWithBodyId, GenericParamId, lang_item::LangItem};
|
||||
use hir_expand::name::Name;
|
||||
use intern::sym;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use rustc_hash::FxHashSet;
|
||||
use rustc_type_ir::{
|
||||
DebruijnIndex, InferConst, InferTy, RegionVid, TyVid, TypeFoldable, TypeFolder,
|
||||
TypeSuperFoldable, TypeVisitableExt, UpcastFrom,
|
||||
|
|
@ -17,12 +17,12 @@ use triomphe::Arc;
|
|||
|
||||
use crate::{
|
||||
TraitEnvironment,
|
||||
db::{HirDatabase, InternedOpaqueTyId},
|
||||
db::HirDatabase,
|
||||
infer::InferenceContext,
|
||||
next_solver::{
|
||||
self, AliasTy, Binder, Canonical, ClauseKind, Const, ConstKind, DbInterner,
|
||||
ErrorGuaranteed, GenericArg, GenericArgs, Predicate, PredicateKind, Region, RegionKind,
|
||||
SolverDefId, SolverDefIds, TraitRef, Ty, TyKind, TypingMode,
|
||||
SolverDefId, TraitRef, Ty, TyKind, TypingMode,
|
||||
fulfill::{FulfillmentCtxt, NextSolverError},
|
||||
infer::{
|
||||
DbInternerInferExt, InferCtxt, InferOk, InferResult,
|
||||
|
|
@ -139,10 +139,7 @@ fn could_unify_impl<'db>(
|
|||
select: for<'a> fn(&mut ObligationCtxt<'a, 'db>) -> Vec<NextSolverError<'db>>,
|
||||
) -> bool {
|
||||
let interner = DbInterner::new_with(db, Some(env.krate), env.block);
|
||||
// FIXME(next-solver): I believe this should use `PostAnalysis` (this is only used for IDE things),
|
||||
// but this causes some bug because of our incorrect impl of `type_of_opaque_hir_typeck()` for TAIT
|
||||
// and async blocks.
|
||||
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
|
||||
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
|
||||
let cause = ObligationCause::dummy();
|
||||
let at = infcx.at(&cause, env.env);
|
||||
let ((ty1_with_vars, ty2_with_vars), _) = infcx.instantiate_canonical(tys);
|
||||
|
|
@ -158,7 +155,6 @@ fn could_unify_impl<'db>(
|
|||
pub(crate) struct InferenceTable<'db> {
|
||||
pub(crate) db: &'db dyn HirDatabase,
|
||||
pub(crate) trait_env: Arc<TraitEnvironment<'db>>,
|
||||
pub(crate) tait_coercion_table: Option<FxHashMap<InternedOpaqueTyId, Ty<'db>>>,
|
||||
pub(crate) infer_ctxt: InferCtxt<'db>,
|
||||
pub(super) fulfillment_cx: FulfillmentCtxt<'db>,
|
||||
pub(super) diverging_type_vars: FxHashSet<Ty<'db>>,
|
||||
|
|
@ -170,15 +166,23 @@ pub(crate) struct InferenceTableSnapshot<'db> {
|
|||
}
|
||||
|
||||
impl<'db> InferenceTable<'db> {
|
||||
pub(crate) fn new(db: &'db dyn HirDatabase, trait_env: Arc<TraitEnvironment<'db>>) -> Self {
|
||||
/// Inside hir-ty you should use this for inference only, and always pass `owner`.
|
||||
/// Outside it, always pass `owner = None`.
|
||||
pub(crate) fn new(
|
||||
db: &'db dyn HirDatabase,
|
||||
trait_env: Arc<TraitEnvironment<'db>>,
|
||||
owner: Option<DefWithBodyId>,
|
||||
) -> Self {
|
||||
let interner = DbInterner::new_with(db, Some(trait_env.krate), trait_env.block);
|
||||
let infer_ctxt = interner.infer_ctxt().build(rustc_type_ir::TypingMode::Analysis {
|
||||
defining_opaque_types_and_generators: SolverDefIds::new_from_iter(interner, []),
|
||||
});
|
||||
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 infer_ctxt = interner.infer_ctxt().build(typing_mode);
|
||||
InferenceTable {
|
||||
db,
|
||||
trait_env,
|
||||
tait_coercion_table: None,
|
||||
fulfillment_cx: FulfillmentCtxt::new(&infer_ctxt),
|
||||
infer_ctxt,
|
||||
diverging_type_vars: FxHashSet::default(),
|
||||
|
|
@ -698,40 +702,7 @@ impl<'db> InferenceTable<'db> {
|
|||
where
|
||||
T: TypeFoldable<DbInterner<'db>>,
|
||||
{
|
||||
struct Folder<'a, 'db> {
|
||||
table: &'a mut InferenceTable<'db>,
|
||||
}
|
||||
impl<'db> TypeFolder<DbInterner<'db>> for Folder<'_, 'db> {
|
||||
fn cx(&self) -> DbInterner<'db> {
|
||||
self.table.interner()
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
|
||||
if !ty.references_error() {
|
||||
return ty;
|
||||
}
|
||||
|
||||
if ty.is_ty_error() { self.table.next_ty_var() } else { ty.super_fold_with(self) }
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
|
||||
if !ct.references_error() {
|
||||
return ct;
|
||||
}
|
||||
|
||||
if ct.is_ct_error() {
|
||||
self.table.next_const_var()
|
||||
} else {
|
||||
ct.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
|
||||
if r.is_error() { self.table.next_region_var() } else { r }
|
||||
}
|
||||
}
|
||||
|
||||
ty.fold_with(&mut Folder { table: self })
|
||||
self.infer_ctxt.insert_type_vars(ty)
|
||||
}
|
||||
|
||||
/// Replaces `Ty::Error` by a new type var, so we can maybe still infer it.
|
||||
|
|
|
|||
|
|
@ -27,9 +27,11 @@ mod infer;
|
|||
mod inhabitedness;
|
||||
mod lower;
|
||||
pub mod next_solver;
|
||||
mod opaques;
|
||||
mod specialization;
|
||||
mod target_feature;
|
||||
mod utils;
|
||||
mod variance;
|
||||
|
||||
pub mod autoderef;
|
||||
pub mod consteval;
|
||||
|
|
@ -50,7 +52,6 @@ pub mod traits;
|
|||
mod test_db;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod variance;
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
|
|
@ -471,6 +472,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// To be used from `hir` only.
|
||||
pub fn callable_sig_from_fn_trait<'db>(
|
||||
self_ty: Ty<'db>,
|
||||
trait_env: Arc<TraitEnvironment<'db>>,
|
||||
|
|
@ -482,7 +484,7 @@ pub fn callable_sig_from_fn_trait<'db>(
|
|||
.trait_items(db)
|
||||
.associated_type_by_name(&Name::new_symbol_root(sym::Output))?;
|
||||
|
||||
let mut table = InferenceTable::new(db, trait_env.clone());
|
||||
let mut table = InferenceTable::new(db, trait_env.clone(), None);
|
||||
|
||||
// Register two obligations:
|
||||
// - Self: FnOnce<?args_ty>
|
||||
|
|
|
|||
|
|
@ -489,9 +489,8 @@ pub fn def_crates<'db>(
|
|||
|
||||
/// Look up the method with the given name.
|
||||
pub(crate) fn lookup_method<'db>(
|
||||
db: &'db dyn HirDatabase,
|
||||
ty: &Canonical<'db, Ty<'db>>,
|
||||
env: Arc<TraitEnvironment<'db>>,
|
||||
table: &mut InferenceTable<'db>,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: &Name,
|
||||
|
|
@ -499,8 +498,7 @@ pub(crate) fn lookup_method<'db>(
|
|||
let mut not_visible = None;
|
||||
let res = iterate_method_candidates(
|
||||
ty,
|
||||
db,
|
||||
env,
|
||||
table,
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
Some(name),
|
||||
|
|
@ -656,8 +654,7 @@ impl ReceiverAdjustments {
|
|||
// FIXME add a context type here?
|
||||
pub(crate) fn iterate_method_candidates<'db, T>(
|
||||
ty: &Canonical<'db, Ty<'db>>,
|
||||
db: &'db dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment<'db>>,
|
||||
table: &mut InferenceTable<'db>,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: Option<&Name>,
|
||||
|
|
@ -665,10 +662,9 @@ pub(crate) fn iterate_method_candidates<'db, T>(
|
|||
mut callback: impl FnMut(ReceiverAdjustments, AssocItemId, bool) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
let mut slot = None;
|
||||
_ = iterate_method_candidates_dyn(
|
||||
_ = iterate_method_candidates_dyn_impl(
|
||||
ty,
|
||||
db,
|
||||
env,
|
||||
table,
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
|
|
@ -985,6 +981,7 @@ pub fn check_orphan_rules<'db>(db: &'db dyn HirDatabase, impl_: ImplId) -> bool
|
|||
is_not_orphan
|
||||
}
|
||||
|
||||
/// To be used from `hir` only.
|
||||
pub fn iterate_path_candidates<'db>(
|
||||
ty: &Canonical<'db, Ty<'db>>,
|
||||
db: &'db dyn HirDatabase,
|
||||
|
|
@ -1007,6 +1004,7 @@ pub fn iterate_path_candidates<'db>(
|
|||
)
|
||||
}
|
||||
|
||||
/// To be used from `hir` only.
|
||||
pub fn iterate_method_candidates_dyn<'db>(
|
||||
ty: &Canonical<'db, Ty<'db>>,
|
||||
db: &'db dyn HirDatabase,
|
||||
|
|
@ -1016,6 +1014,26 @@ pub fn iterate_method_candidates_dyn<'db>(
|
|||
name: Option<&Name>,
|
||||
mode: LookupMode,
|
||||
callback: &mut dyn MethodCandidateCallback,
|
||||
) -> ControlFlow<()> {
|
||||
iterate_method_candidates_dyn_impl(
|
||||
ty,
|
||||
&mut InferenceTable::new(db, env, None),
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
mode,
|
||||
callback,
|
||||
)
|
||||
}
|
||||
|
||||
fn iterate_method_candidates_dyn_impl<'db>(
|
||||
ty: &Canonical<'db, Ty<'db>>,
|
||||
table: &mut InferenceTable<'db>,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: Option<&Name>,
|
||||
mode: LookupMode,
|
||||
callback: &mut dyn MethodCandidateCallback,
|
||||
) -> ControlFlow<()> {
|
||||
let _p = tracing::info_span!(
|
||||
"iterate_method_candidates_dyn",
|
||||
|
|
@ -1046,28 +1064,28 @@ pub fn iterate_method_candidates_dyn<'db>(
|
|||
// the methods by autoderef order of *receiver types*, not *self
|
||||
// types*.
|
||||
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let ty = table.instantiate_canonical(*ty);
|
||||
let deref_chain = autoderef_method_receiver(&mut table, ty);
|
||||
table.run_in_snapshot(|table| {
|
||||
let ty = table.instantiate_canonical(*ty);
|
||||
let deref_chain = autoderef_method_receiver(table, ty);
|
||||
|
||||
deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| {
|
||||
iterate_method_candidates_with_autoref(
|
||||
&mut table,
|
||||
receiver_ty,
|
||||
adj,
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
callback,
|
||||
)
|
||||
deref_chain.into_iter().try_for_each(|(receiver_ty, adj)| {
|
||||
iterate_method_candidates_with_autoref(
|
||||
table,
|
||||
receiver_ty,
|
||||
adj,
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
callback,
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
LookupMode::Path => {
|
||||
// No autoderef for path lookups
|
||||
iterate_method_candidates_for_self_ty(
|
||||
ty,
|
||||
db,
|
||||
env,
|
||||
table,
|
||||
traits_in_scope,
|
||||
visible_from_module,
|
||||
name,
|
||||
|
|
@ -1250,39 +1268,39 @@ fn iterate_method_candidates_by_receiver<'db>(
|
|||
#[tracing::instrument(skip_all, fields(name = ?name))]
|
||||
fn iterate_method_candidates_for_self_ty<'db>(
|
||||
self_ty: &Canonical<'db, Ty<'db>>,
|
||||
db: &'db dyn HirDatabase,
|
||||
env: Arc<TraitEnvironment<'db>>,
|
||||
table: &mut InferenceTable<'db>,
|
||||
traits_in_scope: &FxHashSet<TraitId>,
|
||||
visible_from_module: VisibleFromModule,
|
||||
name: Option<&Name>,
|
||||
callback: &mut dyn MethodCandidateCallback,
|
||||
) -> ControlFlow<()> {
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let self_ty = table.instantiate_canonical(*self_ty);
|
||||
iterate_inherent_methods(
|
||||
self_ty,
|
||||
&mut table,
|
||||
name,
|
||||
None,
|
||||
None,
|
||||
visible_from_module,
|
||||
LookupMode::Path,
|
||||
&mut |adjustments, item, is_visible| {
|
||||
callback.on_inherent_method(adjustments, item, is_visible)
|
||||
},
|
||||
)?;
|
||||
iterate_trait_method_candidates(
|
||||
self_ty,
|
||||
&mut table,
|
||||
traits_in_scope,
|
||||
name,
|
||||
None,
|
||||
None,
|
||||
LookupMode::Path,
|
||||
&mut |adjustments, item, is_visible| {
|
||||
callback.on_trait_method(adjustments, item, is_visible)
|
||||
},
|
||||
)
|
||||
table.run_in_snapshot(|table| {
|
||||
let self_ty = table.instantiate_canonical(*self_ty);
|
||||
iterate_inherent_methods(
|
||||
self_ty,
|
||||
table,
|
||||
name,
|
||||
None,
|
||||
None,
|
||||
visible_from_module,
|
||||
LookupMode::Path,
|
||||
&mut |adjustments, item, is_visible| {
|
||||
callback.on_inherent_method(adjustments, item, is_visible)
|
||||
},
|
||||
)?;
|
||||
iterate_trait_method_candidates(
|
||||
self_ty,
|
||||
table,
|
||||
traits_in_scope,
|
||||
name,
|
||||
None,
|
||||
None,
|
||||
LookupMode::Path,
|
||||
&mut |adjustments, item, is_visible| {
|
||||
callback.on_trait_method(adjustments, item, is_visible)
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all, fields(name = ?name, visible_from_module, receiver_ty))]
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use crate::{
|
|||
display::DisplayTarget,
|
||||
mir::OperandKind,
|
||||
next_solver::{
|
||||
DbInterner, GenericArgs, SolverDefIds, Ty, TypingMode,
|
||||
DbInterner, GenericArgs, Ty, TypingMode,
|
||||
infer::{DbInternerInferExt, InferCtxt},
|
||||
},
|
||||
};
|
||||
|
|
@ -100,11 +100,11 @@ pub fn borrowck_query<'db>(
|
|||
let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block());
|
||||
let env = db.trait_environment_for_body(def);
|
||||
let mut res = vec![];
|
||||
// This calculates opaques defining scope which is a bit costly therefore is put outside `all_mir_bodies()`.
|
||||
let typing_mode = TypingMode::borrowck(interner, def.into());
|
||||
all_mir_bodies(db, def, |body| {
|
||||
// FIXME(next-solver): Opaques.
|
||||
let infcx = interner.infer_ctxt().build(TypingMode::Borrowck {
|
||||
defining_opaque_types: SolverDefIds::new_from_iter(interner, []),
|
||||
});
|
||||
let infcx = interner.infer_ctxt().build(typing_mode);
|
||||
res.push(BorrowckResult {
|
||||
mutability_of_locals: mutability_of_locals(&infcx, &body),
|
||||
moved_out_of_ref: moved_out_of_ref(&infcx, &env, &body),
|
||||
|
|
|
|||
|
|
@ -154,6 +154,29 @@ impl From<DefWithBodyId> for SolverDefId {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<SolverDefId> for DefWithBodyId {
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(value: SolverDefId) -> Result<Self, Self::Error> {
|
||||
let id = match value {
|
||||
SolverDefId::ConstId(id) => id.into(),
|
||||
SolverDefId::FunctionId(id) => id.into(),
|
||||
SolverDefId::StaticId(id) => id.into(),
|
||||
SolverDefId::EnumVariantId(id) | SolverDefId::Ctor(Ctor::Enum(id)) => id.into(),
|
||||
SolverDefId::InternedOpaqueTyId(_)
|
||||
| SolverDefId::TraitId(_)
|
||||
| SolverDefId::TypeAliasId(_)
|
||||
| SolverDefId::ImplId(_)
|
||||
| SolverDefId::InternedClosureId(_)
|
||||
| SolverDefId::InternedCoroutineId(_)
|
||||
| SolverDefId::Ctor(Ctor::Struct(_))
|
||||
| SolverDefId::AdtId(_) => return Err(()),
|
||||
};
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<SolverDefId> for GenericDefId {
|
||||
type Error = ();
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,14 @@ impl<'db> GenericArg<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn expect_region(self) -> Region<'db> {
|
||||
match self {
|
||||
GenericArg::Lifetime(region) => region,
|
||||
_ => panic!("expected a region, got {self:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn error_from_id(interner: DbInterner<'db>, id: GenericParamId) -> GenericArg<'db> {
|
||||
match id {
|
||||
GenericParamId::TypeParamId(_) => Ty::new_error(interner, ErrorGuaranteed).into(),
|
||||
|
|
|
|||
|
|
@ -13,27 +13,27 @@ use opaque_types::{OpaqueHiddenType, OpaqueTypeStorage};
|
|||
use region_constraints::{RegionConstraintCollector, RegionConstraintStorage};
|
||||
use rustc_next_trait_solver::solve::SolverDelegateEvalExt;
|
||||
use rustc_pattern_analysis::Captures;
|
||||
use rustc_type_ir::TypeFoldable;
|
||||
use rustc_type_ir::error::{ExpectedFound, TypeError};
|
||||
use rustc_type_ir::inherent::{
|
||||
Const as _, GenericArg as _, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _,
|
||||
};
|
||||
use rustc_type_ir::{
|
||||
ClosureKind, ConstVid, FloatVarValue, FloatVid, GenericArgKind, InferConst, InferTy,
|
||||
IntVarValue, IntVid, OutlivesPredicate, RegionVid, TyVid, UniverseIndex,
|
||||
IntVarValue, IntVid, OutlivesPredicate, RegionVid, TermKind, TyVid, TypeFoldable, TypeFolder,
|
||||
TypeSuperFoldable, TypeVisitableExt, UniverseIndex,
|
||||
error::{ExpectedFound, TypeError},
|
||||
inherent::{
|
||||
Const as _, GenericArg as _, GenericArgs as _, IntoKind, SliceLike, Term as _, Ty as _,
|
||||
},
|
||||
};
|
||||
use rustc_type_ir::{TermKind, TypeVisitableExt};
|
||||
use snapshot::undo_log::InferCtxtUndoLogs;
|
||||
use tracing::{debug, instrument};
|
||||
use traits::{ObligationCause, PredicateObligations};
|
||||
use type_variable::TypeVariableOrigin;
|
||||
use unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey};
|
||||
|
||||
use crate::next_solver::fold::BoundVarReplacerDelegate;
|
||||
use crate::next_solver::infer::select::EvaluationResult;
|
||||
use crate::next_solver::infer::traits::PredicateObligation;
|
||||
use crate::next_solver::obligation_ctxt::ObligationCtxt;
|
||||
use crate::next_solver::{BoundConst, BoundRegion, BoundTy, BoundVarKind, Goal, SolverContext};
|
||||
use crate::next_solver::{
|
||||
BoundConst, BoundRegion, BoundTy, BoundVarKind, Goal, SolverContext,
|
||||
fold::BoundVarReplacerDelegate,
|
||||
infer::{select::EvaluationResult, traits::PredicateObligation},
|
||||
obligation_ctxt::ObligationCtxt,
|
||||
};
|
||||
|
||||
use super::{
|
||||
AliasTerm, Binder, CanonicalQueryInput, CanonicalVarValues, Const, ConstKind, DbInterner,
|
||||
|
|
@ -46,7 +46,7 @@ use super::{
|
|||
pub mod at;
|
||||
pub mod canonical;
|
||||
mod context;
|
||||
mod opaque_types;
|
||||
pub mod opaque_types;
|
||||
pub mod region_constraints;
|
||||
pub mod relate;
|
||||
pub mod resolve;
|
||||
|
|
@ -400,6 +400,46 @@ impl<'db> InferCtxt<'db> {
|
|||
))
|
||||
}
|
||||
|
||||
pub(crate) fn insert_type_vars<T>(&self, ty: T) -> T
|
||||
where
|
||||
T: TypeFoldable<DbInterner<'db>>,
|
||||
{
|
||||
struct Folder<'a, 'db> {
|
||||
infcx: &'a InferCtxt<'db>,
|
||||
}
|
||||
impl<'db> TypeFolder<DbInterner<'db>> for Folder<'_, 'db> {
|
||||
fn cx(&self) -> DbInterner<'db> {
|
||||
self.infcx.interner
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: Ty<'db>) -> Ty<'db> {
|
||||
if !ty.references_error() {
|
||||
return ty;
|
||||
}
|
||||
|
||||
if ty.is_ty_error() { self.infcx.next_ty_var() } else { ty.super_fold_with(self) }
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, ct: Const<'db>) -> Const<'db> {
|
||||
if !ct.references_error() {
|
||||
return ct;
|
||||
}
|
||||
|
||||
if ct.is_ct_error() {
|
||||
self.infcx.next_const_var()
|
||||
} else {
|
||||
ct.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_region(&mut self, r: Region<'db>) -> Region<'db> {
|
||||
if r.is_error() { self.infcx.next_region_var() } else { r }
|
||||
}
|
||||
}
|
||||
|
||||
ty.fold_with(&mut Folder { infcx: self })
|
||||
}
|
||||
|
||||
/// Evaluates whether the predicate can be satisfied in the given
|
||||
/// `ParamEnv`, and returns `false` if not certain. However, this is
|
||||
/// not entirely accurate if inference variables are involved.
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ pub(crate) mod table;
|
|||
|
||||
pub(crate) use table::{OpaqueTypeStorage, OpaqueTypeTable};
|
||||
|
||||
use macros::{TypeFoldable, TypeVisitable};
|
||||
|
||||
use crate::next_solver::{OpaqueTypeKey, Ty, infer::InferCtxt};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, TypeVisitable, TypeFoldable)]
|
||||
pub struct OpaqueHiddenType<'db> {
|
||||
pub ty: Ty<'db>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,14 +122,6 @@ impl<'db> OpaqueTypeStorage<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'db> Drop for OpaqueTypeStorage<'db> {
|
||||
fn drop(&mut self) {
|
||||
if !self.opaque_types.is_empty() {
|
||||
panic!("{:?}", self.opaque_types)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct OpaqueTypeTable<'a, 'db> {
|
||||
storage: &'a mut OpaqueTypeStorage<'db>,
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ pub use tls_db::{attach_db, attach_db_allow_change, with_attached_db};
|
|||
|
||||
use base_db::Crate;
|
||||
use hir_def::{
|
||||
AdtId, AttrDefId, BlockId, CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId,
|
||||
VariantId,
|
||||
AdtId, AttrDefId, BlockId, CallableDefId, DefWithBodyId, EnumVariantId, ItemContainerId,
|
||||
StructId, UnionId, VariantId,
|
||||
lang_item::LangItem,
|
||||
signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags},
|
||||
};
|
||||
|
|
@ -29,7 +29,7 @@ use rustc_type_ir::{
|
|||
|
||||
use crate::{
|
||||
FnAbi,
|
||||
db::{HirDatabase, InternedCoroutine},
|
||||
db::{HirDatabase, InternedCoroutine, InternedCoroutineId},
|
||||
method_resolution::{ALL_FLOAT_FPS, ALL_INT_FPS, TyFingerprint},
|
||||
next_solver::{
|
||||
AdtIdWrapper, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper,
|
||||
|
|
@ -96,7 +96,7 @@ macro_rules! _interned_vec_nolifetime_salsa {
|
|||
}
|
||||
};
|
||||
($name:ident, $ty:ty, nofold) => {
|
||||
#[salsa::interned(constructor = new_, debug)]
|
||||
#[salsa::interned(constructor = new_)]
|
||||
pub struct $name {
|
||||
#[returns(ref)]
|
||||
inner_: smallvec::SmallVec<[$ty; 2]>,
|
||||
|
|
@ -119,6 +119,12 @@ macro_rules! _interned_vec_nolifetime_salsa {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'db> std::fmt::Debug for $name<'db> {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.as_slice().fmt(fmt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'db> rustc_type_ir::inherent::SliceLike for $name<'db> {
|
||||
type Item = $ty;
|
||||
|
||||
|
|
@ -1866,9 +1872,42 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
Binder::bind_with_vars(inner, bound_vars)
|
||||
}
|
||||
|
||||
fn opaque_types_defined_by(self, _defining_anchor: Self::LocalDefId) -> Self::LocalDefIds {
|
||||
// FIXME(next-solver)
|
||||
SolverDefIds::new_from_iter(self, [])
|
||||
fn opaque_types_defined_by(self, def_id: Self::LocalDefId) -> Self::LocalDefIds {
|
||||
let Ok(def_id) = DefWithBodyId::try_from(def_id) else {
|
||||
return SolverDefIds::default();
|
||||
};
|
||||
let mut result = Vec::new();
|
||||
crate::opaques::opaque_types_defined_by(self.db, def_id, &mut result);
|
||||
SolverDefIds::new_from_iter(self, result)
|
||||
}
|
||||
|
||||
fn opaque_types_and_coroutines_defined_by(self, def_id: Self::LocalDefId) -> Self::LocalDefIds {
|
||||
let Ok(def_id) = DefWithBodyId::try_from(def_id) else {
|
||||
return SolverDefIds::default();
|
||||
};
|
||||
let mut result = Vec::new();
|
||||
|
||||
crate::opaques::opaque_types_defined_by(self.db, def_id, &mut result);
|
||||
|
||||
// Collect coroutines.
|
||||
let body = self.db.body(def_id);
|
||||
body.exprs().for_each(|(expr_id, expr)| {
|
||||
if matches!(
|
||||
expr,
|
||||
hir_def::hir::Expr::Async { .. }
|
||||
| hir_def::hir::Expr::Closure {
|
||||
closure_kind: hir_def::hir::ClosureKind::Async
|
||||
| hir_def::hir::ClosureKind::Coroutine(_),
|
||||
..
|
||||
}
|
||||
) {
|
||||
let coroutine =
|
||||
InternedCoroutineId::new(self.db, InternedCoroutine(def_id, expr_id));
|
||||
result.push(coroutine.into());
|
||||
}
|
||||
});
|
||||
|
||||
SolverDefIds::new_from_iter(self, result)
|
||||
}
|
||||
|
||||
fn alias_has_const_conditions(self, _def_id: Self::DefId) -> bool {
|
||||
|
|
@ -1913,12 +1952,10 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
let impl_trait_id = self.db().lookup_intern_impl_trait_id(opaque);
|
||||
match impl_trait_id {
|
||||
crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => {
|
||||
let infer = self.db().infer(func.into());
|
||||
EarlyBinder::bind(infer.type_of_rpit[idx])
|
||||
crate::opaques::rpit_hidden_types(self.db, func)[idx]
|
||||
}
|
||||
crate::ImplTraitId::TypeAliasImplTrait(..) => {
|
||||
// FIXME(next-solver)
|
||||
EarlyBinder::bind(Ty::new_error(self, ErrorGuaranteed))
|
||||
crate::ImplTraitId::TypeAliasImplTrait(type_alias, idx) => {
|
||||
crate::opaques::tait_hidden_types(self.db, type_alias)[idx]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1969,13 +2006,6 @@ impl<'db> Interner for DbInterner<'db> {
|
|||
true
|
||||
}
|
||||
|
||||
fn opaque_types_and_coroutines_defined_by(
|
||||
self,
|
||||
_defining_anchor: Self::LocalDefId,
|
||||
) -> Self::LocalDefIds {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
type Probe = rustc_type_ir::solve::inspect::Probe<DbInterner<'db>>;
|
||||
fn mk_probe(self, probe: rustc_type_ir::solve::inspect::Probe<Self>) -> Self::Probe {
|
||||
probe
|
||||
|
|
|
|||
|
|
@ -2,17 +2,22 @@
|
|||
|
||||
use hir_def::{AssocItemId, GeneralConstId};
|
||||
use rustc_next_trait_solver::delegate::SolverDelegate;
|
||||
use rustc_type_ir::GenericArgKind;
|
||||
use rustc_type_ir::lang_items::SolverTraitLangItem;
|
||||
use rustc_type_ir::{
|
||||
InferCtxtLike, Interner, PredicatePolarity, TypeFlags, TypeVisitableExt,
|
||||
inherent::{IntoKind, Term as _, Ty as _},
|
||||
AliasTyKind, GenericArgKind, InferCtxtLike, Interner, PredicatePolarity, TypeFlags,
|
||||
TypeVisitableExt,
|
||||
inherent::{IntoKind, SliceLike, Term as _, Ty as _},
|
||||
lang_items::SolverTraitLangItem,
|
||||
solve::{Certainty, NoSolution},
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::next_solver::{CanonicalVarKind, ImplIdWrapper};
|
||||
use crate::next_solver::{
|
||||
ClauseKind, CoercePredicate, PredicateKind, SubtypePredicate, util::sizedness_fast_path,
|
||||
use crate::{
|
||||
ImplTraitId,
|
||||
next_solver::{
|
||||
AliasTy, CanonicalVarKind, Clause, ClauseKind, CoercePredicate, GenericArgs, ImplIdWrapper,
|
||||
ParamEnv, Predicate, PredicateKind, SubtypePredicate, Ty, TyKind, fold::fold_tys,
|
||||
util::sizedness_fast_path,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
|
@ -76,7 +81,7 @@ impl<'db> SolverDelegate for SolverContext<'db> {
|
|||
|
||||
fn well_formed_goals(
|
||||
&self,
|
||||
_param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv,
|
||||
_param_env: ParamEnv<'db>,
|
||||
_arg: <Self::Interner as rustc_type_ir::Interner>::Term,
|
||||
) -> Option<
|
||||
Vec<
|
||||
|
|
@ -125,18 +130,60 @@ impl<'db> SolverDelegate for SolverContext<'db> {
|
|||
|
||||
fn add_item_bounds_for_hidden_type(
|
||||
&self,
|
||||
_def_id: <Self::Interner as rustc_type_ir::Interner>::DefId,
|
||||
_args: <Self::Interner as rustc_type_ir::Interner>::GenericArgs,
|
||||
_param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv,
|
||||
_hidden_ty: <Self::Interner as rustc_type_ir::Interner>::Ty,
|
||||
_goals: &mut Vec<
|
||||
rustc_type_ir::solve::Goal<
|
||||
Self::Interner,
|
||||
<Self::Interner as rustc_type_ir::Interner>::Predicate,
|
||||
>,
|
||||
>,
|
||||
def_id: SolverDefId,
|
||||
args: GenericArgs<'db>,
|
||||
param_env: ParamEnv<'db>,
|
||||
hidden_ty: Ty<'db>,
|
||||
goals: &mut Vec<Goal<'db, Predicate<'db>>>,
|
||||
) {
|
||||
unimplemented!()
|
||||
let interner = self.interner;
|
||||
let opaque_id = def_id.expect_opaque_ty();
|
||||
// Require that the hidden type is well-formed. We have to
|
||||
// make sure we wf-check the hidden type to fix #114728.
|
||||
//
|
||||
// However, we don't check that all types are well-formed.
|
||||
// We only do so for types provided by the user or if they are
|
||||
// "used", e.g. for method selection.
|
||||
//
|
||||
// This means we never check the wf requirements of the hidden
|
||||
// type during MIR borrowck, causing us to infer the wrong
|
||||
// lifetime for its member constraints which then results in
|
||||
// unexpected region errors.
|
||||
goals.push(Goal::new(interner, param_env, ClauseKind::WellFormed(hidden_ty.into())));
|
||||
|
||||
let replace_opaques_in = |clause: Clause<'db>| {
|
||||
fold_tys(interner, clause, |ty| match ty.kind() {
|
||||
// Replace all other mentions of the same opaque type with the hidden type,
|
||||
// as the bounds must hold on the hidden type after all.
|
||||
TyKind::Alias(
|
||||
AliasTyKind::Opaque,
|
||||
AliasTy { def_id: def_id2, args: args2, .. },
|
||||
) if def_id == def_id2 && args == args2 => hidden_ty,
|
||||
_ => ty,
|
||||
})
|
||||
};
|
||||
|
||||
let db = interner.db;
|
||||
let (opaques_table, opaque_idx) = match opaque_id.loc(db) {
|
||||
ImplTraitId::ReturnTypeImplTrait(func, opaque_idx) => {
|
||||
(db.return_type_impl_traits(func), opaque_idx)
|
||||
}
|
||||
ImplTraitId::TypeAliasImplTrait(type_alias, opaque_idx) => {
|
||||
(db.type_alias_impl_traits(type_alias), opaque_idx)
|
||||
}
|
||||
};
|
||||
let item_bounds = opaques_table
|
||||
.as_deref()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.map_bound(|table| &table.impl_traits[opaque_idx].predicates);
|
||||
for predicate in item_bounds.iter_instantiated_copied(interner, args.as_slice()) {
|
||||
let predicate = replace_opaques_in(predicate);
|
||||
|
||||
// Require that the predicate holds for the concrete type.
|
||||
debug!(?predicate);
|
||||
goals.push(Goal::new(interner, param_env, predicate));
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_eligible_assoc_item(
|
||||
|
|
@ -190,8 +237,8 @@ impl<'db> SolverDelegate for SolverContext<'db> {
|
|||
|
||||
fn is_transmutable(
|
||||
&self,
|
||||
_dst: <Self::Interner as rustc_type_ir::Interner>::Ty,
|
||||
_src: <Self::Interner as rustc_type_ir::Interner>::Ty,
|
||||
_dst: Ty<'db>,
|
||||
_src: Ty<'db>,
|
||||
_assume: <Self::Interner as rustc_type_ir::Interner>::Const,
|
||||
) -> Result<Certainty, NoSolution> {
|
||||
unimplemented!()
|
||||
|
|
@ -199,7 +246,7 @@ impl<'db> SolverDelegate for SolverContext<'db> {
|
|||
|
||||
fn evaluate_const(
|
||||
&self,
|
||||
_param_env: <Self::Interner as rustc_type_ir::Interner>::ParamEnv,
|
||||
_param_env: ParamEnv<'db>,
|
||||
uv: rustc_type_ir::UnevaluatedConst<Self::Interner>,
|
||||
) -> Option<<Self::Interner as rustc_type_ir::Interner>::Const> {
|
||||
let c = match uv.def {
|
||||
|
|
|
|||
199
crates/hir-ty/src/opaques.rs
Normal file
199
crates/hir-ty/src/opaques.rs
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
//! Handling of opaque types, detection of defining scope and hidden type.
|
||||
|
||||
use hir_def::{
|
||||
AssocItemId, AssocItemLoc, DefWithBodyId, FunctionId, HasModule, ItemContainerId, TypeAliasId,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
use la_arena::ArenaMap;
|
||||
use rustc_type_ir::inherent::Ty as _;
|
||||
use syntax::ast;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
ImplTraitId,
|
||||
db::{HirDatabase, InternedOpaqueTyId},
|
||||
lower::{ImplTraitIdx, ImplTraits},
|
||||
next_solver::{
|
||||
DbInterner, EarlyBinder, ErrorGuaranteed, SolverDefId, Ty, TypingMode,
|
||||
infer::{DbInternerInferExt, traits::ObligationCause},
|
||||
obligation_ctxt::ObligationCtxt,
|
||||
},
|
||||
};
|
||||
|
||||
pub(crate) fn opaque_types_defined_by(
|
||||
db: &dyn HirDatabase,
|
||||
def_id: DefWithBodyId,
|
||||
result: &mut Vec<SolverDefId>,
|
||||
) {
|
||||
if let DefWithBodyId::FunctionId(func) = def_id {
|
||||
// A function may define its own RPITs.
|
||||
extend_with_opaques(
|
||||
db,
|
||||
db.return_type_impl_traits(func),
|
||||
|opaque_idx| ImplTraitId::ReturnTypeImplTrait(func, opaque_idx),
|
||||
result,
|
||||
);
|
||||
}
|
||||
|
||||
let extend_with_taits = |type_alias| {
|
||||
extend_with_opaques(
|
||||
db,
|
||||
db.type_alias_impl_traits(type_alias),
|
||||
|opaque_idx| ImplTraitId::TypeAliasImplTrait(type_alias, opaque_idx),
|
||||
result,
|
||||
);
|
||||
};
|
||||
|
||||
// Collect opaques from assoc items.
|
||||
let extend_with_atpit_from_assoc_items = |assoc_items: &[(Name, AssocItemId)]| {
|
||||
assoc_items
|
||||
.iter()
|
||||
.filter_map(|&(_, assoc_id)| match assoc_id {
|
||||
AssocItemId::TypeAliasId(it) => Some(it),
|
||||
AssocItemId::FunctionId(_) | AssocItemId::ConstId(_) => None,
|
||||
})
|
||||
.for_each(extend_with_taits);
|
||||
};
|
||||
let extend_with_atpit_from_container = |container| match container {
|
||||
ItemContainerId::ImplId(impl_id) => {
|
||||
if db.impl_signature(impl_id).target_trait.is_some() {
|
||||
extend_with_atpit_from_assoc_items(&impl_id.impl_items(db).items);
|
||||
}
|
||||
}
|
||||
ItemContainerId::TraitId(trait_id) => {
|
||||
extend_with_atpit_from_assoc_items(&trait_id.trait_items(db).items);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
match def_id {
|
||||
DefWithBodyId::ConstId(id) => extend_with_atpit_from_container(id.loc(db).container),
|
||||
DefWithBodyId::FunctionId(id) => extend_with_atpit_from_container(id.loc(db).container),
|
||||
DefWithBodyId::StaticId(_) | DefWithBodyId::VariantId(_) => {}
|
||||
}
|
||||
|
||||
// FIXME: Collect opaques from `#[define_opaque]`.
|
||||
|
||||
fn extend_with_opaques<'db>(
|
||||
db: &'db dyn HirDatabase,
|
||||
opaques: Option<Arc<EarlyBinder<'db, ImplTraits<'db>>>>,
|
||||
mut make_impl_trait: impl FnMut(ImplTraitIdx<'db>) -> ImplTraitId<'db>,
|
||||
result: &mut Vec<SolverDefId>,
|
||||
) {
|
||||
if let Some(opaques) = opaques {
|
||||
for (opaque_idx, _) in (*opaques).as_ref().skip_binder().impl_traits.iter() {
|
||||
let opaque_id = InternedOpaqueTyId::new(db, make_impl_trait(opaque_idx));
|
||||
result.push(opaque_id.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These are firewall queries to prevent drawing dependencies between infers:
|
||||
|
||||
#[salsa::tracked(returns(ref), unsafe(non_update_return_type))]
|
||||
pub(crate) fn rpit_hidden_types<'db>(
|
||||
db: &'db dyn HirDatabase,
|
||||
function: FunctionId,
|
||||
) -> ArenaMap<ImplTraitIdx<'db>, EarlyBinder<'db, Ty<'db>>> {
|
||||
let infer = db.infer(function.into());
|
||||
let mut result = ArenaMap::new();
|
||||
for (opaque, hidden_type) in infer.return_position_impl_trait_types(db) {
|
||||
result.insert(opaque, EarlyBinder::bind(hidden_type));
|
||||
}
|
||||
result.shrink_to_fit();
|
||||
result
|
||||
}
|
||||
|
||||
#[salsa::tracked(returns(ref), unsafe(non_update_return_type))]
|
||||
pub(crate) fn tait_hidden_types<'db>(
|
||||
db: &'db dyn HirDatabase,
|
||||
type_alias: TypeAliasId,
|
||||
) -> ArenaMap<ImplTraitIdx<'db>, EarlyBinder<'db, Ty<'db>>> {
|
||||
let loc = type_alias.loc(db);
|
||||
let module = loc.module(db);
|
||||
let interner = DbInterner::new_with(db, Some(module.krate()), module.containing_block());
|
||||
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
|
||||
let mut ocx = ObligationCtxt::new(&infcx);
|
||||
let cause = ObligationCause::dummy();
|
||||
let param_env = db.trait_environment(type_alias.into()).env;
|
||||
|
||||
let defining_bodies = tait_defining_bodies(db, &loc);
|
||||
|
||||
let taits_count = db
|
||||
.type_alias_impl_traits(type_alias)
|
||||
.map_or(0, |taits| (*taits).as_ref().skip_binder().impl_traits.len());
|
||||
|
||||
let mut result = ArenaMap::with_capacity(taits_count);
|
||||
for defining_body in defining_bodies {
|
||||
let infer = db.infer(defining_body);
|
||||
for (&opaque, &hidden_type) in &infer.type_of_opaque {
|
||||
let ImplTraitId::TypeAliasImplTrait(opaque_owner, opaque_idx) = opaque.loc(db) else {
|
||||
continue;
|
||||
};
|
||||
if opaque_owner != type_alias {
|
||||
continue;
|
||||
}
|
||||
// In the presence of errors, we attempt to create a unified type from all
|
||||
// types. rustc doesn't do that, but this should improve the experience.
|
||||
let hidden_type = infcx.insert_type_vars(hidden_type);
|
||||
match result.entry(opaque_idx) {
|
||||
la_arena::Entry::Vacant(entry) => {
|
||||
entry.insert(EarlyBinder::bind(hidden_type));
|
||||
}
|
||||
la_arena::Entry::Occupied(entry) => {
|
||||
_ = ocx.eq(&cause, param_env, entry.get().instantiate_identity(), hidden_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ = ocx.try_evaluate_obligations();
|
||||
|
||||
// Fill missing entries.
|
||||
for idx in 0..taits_count {
|
||||
let idx = la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(idx as u32));
|
||||
match result.entry(idx) {
|
||||
la_arena::Entry::Vacant(entry) => {
|
||||
entry.insert(EarlyBinder::bind(Ty::new_error(interner, ErrorGuaranteed)));
|
||||
}
|
||||
la_arena::Entry::Occupied(mut entry) => {
|
||||
*entry.get_mut() = entry.get().map_bound(|hidden_type| {
|
||||
infcx.resolve_vars_if_possible(hidden_type).replace_infer_with_error(interner)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn tait_defining_bodies(
|
||||
db: &dyn HirDatabase,
|
||||
loc: &AssocItemLoc<ast::TypeAlias>,
|
||||
) -> Vec<DefWithBodyId> {
|
||||
let from_assoc_items = |assoc_items: &[(Name, AssocItemId)]| {
|
||||
// Associated Type Position Impl Trait.
|
||||
assoc_items
|
||||
.iter()
|
||||
.filter_map(|&(_, assoc_id)| match assoc_id {
|
||||
AssocItemId::FunctionId(it) => Some(it.into()),
|
||||
AssocItemId::ConstId(it) => Some(it.into()),
|
||||
AssocItemId::TypeAliasId(_) => None,
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
match loc.container {
|
||||
ItemContainerId::ImplId(impl_id) => {
|
||||
if db.impl_signature(impl_id).target_trait.is_some() {
|
||||
return from_assoc_items(&impl_id.impl_items(db).items);
|
||||
}
|
||||
}
|
||||
ItemContainerId::TraitId(trait_id) => {
|
||||
return from_assoc_items(&trait_id.trait_items(db).items);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// FIXME: Support general TAITs, or decisively decide not to.
|
||||
Vec::new()
|
||||
}
|
||||
|
|
@ -591,6 +591,7 @@ fn main() {
|
|||
"function_signature_shim",
|
||||
"function_signature_with_source_map_shim",
|
||||
"trait_environment_shim",
|
||||
"return_type_impl_traits_shim",
|
||||
"expr_scopes_shim",
|
||||
"struct_signature_shim",
|
||||
"struct_signature_with_source_map_shim",
|
||||
|
|
@ -686,6 +687,7 @@ fn main() {
|
|||
"return_type_impl_traits_shim",
|
||||
"infer_shim",
|
||||
"function_signature_with_source_map_shim",
|
||||
"return_type_impl_traits_shim",
|
||||
"expr_scopes_shim",
|
||||
"struct_signature_with_source_map_shim",
|
||||
"generic_predicates_shim",
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ fn test() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "FIXME(next-solver): This currently generates a type mismatch, need to switch opaque type handling to the solver"]
|
||||
fn associated_type_impl_traits_complex() {
|
||||
check_types(
|
||||
r#"
|
||||
|
|
@ -116,6 +115,7 @@ fn foo() {
|
|||
);
|
||||
}
|
||||
|
||||
#[ignore = "FIXME(next-solver): TAIT support was removed, need to rework it to work with `#[define_opaque]`"]
|
||||
#[test]
|
||||
fn type_alias_impl_trait_simple() {
|
||||
check_no_mismatches(
|
||||
|
|
@ -135,9 +135,6 @@ static ALIAS: AliasTy = {
|
|||
"#,
|
||||
);
|
||||
|
||||
// FIXME(next-solver): This should emit type mismatch error but leaving it for now
|
||||
// as we should fully migrate into next-solver without chalk-ir and TAIT should be
|
||||
// reworked on r-a to handle `#[define_opaque(T)]`
|
||||
check_infer_with_mismatches(
|
||||
r#"
|
||||
trait Trait {}
|
||||
|
|
|
|||
|
|
@ -725,7 +725,7 @@ fn issue_4885() {
|
|||
138..146 'bar(key)': impl Future<Output = <K as Foo<R>>::Bar>
|
||||
142..145 'key': &'? K
|
||||
162..165 'key': &'? K
|
||||
224..227 '{ }': ()
|
||||
224..227 '{ }': impl Future<Output = <K as Foo<R>>::Bar>
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ impl<'a> IntoIterator for &'a Grid {
|
|||
"#,
|
||||
expect![[r#"
|
||||
150..154 'self': &'a Grid
|
||||
174..181 '{ }': impl Iterator<Item = &'a ()>
|
||||
174..181 '{ }': <&'a Grid as IntoIterator>::IntoIter
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1211,7 +1211,7 @@ fn test(x: impl Trait<u64>, y: &impl Trait<u64>) {
|
|||
expect![[r#"
|
||||
29..33 'self': &'? Self
|
||||
54..58 'self': &'? Self
|
||||
98..100 '{}': ()
|
||||
98..100 '{}': impl Trait<u64>
|
||||
110..111 'x': impl Trait<u64>
|
||||
130..131 'y': &'? impl Trait<u64>
|
||||
151..268 '{ ...2(); }': ()
|
||||
|
|
@ -1373,11 +1373,11 @@ fn test() {
|
|||
expect![[r#"
|
||||
49..53 'self': &'? mut Self
|
||||
101..105 'self': &'? Self
|
||||
184..195 '{ loop {} }': ({unknown}, {unknown})
|
||||
184..195 '{ loop {} }': (impl Iterator<Item = impl Trait<u32>>, impl Trait<u64>)
|
||||
186..193 'loop {}': !
|
||||
191..193 '{}': ()
|
||||
206..207 't': T
|
||||
268..279 '{ loop {} }': ({unknown}, {unknown})
|
||||
268..279 '{ loop {} }': (impl Iterator<Item = impl Trait<T>>, impl Trait<T>)
|
||||
270..277 'loop {}': !
|
||||
275..277 '{}': ()
|
||||
291..413 '{ ...o(); }': ()
|
||||
|
|
@ -1419,7 +1419,7 @@ fn foo<const C: u8, T>() -> (impl FnOnce(&str, T), impl Trait<u8>) {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
134..165 '{ ...(C)) }': (impl FnOnce(&'? str, T), Bar<u8>)
|
||||
134..165 '{ ...(C)) }': (impl FnOnce(&'? str, T), impl Trait<u8>)
|
||||
140..163 '(|inpu...ar(C))': (impl FnOnce(&'? str, T), Bar<u8>)
|
||||
141..154 '|input, t| {}': impl FnOnce(&'? str, T)
|
||||
142..147 'input': &'? str
|
||||
|
|
@ -1441,7 +1441,7 @@ fn return_pos_impl_trait_in_projection() {
|
|||
trait Future { type Output; }
|
||||
impl Future for () { type Output = i32; }
|
||||
type Foo<F> = (<F as Future>::Output, F);
|
||||
fn foo<X>() -> Foo<impl Future<Output = ()>> {
|
||||
fn foo<X>() -> Foo<impl Future<Output = i32>> {
|
||||
(0, ())
|
||||
}
|
||||
"#,
|
||||
|
|
|
|||
|
|
@ -107,24 +107,26 @@ pub fn next_trait_solve_canonical_in_ctxt<'db>(
|
|||
infer_ctxt: &InferCtxt<'db>,
|
||||
goal: Canonical<'db, Goal<'db, Predicate<'db>>>,
|
||||
) -> NextTraitSolveResult {
|
||||
let context = SolverContext(infer_ctxt.clone());
|
||||
infer_ctxt.probe(|_| {
|
||||
let context = <&SolverContext<'db>>::from(infer_ctxt);
|
||||
|
||||
tracing::info!(?goal);
|
||||
tracing::info!(?goal);
|
||||
|
||||
let (goal, var_values) = context.instantiate_canonical(&goal);
|
||||
tracing::info!(?var_values);
|
||||
let (goal, var_values) = context.instantiate_canonical(&goal);
|
||||
tracing::info!(?var_values);
|
||||
|
||||
let res = context.evaluate_root_goal(goal, Span::dummy(), None);
|
||||
let res = context.evaluate_root_goal(goal, Span::dummy(), None);
|
||||
|
||||
let res = res.map(|r| (r.has_changed, r.certainty));
|
||||
let res = res.map(|r| (r.has_changed, r.certainty));
|
||||
|
||||
tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res);
|
||||
tracing::debug!("solve_nextsolver({:?}) => {:?}", goal, res);
|
||||
|
||||
match res {
|
||||
Err(_) => NextTraitSolveResult::NoSolution,
|
||||
Ok((_, Certainty::Yes)) => NextTraitSolveResult::Certain,
|
||||
Ok((_, Certainty::Maybe { .. })) => NextTraitSolveResult::Uncertain,
|
||||
}
|
||||
match res {
|
||||
Err(_) => NextTraitSolveResult::NoSolution,
|
||||
Ok((_, Certainty::Yes)) => NextTraitSolveResult::Certain,
|
||||
Ok((_, Certainty::Maybe { .. })) => NextTraitSolveResult::Uncertain,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Solve a trait goal using next trait solver.
|
||||
|
|
|
|||
|
|
@ -5136,10 +5136,7 @@ impl<'db> Type<'db> {
|
|||
AliasTy::new(interner, alias.id.into(), args),
|
||||
);
|
||||
|
||||
// FIXME(next-solver): This needs to be `PostAnalysis`, but this currently causes errors due to our incorrect
|
||||
// handling of opaques. `non_body_analysis()` will also cause errors (from not revealing opaques inside their
|
||||
// defining places), so we choose between two bad options.
|
||||
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
|
||||
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
|
||||
let ty = structurally_normalize_ty(&infcx, projection, self.env.clone());
|
||||
if ty.is_ty_error() { None } else { Some(self.derived(ty)) }
|
||||
}
|
||||
|
|
@ -5758,8 +5755,7 @@ impl<'db> Type<'db> {
|
|||
|
||||
pub fn drop_glue(&self, db: &'db dyn HirDatabase) -> DropGlue {
|
||||
let interner = DbInterner::new_with(db, Some(self.env.krate), self.env.block);
|
||||
// FIXME: This should be `PostAnalysis` I believe.
|
||||
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
|
||||
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
|
||||
hir_ty::drop::has_drop_glue(&infcx, self.ty, self.env.clone())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10809,7 +10809,7 @@ type Foo$0 = impl Sized;
|
|||
|
||||
---
|
||||
|
||||
needs Drop
|
||||
no Drop
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
|
|
|
|||
|
|
@ -1996,6 +1996,12 @@ fn f<T, F: FnMut(&T, u16) -> &T>(f: F) {
|
|||
|
||||
#[test]
|
||||
fn regression_13579() {
|
||||
// FIXME(next-solver): There should be signature help available here.
|
||||
// The reason it is not is because of a trait solver bug. Since `Error` is not provided
|
||||
// nor it can be inferred, it becomes an error type. The bug is that the solver ignores
|
||||
// predicates on error types, and they do not guide infer vars, not allowing us to infer
|
||||
// that `take`'s return type is callable.
|
||||
// https://github.com/rust-lang/rust/pull/146602 should fix the solver bug.
|
||||
check(
|
||||
r#"
|
||||
fn f() {
|
||||
|
|
@ -2008,9 +2014,7 @@ fn take<C, Error>(
|
|||
move || count
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
impl Fn() -> i32
|
||||
"#]],
|
||||
expect![""],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue