Skip to content

Commit

Permalink
Unrolled build for rust-lang#133831
Browse files Browse the repository at this point in the history
Rollup merge of rust-lang#133831 - BoxyUwU:ice_on_unfed_type_of, r=compiler-errors

Don't try and handle unfed `type_of` on anon consts

The `type_of` query for anon consts in the type system is actually implemented by feeding the return value during hir ty lowering, not the hir-based logic in `const_arg_anon_type_of`. The HIR based logic is incomplete (doesn't handle all hir nodes) and also generally wrong to call (re-lowers HIR or invokes typeck which can result in query cycles).

r? `@compiler-errors`
  • Loading branch information
rust-timer authored Dec 4, 2024
2 parents 96e51d9 + 52d1c30 commit b65a9d5
Show file tree
Hide file tree
Showing 7 changed files with 16 additions and 271 deletions.
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
// 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`

}

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`.

0 comments on commit b65a9d5

Please sign in to comment.