Don't try and handle unfed type_of on anon consts

This commit is contained in:
Boxy 2024-11-30 19:31:06 +00:00
parent c44b3d50fe
commit ec036cda3f
6 changed files with 14 additions and 270 deletions

View file

@ -309,10 +309,10 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
self.tcx.ensure().type_of(param.def_id);
if let Some(default) = default {
// need to store default and type of default
self.tcx.ensure().const_param_default(param.def_id);
if let hir::ConstArgKind::Anon(ac) = default.kind {
self.tcx.ensure().type_of(ac.def_id);
}
self.tcx.ensure().const_param_default(param.def_id);
}
}
}
@ -1817,7 +1817,6 @@ fn const_param_default<'tcx>(
),
};
let icx = ItemCtxt::new(tcx, def_id);
// FIXME(const_generics): investigate which places do and don't need const ty feeding
let ct = icx.lowerer().lower_const_arg(default_ct, FeedConstTy::No);
let ct = icx.lowerer().lower_const_arg(default_ct, FeedConstTy::Param(def_id.to_def_id()));
ty::EarlyBinder::bind(ct)
}

View file

@ -12,7 +12,6 @@ use rustc_middle::ty::{self, Article, IsSuggestable, Ty, TyCtxt, TypeVisitableEx
use rustc_middle::{bug, span_bug};
use rustc_span::symbol::Ident;
use rustc_span::{DUMMY_SP, Span};
use tracing::debug;
use super::{ItemCtxt, bad_placeholder};
use crate::errors::TypeofReservedKeywordUsed;
@ -138,252 +137,26 @@ fn const_arg_anon_type_of<'tcx>(tcx: TyCtxt<'tcx>, arg_hir_id: HirId, span: Span
use hir::*;
use rustc_middle::ty::Ty;
let parent_node_id = tcx.parent_hir_id(arg_hir_id);
let parent_node = tcx.hir_node(parent_node_id);
let (generics, arg_idx) = match parent_node {
// Easy case: arrays repeat expressions.
match tcx.parent_hir_node(arg_hir_id) {
// Array length const arguments do not have `type_of` fed as there is never a corresponding
// generic parameter definition.
Node::Ty(&hir::Ty { kind: TyKind::Array(_, ref constant), .. })
| Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
if constant.hir_id == arg_hir_id =>
{
return tcx.types.usize;
}
Node::GenericParam(&GenericParam {
def_id: param_def_id,
kind: GenericParamKind::Const { default: Some(ct), .. },
..
}) if ct.hir_id == arg_hir_id => {
return tcx
.type_of(param_def_id)
.no_bound_vars()
.expect("const parameter types cannot be generic");
}
// This match arm is for when the def_id appears in a GAT whose
// path can't be resolved without typechecking e.g.
//
// trait Foo {
// type Assoc<const N: usize>;
// fn foo() -> Self::Assoc<3>;
// }
//
// In the above code we would call this query with the def_id of 3 and
// the parent_node we match on would be the hir node for Self::Assoc<3>
//
// `Self::Assoc<3>` cant be resolved without typechecking here as we
// didnt write <Self as Foo>::Assoc<3>. If we did then another match
// arm would handle this.
//
// I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU
Node::Ty(hir_ty @ hir::Ty { kind: TyKind::Path(QPath::TypeRelative(ty, segment)), .. }) => {
// Find the Item containing the associated type so we can create an ItemCtxt.
// Using the ItemCtxt lower the HIR for the unresolved assoc type into a
// ty which is a fully resolved projection.
// For the code example above, this would mean lowering `Self::Assoc<3>`
// to a ty::Alias(ty::Projection, `<Self as Foo>::Assoc<3>`).
let item_def_id = tcx.hir().get_parent_item(ty.hir_id).def_id;
let ty = ItemCtxt::new(tcx, item_def_id).lower_ty(hir_ty);
// Iterate through the generics of the projection to find the one that corresponds to
// the def_id that this query was called with. We filter to only type and const args here
// as a precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't
// but it can't hurt to be safe ^^
if let ty::Alias(ty::Projection | ty::Inherent, projection) = ty.kind() {
let generics = tcx.generics_of(projection.def_id);
let arg_index = segment
.args
.and_then(|args| {
args.args
.iter()
.filter(|arg| arg.is_ty_or_const())
.position(|arg| arg.hir_id() == arg_hir_id)
})
.unwrap_or_else(|| {
bug!("no arg matching AnonConst in segment");
});
(generics, arg_index)
} else {
// I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU
return Ty::new_error_with_message(
tcx,
span,
"unexpected non-GAT usage of an anon const",
);
}
}
Node::Expr(&Expr {
kind:
ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)),
..
}) => {
let body_owner = tcx.hir().enclosing_body_owner(arg_hir_id);
let tables = tcx.typeck(body_owner);
// 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 Some(type_dependent_def) = tables.type_dependent_def_id(parent_node_id) else {
return Ty::new_error_with_message(
tcx,
span,
format!("unable to find type-dependent def for {parent_node_id:?}"),
);
};
let idx = segment
.args
.and_then(|args| {
args.args
.iter()
.filter(|arg| arg.is_ty_or_const())
.position(|arg| arg.hir_id() == arg_hir_id)
})
.unwrap_or_else(|| {
bug!("no arg matching ConstArg in segment");
});
(tcx.generics_of(type_dependent_def), idx)
}
Node::Ty(&hir::Ty { kind: TyKind::Path(_), .. })
| Node::Expr(&Expr { kind: ExprKind::Path(_) | ExprKind::Struct(..), .. })
| Node::TraitRef(..)
| Node::Pat(_) => {
let path = match parent_node {
Node::Ty(&hir::Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. })
| Node::TraitRef(&TraitRef { path, .. }) => &*path,
Node::Expr(&Expr {
kind:
ExprKind::Path(QPath::Resolved(_, path))
| ExprKind::Struct(&QPath::Resolved(_, path), ..),
..
}) => {
let body_owner = tcx.hir().enclosing_body_owner(arg_hir_id);
let _tables = tcx.typeck(body_owner);
&*path
}
Node::Pat(pat) => {
if let Some(path) = get_path_containing_arg_in_pat(pat, arg_hir_id) {
path
} else {
return Ty::new_error_with_message(
tcx,
span,
format!("unable to find const parent for {arg_hir_id} in pat {pat:?}"),
);
}
}
_ => {
return Ty::new_error_with_message(
tcx,
span,
format!("unexpected const parent path {parent_node:?}"),
);
}
};
// We've encountered an `AnonConst` in some path, so we need to
// figure out which generic parameter it corresponds to and return
// the relevant type.
let Some((arg_index, segment)) = path.segments.iter().find_map(|seg| {
let args = seg.args?;
args.args
.iter()
.filter(|arg| arg.is_ty_or_const())
.position(|arg| arg.hir_id() == arg_hir_id)
.map(|index| (index, seg))
.or_else(|| {
args.constraints
.iter()
.copied()
.filter_map(AssocItemConstraint::ct)
.position(|ct| ct.hir_id == arg_hir_id)
.map(|idx| (idx, seg))
})
}) else {
return Ty::new_error_with_message(tcx, span, "no arg matching AnonConst in path");
};
let generics = match tcx.res_generics_def_id(segment.res) {
Some(def_id) => tcx.generics_of(def_id),
None => {
return Ty::new_error_with_message(
tcx,
span,
format!("unexpected anon const res {:?} in path: {:?}", segment.res, path),
);
}
};
(generics, arg_index)
}
_ => {
return Ty::new_error_with_message(
tcx,
span,
format!("unexpected const arg parent in type_of(): {parent_node:?}"),
);
}
};
debug!(?parent_node);
debug!(?generics, ?arg_idx);
if let Some(param_def_id) = generics
.own_params
.iter()
.filter(|param| param.kind.is_ty_or_const())
.nth(match generics.has_self && generics.parent.is_none() {
true => arg_idx + 1,
false => arg_idx,
})
.and_then(|param| match param.kind {
ty::GenericParamDefKind::Const { .. } => {
debug!(?param);
Some(param.def_id)
}
_ => None,
})
{
tcx.type_of(param_def_id).no_bound_vars().expect("const parameter types cannot be generic")
} else {
return Ty::new_error_with_message(
// This is not a `bug!` as const arguments in path segments that did not resolve to anything
// will result in `type_of` never being fed.
_ => Ty::new_error_with_message(
tcx,
span,
format!("const generic parameter not found in {generics:?} at position {arg_idx:?}"),
);
"`type_of` called on const argument's anon const before the const argument was lowered",
),
}
}
fn get_path_containing_arg_in_pat<'hir>(
pat: &'hir hir::Pat<'hir>,
arg_id: HirId,
) -> Option<&'hir hir::Path<'hir>> {
use hir::*;
let is_arg_in_path = |p: &hir::Path<'_>| {
p.segments
.iter()
.filter_map(|seg| seg.args)
.flat_map(|args| args.args)
.any(|arg| arg.hir_id() == arg_id)
};
let mut arg_path = None;
pat.walk(|pat| match pat.kind {
PatKind::Struct(QPath::Resolved(_, path), _, _)
| PatKind::TupleStruct(QPath::Resolved(_, path), _, _)
| PatKind::Path(QPath::Resolved(_, path))
if is_arg_in_path(path) =>
{
arg_path = Some(path);
false
}
_ => true,
});
arg_path
}
pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, Ty<'_>> {
use rustc_hir::*;
use rustc_middle::ty::Ty;