1
Fork 0

Rollup merge of #92875 - BoxyUwU:infer_arg_opt_const_param_of, r=lcnr

Make `opt_const_param_of` work in the presence of `GenericArg::Infer`

highly recommend viewing the first and second commits on their own rather than looking at file changes 🤣

Because we filtered args down to just const args we would ignore `GenericArg::Infer` which made us get a `arg_index` which was wrong by however many const `GenericArg::Infer` came previously

[example](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=46dba6a53aca6333028a10908ef16e0b) of the [bugs](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=a8eebced26eefa4119fc2e7ae0c76de6) fixed.

r? ```@lcnr```
This commit is contained in:
Matthias Krüger 2022-01-15 02:25:20 +01:00 committed by GitHub
commit 6c94f99d83
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 228 additions and 216 deletions

View file

@ -293,10 +293,6 @@ impl GenericArg<'_> {
} }
} }
pub fn is_const(&self) -> bool {
matches!(self, GenericArg::Const(_))
}
pub fn is_synthetic(&self) -> bool { pub fn is_synthetic(&self) -> bool {
matches!(self, GenericArg::Lifetime(lifetime) if lifetime.name.ident() == Ident::empty()) matches!(self, GenericArg::Lifetime(lifetime) if lifetime.name.ident() == Ident::empty())
} }
@ -318,6 +314,13 @@ impl GenericArg<'_> {
GenericArg::Infer(_) => ast::ParamKindOrd::Infer, GenericArg::Infer(_) => ast::ParamKindOrd::Infer,
} }
} }
pub fn is_ty_or_const(&self) -> bool {
match self {
GenericArg::Lifetime(_) => false,
GenericArg::Type(_) | GenericArg::Const(_) | GenericArg::Infer(_) => true,
}
}
} }
#[derive(Debug, HashStable_Generic)] #[derive(Debug, HashStable_Generic)]

View file

@ -31,6 +31,13 @@ impl GenericParamDefKind {
GenericParamDefKind::Const { .. } => ast::ParamKindOrd::Const, GenericParamDefKind::Const { .. } => ast::ParamKindOrd::Const,
} }
} }
pub fn is_ty_or_const(&self) -> bool {
match self {
GenericParamDefKind::Lifetime => false,
GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => true,
}
}
} }
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]

View file

