1
Fork 0

Rollup merge of #135426 - compiler-errors:no-resolve-assoc-ty, r=lcnr

Assert that `Instance::try_resolve` is only used on body-like things

`Instance::resolve` is not set up to resolve items that are not body-like things. The logic in `resolve_associated_item` very much encodes this assumption:

e7ad3ae331/compiler/rustc_ty_utils/src/instance.rs (L96-L386)

However, some diagnostics were using `Instance::resolve` on an associated type, and it was simply a lucky coicidence that nothing went wrong.

This PR adds an assertion to make sure we won't do this again in the future, and fixes two callsites:
1. `call_kind` which returns a `CallKind` enum to categorize what a call in MIR comes from, and was using `Instance::resolve` to point at the associated type `Deref::Target` for a specific self ty.
2. `MirBorrowckCtxt::explain_deref_coercion`, which was doing the same thing.

The logic was replaced with `specialization_graph::assoc_def`, which is the proper way of fetching the right `AssocItem` for a given impl.

r? `@lcnr` or re-roll :)
This commit is contained in:
Matthias Krüger 2025-01-13 15:57:14 +01:00 committed by GitHub
commit 40f5861de9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 118 additions and 99 deletions

View file

@ -5,7 +5,7 @@ use std::path::PathBuf;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def::Namespace;
use rustc_hir::def::{CtorKind, DefKind, Namespace};
use rustc_hir::def_id::{CrateNum, DefId};
use rustc_hir::lang_items::LangItem;
use rustc_index::bit_set::FiniteBitSet;
@ -498,7 +498,8 @@ impl<'tcx> Instance<'tcx> {
/// Resolves a `(def_id, args)` pair to an (optional) instance -- most commonly,
/// this is used to find the precise code that will run for a trait method invocation,
/// if known.
/// if known. This should only be used for functions and consts. If you want to
/// resolve an associated type, use [`TyCtxt::try_normalize_erasing_regions`].
///
/// Returns `Ok(None)` if we cannot resolve `Instance` to a specific instance.
/// For example, in a context like this,
@ -527,6 +528,23 @@ impl<'tcx> Instance<'tcx> {
def_id: DefId,
args: GenericArgsRef<'tcx>,
) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
assert_matches!(
tcx.def_kind(def_id),
DefKind::Fn
| DefKind::AssocFn
| DefKind::Const
| DefKind::AssocConst
| DefKind::AnonConst
| DefKind::InlineConst
| DefKind::Static { .. }
| DefKind::Ctor(_, CtorKind::Fn)
| DefKind::Closure
| DefKind::SyntheticCoroutineBody,
"`Instance::try_resolve` should only be used to resolve instances of \
functions, statics, and consts; to resolve associated types, use \
`try_normalize_erasing_regions`."
);
// Rust code can easily create exponentially-long types using only a
// polynomial recursion depth. Even with the default recursion
// depth, you can easily get cases that take >2^60 steps to run,

View file

@ -1,149 +0,0 @@
//! Common logic for borrowck use-after-move errors when moved into a `fn(self)`,
//! as well as errors when attempting to call a non-const function in a const
//! context.
use rustc_hir::def_id::DefId;
use rustc_hir::{LangItem, lang_items};
use rustc_span::{DesugaringKind, Ident, Span, sym};
use tracing::debug;
use crate::ty::{AssocItemContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum CallDesugaringKind {
/// for _ in x {} calls x.into_iter()
ForLoopIntoIter,
/// for _ in x {} calls iter.next()
ForLoopNext,
/// x? calls x.branch()
QuestionBranch,
/// x? calls type_of(x)::from_residual()
QuestionFromResidual,
/// try { ..; x } calls type_of(x)::from_output(x)
TryBlockFromOutput,
/// `.await` calls `IntoFuture::into_future`
Await,
}
impl CallDesugaringKind {
pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId {
match self {
Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(),
Self::ForLoopNext => tcx.require_lang_item(LangItem::Iterator, None),
Self::QuestionBranch | Self::TryBlockFromOutput => {
tcx.require_lang_item(LangItem::Try, None)
}
Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(),
Self::Await => tcx.get_diagnostic_item(sym::IntoFuture).unwrap(),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum CallKind<'tcx> {
/// A normal method call of the form `receiver.foo(a, b, c)`
Normal {
self_arg: Option<Ident>,
desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>,
method_did: DefId,
method_args: GenericArgsRef<'tcx>,
},
/// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)`
FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> },
/// A call to an operator trait, desugared from operator syntax (e.g. `a << b`)
Operator { self_arg: Option<Ident>, trait_id: DefId, self_ty: Ty<'tcx> },
DerefCoercion {
/// The `Span` of the `Target` associated type
/// in the `Deref` impl we are using.
deref_target: Span,
/// The type `T::Deref` we are dereferencing to
deref_target_ty: Ty<'tcx>,
self_ty: Ty<'tcx>,
},
}
pub fn call_kind<'tcx>(
tcx: TyCtxt<'tcx>,
typing_env: TypingEnv<'tcx>,
method_did: DefId,
method_args: GenericArgsRef<'tcx>,
fn_call_span: Span,
from_hir_call: bool,
self_arg: Option<Ident>,
) -> CallKind<'tcx> {
let parent = tcx.opt_associated_item(method_did).and_then(|assoc| {
let container_id = assoc.container_id(tcx);
match assoc.container {
AssocItemContainer::Impl => tcx.trait_id_of_impl(container_id),
AssocItemContainer::Trait => Some(container_id),
}
});
let fn_call = parent.and_then(|p| {
lang_items::FN_TRAITS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
});
let operator = if !from_hir_call && let Some(p) = parent {
lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
} else {
None
};
let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did);
// Check for a 'special' use of 'self' -
// an FnOnce call, an operator (e.g. `<<`), or a
// deref coercion.
let kind = if let Some(trait_id) = fn_call {
Some(CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_args.type_at(0) })
} else if let Some(trait_id) = operator {
Some(CallKind::Operator { self_arg, trait_id, self_ty: method_args.type_at(0) })
} else if is_deref {
let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
Instance::try_resolve(tcx, typing_env, deref_target, method_args).transpose()
});
if let Some(Ok(instance)) = deref_target {
let deref_target_ty = instance.ty(tcx, typing_env);
Some(CallKind::DerefCoercion {
deref_target: tcx.def_span(instance.def_id()),
deref_target_ty,
self_ty: method_args.type_at(0),
})
} else {
None
}
} else {
None
};
kind.unwrap_or_else(|| {
// This isn't a 'special' use of `self`
debug!(?method_did, ?fn_call_span);
let desugaring = if tcx.is_lang_item(method_did, LangItem::IntoIterIntoIter)
&& fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
{
Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0)))
} else if tcx.is_lang_item(method_did, LangItem::IteratorNext)
&& fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
{
Some((CallDesugaringKind::ForLoopNext, method_args.type_at(0)))
} else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) {
if tcx.is_lang_item(method_did, LangItem::TryTraitBranch) {
Some((CallDesugaringKind::QuestionBranch, method_args.type_at(0)))
} else if tcx.is_lang_item(method_did, LangItem::TryTraitFromResidual) {
Some((CallDesugaringKind::QuestionFromResidual, method_args.type_at(0)))
} else {
None
}
} else if tcx.is_lang_item(method_did, LangItem::TryTraitFromOutput)
&& fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock)
{
Some((CallDesugaringKind::TryBlockFromOutput, method_args.type_at(0)))
} else if fn_call_span.is_desugaring(DesugaringKind::Await) {
Some((CallDesugaringKind::Await, method_args.type_at(0)))
} else {
None
};
CallKind::Normal { self_arg, desugaring, method_did, method_args }
})
}

View file

@ -1,9 +1,7 @@
pub mod bug;
pub mod call_kind;
pub mod common;
pub mod find_self_call;
pub use call_kind::{CallDesugaringKind, CallKind, call_kind};
pub use find_self_call::find_self_call;
#[derive(Default, Copy, Clone)]