Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't try and handle unfed type_of on anon consts #133831

Merged
merged 2 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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)
}
243 changes: 8 additions & 235 deletions compiler/rustc_hir_analysis/src/collect/type_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion tests/ui/const-generics/issues/cg-in-dyn-issue-128176.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//@ check-pass

// Regression test for #128176.
// Regression test for #128176. Previously we would call `type_of` on the `1` anon const
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test being an example of the hir-based logic being wrong resulting in an ICE, which was fixed by no longer calling type_of before the anon const had been lowered

// before the anon const had been lowered and had the `type_of` fed with a result.

#![feature(generic_const_exprs)]
#![feature(dyn_compatible_for_dispatch)]
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/coroutine/coroutine-in-orphaned-anon-const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
trait X {
fn test() -> Self::Assoc<{ async {} }>;
//~^ ERROR associated type `Assoc` not found for `Self`
//~| ERROR associated type `Assoc` not found for `Self`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the "const args in unresolved paths will not have type_of fed", previously we would attempt to use the hir based logic which re-lowered the hir path resulting in a second error, now we just use TyKind::Error.


}

pub fn main() {}
10 changes: 1 addition & 9 deletions tests/ui/coroutine/coroutine-in-orphaned-anon-const.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@ error[E0220]: associated type `Assoc` not found for `Self`
LL | fn test() -> Self::Assoc<{ async {} }>;
| ^^^^^ associated type `Assoc` not found

error[E0220]: associated type `Assoc` not found for `Self`
--> $DIR/coroutine-in-orphaned-anon-const.rs:4:24
|
LL | fn test() -> Self::Assoc<{ async {} }>;
| ^^^^^ associated type `Assoc` not found
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error: aborting due to 2 previous errors
error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0220`.
2 changes: 0 additions & 2 deletions tests/ui/traits/bound/unknown-assoc-with-const-arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
trait X {
fn a<T>() -> T::unknown<{}> {}
//~^ ERROR: associated type `unknown` not found for `T`
//~| ERROR: associated type `unknown` not found for `T`
}

trait Y {
Expand All @@ -14,7 +13,6 @@ trait Y {
trait Z<T> {
fn a() -> T::unknown<{}> {}
//~^ ERROR: associated type `unknown` not found for `T`
//~| ERROR: associated type `unknown` not found for `T`
}

fn main() {}
22 changes: 3 additions & 19 deletions tests/ui/traits/bound/unknown-assoc-with-const-arg.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,18 @@ LL | fn a<T>() -> T::unknown<{}> {}
| ^^^^^^^ associated type `unknown` not found

error[E0220]: associated type `unknown` not found for `T`
--> $DIR/unknown-assoc-with-const-arg.rs:15:18
--> $DIR/unknown-assoc-with-const-arg.rs:14:18
|
LL | fn a() -> T::unknown<{}> {}
| ^^^^^^^ associated type `unknown` not found

error[E0220]: associated type `unknown` not found for `T`
--> $DIR/unknown-assoc-with-const-arg.rs:4:21
|
LL | fn a<T>() -> T::unknown<{}> {}
| ^^^^^^^ associated type `unknown` not found
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error[E0220]: associated type `unknown` not found for `T`
--> $DIR/unknown-assoc-with-const-arg.rs:15:18
|
LL | fn a() -> T::unknown<{}> {}
| ^^^^^^^ associated type `unknown` not found
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`

error[E0433]: failed to resolve: use of undeclared type `NOT_EXIST`
--> $DIR/unknown-assoc-with-const-arg.rs:10:15
--> $DIR/unknown-assoc-with-const-arg.rs:9:15
|
LL | fn a() -> NOT_EXIST::unknown<{}> {}
| ^^^^^^^^^ use of undeclared type `NOT_EXIST`

error: aborting due to 5 previous errors
error: aborting due to 3 previous errors

Some errors have detailed explanations: E0220, E0433.
For more information about an error, try `rustc --explain E0220`.
Loading