@ -18,6 +18,7 @@ use super::{bad_placeholder, is_suggestable_infer_ty};
/// Computes the relevant generic parameter for a potential generic const argument. /// Computes the relevant generic parameter for a potential generic const argument.
/// ///
/// This should be called using the query `tcx.opt_const_param_of`. /// This should be called using the query `tcx.opt_const_param_of`.
#[instrument(level = "debug", skip(tcx))]
pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> { pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> {
// FIXME(generic_arg_infer): allow for returning DefIds of inference of // FIXME(generic_arg_infer): allow for returning DefIds of inference of
// GenericArg::Infer below. This may require a change where GenericArg::Infer has some flag // GenericArg::Infer below. This may require a change where GenericArg::Infer has some flag
@ -25,71 +26,69 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
use hir::*; use hir::*;
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
if let Node::AnonConst(_) = tcx.hir().get(hir_id) { match tcx.hir().get(hir_id) {
let parent_node_id = tcx.hir().get_parent_node(hir_id); Node::AnonConst(_) => (),
let parent_node = tcx.hir().get(parent_node_id); _ => return None,
};
match parent_node { let parent_node_id = tcx.hir().get_parent_node(hir_id);
// This match arm is for when the def_id appears in a GAT whose let parent_node = tcx.hir().get(parent_node_id);
// 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 typchecking 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 @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => {
// Find the Item containing the associated type so we can create an ItemCtxt.
// Using the ItemCtxt convert the HIR for the unresolved assoc type into a
// ty which is a fully resolved projection.
// For the code example above, this would mean converting Self::Assoc<3>
// into a ty::Projection(<Self as Foo>::Assoc<3>)
let item_hir_id = tcx
.hir()
.parent_iter(hir_id)
.filter(|(_, node)| matches!(node, Node::Item(_)))
.map(|(id, _)| id)
.next()
.unwrap();
let item_did = tcx.hir().local_def_id(item_hir_id).to_def_id();
let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>;
let ty = item_ctxt.ast_ty_to_ty(hir_ty);
// Iterate through the generics of the projection to find the one that corresponds to let (generics, arg_idx) = match parent_node {
// the def_id that this query was called with. We filter to only const args here as a // This match arm is for when the def_id appears in a GAT whose
// precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't // path can't be resolved without typechecking e.g.
// but it can't hurt to be safe ^^ //
if let ty::Projection(projection) = ty.kind() { // trait Foo {
let generics = tcx.generics_of(projection.item_def_id); // 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 typchecking 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 @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => {
// Find the Item containing the associated type so we can create an ItemCtxt.
// Using the ItemCtxt convert the HIR for the unresolved assoc type into a
// ty which is a fully resolved projection.
// For the code example above, this would mean converting Self::Assoc<3>
// into a ty::Projection(<Self as Foo>::Assoc<3>)
let item_hir_id = tcx
.hir()
.parent_iter(hir_id)
.filter(|(_, node)| matches!(node, Node::Item(_)))
.map(|(id, _)| id)
.next()
.unwrap();
let item_did = tcx.hir().local_def_id(item_hir_id).to_def_id();
let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>;
let ty = item_ctxt.ast_ty_to_ty(hir_ty);
let arg_index = segment // Iterate through the generics of the projection to find the one that corresponds to
.args // the def_id that this query was called with. We filter to only const args here as a
.and_then(|args| { // precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't
args.args // but it can't hurt to be safe ^^
.iter() if let ty::Projection(projection) = ty.kind() {
.filter(|arg| arg.is_const()) let generics = tcx.generics_of(projection.item_def_id);
.position(|arg| arg.id() == hir_id)
})
.unwrap_or_else(|| {
bug!("no arg matching AnonConst in segment");
});
return generics let arg_index = segment
.params .args
.iter() .and_then(|args| {
.filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. })) args.args
.nth(arg_index) .iter()
.map(|param| param.def_id); .filter(|arg| arg.is_ty_or_const())
} .position(|arg| arg.id() == 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 // I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU
tcx.sess.delay_span_bug( tcx.sess.delay_span_bug(
tcx.def_span(def_id), tcx.def_span(def_id),
@ -97,159 +96,158 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
); );
return None; return None;
} }
Node::Expr(&Expr { }
kind: Node::Expr(&Expr {
ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), kind:
.. ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)),
}) => { ..
let body_owner = tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); }) => {
let tables = tcx.typeck(body_owner); let body_owner = tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id));
// This may fail in case the method/path does not actually exist. let tables = tcx.typeck(body_owner);
// As there is no relevant param for `def_id`, we simply return // This may fail in case the method/path does not actually exist.
// `None` here. // As there is no relevant param for `def_id`, we simply return
let type_dependent_def = tables.type_dependent_def_id(parent_node_id)?; // `None` here.
let idx = segment let type_dependent_def = tables.type_dependent_def_id(parent_node_id)?;
.args let idx = segment
.and_then(|args| { .args
args.args .and_then(|args| {
.iter() args.args
.filter(|arg| arg.is_const()) .iter()
.position(|arg| arg.id() == hir_id) .filter(|arg| arg.is_ty_or_const())
}) .position(|arg| arg.id() == hir_id)
.unwrap_or_else(|| { })
bug!("no arg matching AnonConst in segment"); .unwrap_or_else(|| {
}); bug!("no arg matching AnonConst in segment");
});
tcx.generics_of(type_dependent_def) (tcx.generics_of(type_dependent_def), idx)
.params }
.iter()
.filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. }))
.nth(idx)
.map(|param| param.def_id)
}
Node::Ty(&Ty { kind: TyKind::Path(_), .. }) Node::Ty(&Ty { kind: TyKind::Path(_), .. })
| Node::Expr(&Expr { kind: ExprKind::Path(_) | ExprKind::Struct(..), .. }) | Node::Expr(&Expr { kind: ExprKind::Path(_) | ExprKind::Struct(..), .. })
| Node::TraitRef(..) | Node::TraitRef(..)
| Node::Pat(_) => { | Node::Pat(_) => {
let path = match parent_node { let path = match parent_node {
Node::Ty(&Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. }) Node::Ty(&Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. })
| Node::TraitRef(&TraitRef { path, .. }) => &*path, | Node::TraitRef(&TraitRef { path, .. }) => &*path,
Node::Expr(&Expr { Node::Expr(&Expr {
kind: kind:
ExprKind::Path(QPath::Resolved(_, path)) ExprKind::Path(QPath::Resolved(_, path))
| ExprKind::Struct(&QPath::Resolved(_, path), ..), | ExprKind::Struct(&QPath::Resolved(_, path), ..),
.. ..
}) => { }) => {
let body_owner = let body_owner = tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id));
tcx.hir().local_def_id(tcx.hir().enclosing_body_owner(hir_id)); let _tables = tcx.typeck(body_owner);
let _tables = tcx.typeck(body_owner); &*path
&*path }
} Node::Pat(pat) => {
Node::Pat(pat) => { if let Some(path) = get_path_containing_arg_in_pat(pat, hir_id) {
if let Some(path) = get_path_containing_arg_in_pat(pat, hir_id) { path
path } else {
} else {
tcx.sess.delay_span_bug(
tcx.def_span(def_id),
&format!(
"unable to find const parent for {} in pat {:?}",
hir_id, pat
),
);
return None;
}
}
_ => {
tcx.sess.delay_span_bug( tcx.sess.delay_span_bug(
tcx.def_span(def_id), tcx.def_span(def_id),
&format!("unexpected const parent path {:?}", parent_node), &format!("unable to find const parent for {} in pat {:?}", hir_id, pat),
); );
return None; return None;
} }
}; }
_ => {
tcx.sess.delay_span_bug(
tcx.def_span(def_id),
&format!("unexpected const parent path {:?}", parent_node),
);
return None;
}
};
// We've encountered an `AnonConst` in some path, so we need to // We've encountered an `AnonConst` in some path, so we need to
// figure out which generic parameter it corresponds to and return // figure out which generic parameter it corresponds to and return
// the relevant type. // the relevant type.
let filtered = path let filtered = path
.segments .segments
.iter() .iter()
.filter_map(|seg| seg.args.map(|args| (args.args, seg))) .filter_map(|seg| seg.args.map(|args| (args.args, seg)))
.find_map(|(args, seg)| { .find_map(|(args, seg)| {
args.iter() args.iter()
.filter(|arg| arg.is_const()) .filter(|arg| arg.is_ty_or_const())
.position(|arg| arg.id() == hir_id) .position(|arg| arg.id() == hir_id)
.map(|index| (index, seg)) .map(|index| (index, seg))
}); });
let (arg_index, segment) = match filtered { let (arg_index, segment) = match filtered {
None => { None => {
tcx.sess.delay_span_bug( tcx.sess
tcx.def_span(def_id), .delay_span_bug(tcx.def_span(def_id), "no arg matching AnonConst in path");
"no arg matching AnonConst in path", return None;
); }
return None; Some(inner) => inner,
} };
Some(inner) => inner,
};
// Try to use the segment resolution if it is valid, otherwise we // Try to use the segment resolution if it is valid, otherwise we
// default to the path resolution. // default to the path resolution.
let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res); let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res);
use def::CtorOf; use def::CtorOf;
let generics = match res { let generics = match res {
Res::Def(DefKind::Ctor(CtorOf::Variant, _), def_id) => tcx.generics_of( Res::Def(DefKind::Ctor(CtorOf::Variant, _), def_id) => tcx
tcx.parent(def_id).and_then(|def_id| tcx.parent(def_id)).unwrap(), .generics_of(tcx.parent(def_id).and_then(|def_id| tcx.parent(def_id)).unwrap()),
), Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Struct, _), def_id) => {
Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Struct, _), def_id) => { tcx.generics_of(tcx.parent(def_id).unwrap())
tcx.generics_of(tcx.parent(def_id).unwrap()) }
} // Other `DefKind`s don't have generics and would ICE when calling
// Other `DefKind`s don't have generics and would ICE when calling // `generics_of`.
// `generics_of`. Res::Def(
Res::Def( DefKind::Struct
DefKind::Struct | DefKind::Union
| DefKind::Union | DefKind::Enum
| DefKind::Enum | DefKind::Trait
| DefKind::Trait | DefKind::OpaqueTy
| DefKind::OpaqueTy | DefKind::TyAlias
| DefKind::TyAlias | DefKind::ForeignTy
| DefKind::ForeignTy | DefKind::TraitAlias
| DefKind::TraitAlias | DefKind::AssocTy
| DefKind::AssocTy | DefKind::Fn
| DefKind::Fn | DefKind::AssocFn
| DefKind::AssocFn | DefKind::AssocConst
| DefKind::AssocConst | DefKind::Impl,
| DefKind::Impl, def_id,
def_id, ) => tcx.generics_of(def_id),
) => tcx.generics_of(def_id), Res::Err => {
Res::Err => { tcx.sess.delay_span_bug(tcx.def_span(def_id), "anon const with Res::Err");
tcx.sess.delay_span_bug(tcx.def_span(def_id), "anon const with Res::Err"); return None;
return None; }
} _ => {
_ => { // If the user tries to specify generics on a type that does not take them,
// If the user tries to specify generics on a type that does not take them, // e.g. `usize<T>`, we may hit this branch, in which case we treat it as if
// e.g. `usize<T>`, we may hit this branch, in which case we treat it as if // no arguments have been passed. An error should already have been emitted.
// no arguments have been passed. An error should already have been emitted. tcx.sess.delay_span_bug(
tcx.sess.delay_span_bug( tcx.def_span(def_id),
tcx.def_span(def_id), &format!("unexpected anon const res {:?} in path: {:?}", res, path),
&format!("unexpected anon const res {:?} in path: {:?}", res, path), );
); return None;
return None; }
} };
};
generics (generics, arg_index)
.params }
.iter() _ => return None,
.filter(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. })) };
.nth(arg_index)
.map(|param| param.def_id) debug!(?parent_node);
debug!(?generics, ?arg_idx);
generics
.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, _ => None,
} })
} else {
None
}
} }
fn get_path_containing_arg_in_pat<'hir>( fn get_path_containing_arg_in_pat<'hir>(

View file

@ -0,0 +1,12 @@
// check-pass
#![feature(generic_arg_infer)]
struct Foo<const N: bool, const M: u8>;
struct Bar<const N: u8, const M: u32>;
fn main() {
let _: Foo<true, _> = Foo::<_, 1>;
let _: Foo<_, 1> = Foo::<true, _>;
let _: Bar<1, _> = Bar::<_, 300>;
let _: Bar<_, 300> = Bar::<1, _>;
}

View file

@ -4,13 +4,6 @@ error[E0770]: the type of const parameters must not depend on other generic para
LL | fn foo<const N: usize, const A: [u8; N]>() {} LL | fn foo<const N: usize, const A: [u8; N]>() {}
| ^ the type must not depend on the parameter `N` | ^ the type must not depend on the parameter `N`
error[E0308]: mismatched types error: aborting due to previous error
--> $DIR/issue-62878.rs:10:15
|
LL | foo::<_, {[1]}>();
| ^^^ expected `usize`, found array `[{integer}; 1]`
error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0770`.
Some errors have detailed explanations: E0308, E0770.
For more information about an error, try `rustc --explain E0308`.

View file

@ -7,6 +7,5 @@ fn foo<const N: usize, const A: [u8; N]>() {}
//[min]~| ERROR `[u8; _]` is forbidden as the type of a const generic parameter //[min]~| ERROR `[u8; _]` is forbidden as the type of a const generic parameter
fn main() { fn main() {
foo::<_, {[1]}>(); foo::<_, { [1] }>();
//[full]~^ ERROR mismatched types
} }