Auto merge of #96840 - cjgillot:query-feed, r=oli-obk
Allow to feed a value in another query's cache and remove `WithOptConstParam` I used it to remove `WithOptConstParam` queries, as an example. The idea is that a query (here `typeck(function)`) can write into another query's cache (here `type_of(anon const)`). The dependency node for `type_of` would depend on all the current dependencies of `typeck`. There is still an issue with cycles: if `type_of(anon const)` is accessed before `typeck(function)`, we will still have the usual cycle. The way around this issue is to `ensure` that `typeck(function)` is called before accessing `type_of(anon const)`. When replayed, we may the following cases: - `typeck` is green, in that case `type_of` is green too, and all is right; - `type_of` is green, `typeck` may still be marked as red (it depends on strictly more things than `type_of`) -> we verify that the saved value and the re-computed value of `type_of` have the same hash; - `type_of` is red, then `typeck` is red -> it's the caller responsibility to ensure `typeck` is recomputed *before* `type_of`. As `anon consts` have their own `DefPathData`, it's not possible to have the def-id of the anon-const point to something outside the original function, but the general case may have to be resolved before using this device more broadly. There is an open question about loading from the on-disk cache. If `typeck` is loaded from the on-disk cache, the side-effect does not happen. The regular `type_of` implementation can go and fetch the correct value from the decoded `typeck` results, and the dep-graph will check that the hashes match, but I'm not sure we want to rely on this behaviour. I specifically allowed to feed the value to `type_of` from inside a call to `type_of`. In that case, the dep-graph will check that the fingerprints of both values match. This implementation is still very sensitive to cycles, and requires that we call `typeck(function)` before `typeck(anon const)`. The reason is that `typeck(anon const)` calls `type_of(anon const)`, which calls `typeck(function)`, which feeds `type_of(anon const)`, and needs to build the MIR so needs `typeck(anon const)`. The latter call would not cycle, since `type_of(anon const)` has been set, but I'd rather not remove the cycle check.
This commit is contained in:
commit
1f5768bc67
81 changed files with 598 additions and 1197 deletions
|
@ -447,14 +447,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
handle_ty_args(has_default, &inf.to_ty())
|
||||
}
|
||||
(GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
|
||||
ty::Const::from_opt_const_arg_anon_const(
|
||||
tcx,
|
||||
ty::WithOptConstParam {
|
||||
did: ct.value.def_id,
|
||||
const_param_did: Some(param.def_id),
|
||||
},
|
||||
)
|
||||
.into()
|
||||
let did = ct.value.def_id;
|
||||
tcx.feed_anon_const_type(did, tcx.type_of(param.def_id));
|
||||
ty::Const::from_anon_const(tcx, did).into()
|
||||
}
|
||||
(&GenericParamDefKind::Const { .. }, hir::GenericArg::Infer(inf)) => {
|
||||
let ty = tcx
|
||||
|
|
|
@ -55,7 +55,6 @@ fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
|
|||
pub fn provide(providers: &mut Providers) {
|
||||
resolve_bound_vars::provide(providers);
|
||||
*providers = Providers {
|
||||
opt_const_param_of: type_of::opt_const_param_of,
|
||||
type_of: type_of::type_of,
|
||||
item_bounds: item_bounds::item_bounds,
|
||||
explicit_item_bounds: item_bounds::explicit_item_bounds,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use rustc_errors::{Applicability, StashKey};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{HirId, Node};
|
||||
|
@ -16,22 +16,81 @@ use super::ItemCtxt;
|
|||
use super::{bad_placeholder, is_suggestable_infer_ty};
|
||||
use crate::errors::UnconstrainedOpaqueType;
|
||||
|
||||
/// Computes the relevant generic parameter for a potential generic const argument.
|
||||
///
|
||||
/// This should be called using the query `tcx.opt_const_param_of`.
|
||||
pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> {
|
||||
fn anon_const_type_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
|
||||
use hir::*;
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
|
||||
|
||||
match tcx.hir().get(hir_id) {
|
||||
Node::AnonConst(_) => (),
|
||||
_ => return None,
|
||||
};
|
||||
let Node::AnonConst(_) = tcx.hir().get(hir_id) else { panic!() };
|
||||
|
||||
let parent_node_id = tcx.hir().parent_id(hir_id);
|
||||
let parent_node = tcx.hir().get(parent_node_id);
|
||||
|
||||
let (generics, arg_idx) = match parent_node {
|
||||
// Easy case: arrays repeat expressions.
|
||||
Node::Ty(&Ty { kind: TyKind::Array(_, ref constant), .. })
|
||||
| Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
|
||||
if constant.hir_id() == hir_id =>
|
||||
{
|
||||
return tcx.types.usize
|
||||
}
|
||||
Node::Ty(&Ty { kind: TyKind::Typeof(ref e), .. }) if e.hir_id == hir_id => {
|
||||
return tcx.typeck(def_id).node_type(e.hir_id)
|
||||
}
|
||||
Node::Expr(&Expr { kind: ExprKind::ConstBlock(ref anon_const), .. })
|
||||
if anon_const.hir_id == hir_id =>
|
||||
{
|
||||
let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
|
||||
return substs.as_inline_const().ty()
|
||||
}
|
||||
Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
|
||||
| Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. })
|
||||
if asm.operands.iter().any(|(op, _op_sp)| match op {
|
||||
hir::InlineAsmOperand::Const { anon_const }
|
||||
| hir::InlineAsmOperand::SymFn { anon_const } => anon_const.hir_id == hir_id,
|
||||
_ => false,
|
||||
}) =>
|
||||
{
|
||||
return tcx.typeck(def_id).node_type(hir_id)
|
||||
}
|
||||
Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => {
|
||||
return tcx
|
||||
.adt_def(tcx.hir().get_parent_item(hir_id))
|
||||
.repr()
|
||||
.discr_type()
|
||||
.to_ty(tcx)
|
||||
}
|
||||
Node::GenericParam(&GenericParam {
|
||||
def_id: param_def_id,
|
||||
kind: GenericParamKind::Const { default: Some(ct), .. },
|
||||
..
|
||||
}) if ct.hir_id == hir_id => {
|
||||
return tcx.type_of(param_def_id)
|
||||
.no_bound_vars()
|
||||
.expect("const parameter types cannot be generic")
|
||||
}
|
||||
|
||||
Node::TypeBinding(binding @ &TypeBinding { hir_id: binding_id, .. })
|
||||
if let Node::TraitRef(trait_ref) = tcx.hir().get(
|
||||
tcx.hir().parent_id(binding_id)
|
||||
) =>
|
||||
{
|
||||
let Some(trait_def_id) = trait_ref.trait_def_id() else {
|
||||
return tcx.ty_error_with_message(tcx.def_span(def_id), "Could not find trait");
|
||||
};
|
||||
let assoc_items = tcx.associated_items(trait_def_id);
|
||||
let assoc_item = assoc_items.find_by_name_and_kind(
|
||||
tcx, binding.ident, ty::AssocKind::Const, def_id.to_def_id(),
|
||||
);
|
||||
return if let Some(assoc_item) = assoc_item {
|
||||
tcx.type_of(assoc_item.def_id)
|
||||
.no_bound_vars()
|
||||
.expect("const parameter types cannot be generic")
|
||||
} else {
|
||||
// FIXME(associated_const_equality): add a useful error message here.
|
||||
tcx.ty_error_with_message(tcx.def_span(def_id), "Could not find associated const on trait")
|
||||
}
|
||||
}
|
||||
|
||||
// This match arm is for when the def_id appears in a GAT whose
|
||||
// path can't be resolved without typechecking e.g.
|
||||
//
|
||||
|
@ -86,11 +145,10 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
|
|||
(generics, arg_index)
|
||||
} else {
|
||||
// I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU
|
||||
tcx.sess.delay_span_bug(
|
||||
return tcx.ty_error_with_message(
|
||||
tcx.def_span(def_id),
|
||||
"unexpected non-GAT usage of an anon const",
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Node::Expr(&Expr {
|
||||
|
@ -103,7 +161,12 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
|
|||
// This may fail in case the method/path does not actually exist.
|
||||
// As there is no relevant param for `def_id`, we simply return
|
||||
// `None` here.
|
||||
let type_dependent_def = tables.type_dependent_def_id(parent_node_id)?;
|
||||
let Some(type_dependent_def) = tables.type_dependent_def_id(parent_node_id) else {
|
||||
return tcx.ty_error_with_message(
|
||||
tcx.def_span(def_id),
|
||||
&format!("unable to find type-dependent def for {:?}", parent_node_id),
|
||||
);
|
||||
};
|
||||
let idx = segment
|
||||
.args
|
||||
.and_then(|args| {
|
||||
|
@ -140,19 +203,17 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
|
|||
if let Some(path) = get_path_containing_arg_in_pat(pat, hir_id) {
|
||||
path
|
||||
} else {
|
||||
tcx.sess.delay_span_bug(
|
||||
return tcx.ty_error_with_message(
|
||||
tcx.def_span(def_id),
|
||||
&format!("unable to find const parent for {} in pat {:?}", hir_id, pat),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
tcx.sess.delay_span_bug(
|
||||
return tcx.ty_error_with_message(
|
||||
tcx.def_span(def_id),
|
||||
&format!("unexpected const parent path {:?}", parent_node),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -171,32 +232,34 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
|
|||
.position(|ct| ct.hir_id == hir_id)
|
||||
.map(|idx| (idx, seg)))
|
||||
}) else {
|
||||
tcx.sess.delay_span_bug(
|
||||
return tcx.ty_error_with_message(
|
||||
tcx.def_span(def_id),
|
||||
"no arg matching AnonConst in path",
|
||||
);
|
||||
return None;
|
||||
};
|
||||
|
||||
let generics = match tcx.res_generics_def_id(segment.res) {
|
||||
Some(def_id) => tcx.generics_of(def_id),
|
||||
None => {
|
||||
tcx.sess.delay_span_bug(
|
||||
return tcx.ty_error_with_message(
|
||||
tcx.def_span(def_id),
|
||||
&format!("unexpected anon const res {:?} in path: {:?}", segment.res, path),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
(generics, arg_index)
|
||||
}
|
||||
_ => return None,
|
||||
|
||||
_ => return tcx.ty_error_with_message(
|
||||
tcx.def_span(def_id),
|
||||
&format!("unexpected const parent in type_of(): {parent_node:?}"),
|
||||
),
|
||||
};
|
||||
|
||||
debug!(?parent_node);
|
||||
debug!(?generics, ?arg_idx);
|
||||
generics
|
||||
if let Some(param_def_id) = generics
|
||||
.params
|
||||
.iter()
|
||||
.filter(|param| param.kind.is_ty_or_const())
|
||||
|
@ -211,6 +274,14 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
{
|
||||
tcx.type_of(param_def_id).no_bound_vars().expect("const parameter types cannot be generic")
|
||||
} else {
|
||||
return tcx.ty_error_with_message(
|
||||
tcx.def_span(def_id),
|
||||
&format!("const generic parameter not found in {generics:?} at position {arg_idx:?}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_path_containing_arg_in_pat<'hir>(
|
||||
|
@ -415,143 +486,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
|
|||
tcx.typeck(def_id).node_type(hir_id)
|
||||
}
|
||||
|
||||
Node::AnonConst(_) if let Some(param) = tcx.opt_const_param_of(def_id) => {
|
||||
// We defer to `type_of` of the corresponding parameter
|
||||
// for generic arguments.
|
||||
tcx.type_of(param).subst_identity()
|
||||
}
|
||||
|
||||
Node::AnonConst(_) => {
|
||||
let parent_node = tcx.hir().get_parent(hir_id);
|
||||
match parent_node {
|
||||
Node::Ty(Ty { kind: TyKind::Array(_, constant), .. })
|
||||
| Node::Expr(Expr { kind: ExprKind::Repeat(_, constant), .. })
|
||||
if constant.hir_id() == hir_id =>
|
||||
{
|
||||
tcx.types.usize
|
||||
}
|
||||
Node::Ty(Ty { kind: TyKind::Typeof(e), .. }) if e.hir_id == hir_id => {
|
||||
tcx.typeck(def_id).node_type(e.hir_id)
|
||||
}
|
||||
|
||||
Node::Expr(Expr { kind: ExprKind::ConstBlock(anon_const), .. })
|
||||
if anon_const.hir_id == hir_id =>
|
||||
{
|
||||
let substs = InternalSubsts::identity_for_item(tcx, def_id);
|
||||
substs.as_inline_const().ty()
|
||||
}
|
||||
|
||||
Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
|
||||
| Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. })
|
||||
if asm.operands.iter().any(|(op, _op_sp)| match op {
|
||||
hir::InlineAsmOperand::Const { anon_const }
|
||||
| hir::InlineAsmOperand::SymFn { anon_const } => {
|
||||
anon_const.hir_id == hir_id
|
||||
}
|
||||
_ => false,
|
||||
}) =>
|
||||
{
|
||||
tcx.typeck(def_id).node_type(hir_id)
|
||||
}
|
||||
|
||||
Node::Variant(Variant { disr_expr: Some(e), .. }) if e.hir_id == hir_id => {
|
||||
tcx.adt_def(tcx.hir().get_parent_item(hir_id)).repr().discr_type().to_ty(tcx)
|
||||
}
|
||||
|
||||
Node::TypeBinding(TypeBinding {
|
||||
hir_id: binding_id,
|
||||
kind: TypeBindingKind::Equality { term: Term::Const(e) },
|
||||
ident,
|
||||
..
|
||||
}) if let Node::TraitRef(trait_ref) = tcx.hir().get_parent(*binding_id)
|
||||
&& e.hir_id == hir_id =>
|
||||
{
|
||||
let Some(trait_def_id) = trait_ref.trait_def_id() else {
|
||||
return ty::EarlyBinder(tcx.ty_error_with_message(DUMMY_SP, "Could not find trait"));
|
||||
};
|
||||
let assoc_items = tcx.associated_items(trait_def_id);
|
||||
let assoc_item = assoc_items.find_by_name_and_kind(
|
||||
tcx,
|
||||
*ident,
|
||||
ty::AssocKind::Const,
|
||||
def_id.to_def_id(),
|
||||
);
|
||||
if let Some(assoc_item) = assoc_item {
|
||||
tcx.type_of(assoc_item.def_id)
|
||||
.no_bound_vars()
|
||||
.expect("const parameter types cannot be generic")
|
||||
} else {
|
||||
// FIXME(associated_const_equality): add a useful error message here.
|
||||
tcx.ty_error_with_message(
|
||||
DUMMY_SP,
|
||||
"Could not find associated const on trait",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Node::TypeBinding(TypeBinding {
|
||||
hir_id: binding_id,
|
||||
gen_args,
|
||||
kind,
|
||||
ident,
|
||||
..
|
||||
}) if let Node::TraitRef(trait_ref) = tcx.hir().get_parent(*binding_id)
|
||||
&& let Some((idx, _)) =
|
||||
gen_args.args.iter().enumerate().find(|(_, arg)| {
|
||||
if let GenericArg::Const(ct) = arg {
|
||||
ct.value.hir_id == hir_id
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) =>
|
||||
{
|
||||
let Some(trait_def_id) = trait_ref.trait_def_id() else {
|
||||
return ty::EarlyBinder(tcx.ty_error_with_message(DUMMY_SP, "Could not find trait"));
|
||||
};
|
||||
let assoc_items = tcx.associated_items(trait_def_id);
|
||||
let assoc_item = assoc_items.find_by_name_and_kind(
|
||||
tcx,
|
||||
*ident,
|
||||
match kind {
|
||||
// I think `<A: T>` type bindings requires that `A` is a type
|
||||
TypeBindingKind::Constraint { .. }
|
||||
| TypeBindingKind::Equality { term: Term::Ty(..) } => {
|
||||
ty::AssocKind::Type
|
||||
}
|
||||
TypeBindingKind::Equality { term: Term::Const(..) } => {
|
||||
ty::AssocKind::Const
|
||||
}
|
||||
},
|
||||
def_id.to_def_id(),
|
||||
);
|
||||
if let Some(assoc_item) = assoc_item
|
||||
&& let param = &tcx.generics_of(assoc_item.def_id).params[idx]
|
||||
&& matches!(param.kind, ty::GenericParamDefKind::Const { .. })
|
||||
{
|
||||
tcx.type_of(param.def_id)
|
||||
.no_bound_vars()
|
||||
.expect("const parameter types cannot be generic")
|
||||
} else {
|
||||
// FIXME(associated_const_equality): add a useful error message here.
|
||||
tcx.ty_error_with_message(
|
||||
DUMMY_SP,
|
||||
"Could not find const param on associated item",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Node::GenericParam(&GenericParam {
|
||||
def_id: param_def_id,
|
||||
kind: GenericParamKind::Const { default: Some(ct), .. },
|
||||
..
|
||||
}) if ct.hir_id == hir_id => tcx.type_of(param_def_id).subst_identity(),
|
||||
|
||||
x => tcx.ty_error_with_message(
|
||||
DUMMY_SP,
|
||||
&format!("unexpected const parent in type_of(): {x:?}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
Node::AnonConst(_) => anon_const_type_of(tcx, def_id),
|
||||
|
||||
Node::GenericParam(param) => match ¶m.kind {
|
||||
GenericParamKind::Type { default: Some(ty), .. }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue