Skip to content
Closed
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
12 changes: 12 additions & 0 deletions compiler/rustc_hir_analysis/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,18 @@ pub(crate) struct ConstParamTyFieldVisMismatch {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag("cannot infer anonymous type parameter of `{$fn_name}`")]
pub(crate) struct ConstParamCannotInferAnonTypeParam {
#[primary_span]
#[label("cannot infer type `{$anon_param}`")]
pub span: Span,
pub fn_name: Ident,
pub anon_param: String,
#[label("anonymous type parameter declared here")]
pub fn_span: Option<Span>,
}

#[derive(Diagnostic)]
#[diag("at least one trait is required for an object type", code = E0224)]
pub(crate) struct TraitObjectDeclaredWithNoTraits {
Expand Down
63 changes: 46 additions & 17 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ use rustc_trait_selection::traits::{self, FulfillmentError};
use tracing::{debug, instrument};

use crate::check::check_abi;
use crate::diagnostics::{BadReturnTypeNotation, NoFieldOnType};
use crate::diagnostics::{
BadReturnTypeNotation, ConstParamCannotInferAnonTypeParam, NoFieldOnType,
};
use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
use crate::middle::resolve_bound_vars as rbv;
Expand Down Expand Up @@ -621,15 +623,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
span: Span,
def_id: DefId,
item_segment: &hir::PathSegment<'tcx>,
) -> GenericArgsRef<'tcx> {
let (args, _) = self.lower_generic_args_of_path(span, def_id, &[], item_segment, None);
) -> (GenericArgsRef<'tcx>, Option<DefId>) {
let (args, _, inferred_synth_args) =
self.lower_generic_args_of_path(span, def_id, &[], item_segment, None);
if let Some(c) = item_segment.args().constraints.first() {
prohibit_assoc_item_constraint(self, c, Some((def_id, item_segment, span)));
}
args
(args, inferred_synth_args)
}

/// Lower the generic arguments provided to some path.
/// Returns the `DefId` of the first inferred synthetic argument lowered to a type parameter.
///
/// If this is a trait reference, you also need to pass the self type `self_ty`.
/// The lowering process may involve applying defaulted type parameters.
Expand Down Expand Up @@ -671,7 +675,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
parent_args: &[ty::GenericArg<'tcx>],
segment: &hir::PathSegment<'tcx>,
self_ty: Option<Ty<'tcx>>,
) -> (GenericArgsRef<'tcx>, GenericArgCountResult) {
) -> (GenericArgsRef<'tcx>, GenericArgCountResult, Option<DefId>) {
// If the type is parameterized by this region, then replace this
// region with the current anon region binding (in other words,
// whatever & would get replaced with).
Expand Down Expand Up @@ -707,7 +711,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// here and so associated item constraints will be handled regardless of whether there are
// any non-`Self` generic parameters.
if generics.is_own_empty() {
return (tcx.mk_args(parent_args), arg_count);
return (tcx.mk_args(parent_args), arg_count, None);
}

struct GenericArgsCtxt<'a, 'tcx> {
Expand All @@ -716,6 +720,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
generic_args: &'a GenericArgs<'tcx>,
span: Span,
infer_args: bool,
inferred_synth_arg: Option<DefId>,
incorrect_args: &'a Result<(), GenericArgCountMismatch>,
}

Expand Down Expand Up @@ -829,6 +834,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.skip_norm_wip()
.into()
} else if synthetic {
// cannot infer type in most situations, but delegation needs it to be a
// type parameter, so return the param for if an error needs reporting
let _ = self.inferred_synth_arg.get_or_insert(param.def_id);
Ty::new_param(tcx, param.index, param.name).into()
} else if infer_args {
self.lowerer.ty_infer(Some(param), self.span).into()
Expand Down Expand Up @@ -868,6 +876,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
span,
generic_args: segment.args(),
infer_args: segment.infer_args,
inferred_synth_arg: None,
incorrect_args: &arg_count.correct,
};

Expand All @@ -881,7 +890,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
&mut args_ctx,
);

(args, arg_count)
let inferred_synth_arg = args_ctx.inferred_synth_arg;

(args, arg_count, inferred_synth_arg)
}

#[instrument(level = "debug", skip(self))]
Expand All @@ -892,7 +903,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
item_segment: &hir::PathSegment<'tcx>,
parent_args: GenericArgsRef<'tcx>,
) -> GenericArgsRef<'tcx> {
let (args, _) =
let (args, _, _) =
self.lower_generic_args_of_path(span, item_def_id, parent_args, item_segment, None);
if let Some(c) = item_segment.args().constraints.first() {
prohibit_assoc_item_constraint(self, c, Some((item_def_id, item_segment, span)));
Expand Down Expand Up @@ -999,7 +1010,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let _ = self.prohibit_generic_args(leading_segments.iter(), GenericsArgsErrExtend::None);
self.report_internal_fn_trait(span, trait_def_id, segment, false);

let (generic_args, arg_count) = self.lower_generic_args_of_path(
let (generic_args, arg_count, _) = self.lower_generic_args_of_path(
trait_ref.path.span,
trait_def_id,
&[],
Expand Down Expand Up @@ -1180,7 +1191,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
) -> ty::TraitRef<'tcx> {
self.report_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);

let (generic_args, _) =
let (generic_args, _, _) =
self.lower_generic_args_of_path(span, trait_def_id, &[], trait_segment, Some(self_ty));
if let Some(c) = trait_segment.args().constraints.first() {
prohibit_assoc_item_constraint(self, c, Some((trait_def_id, trait_segment, span)));
Expand All @@ -1207,7 +1218,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
item_segment: &hir::PathSegment<'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx();
let args = self.lower_generic_args_of_path_segment(span, def_id, item_segment);
let (args, _) = self.lower_generic_args_of_path_segment(span, def_id, item_segment);

if let DefKind::TyAlias = tcx.def_kind(def_id)
&& tcx.type_alias_is_lazy(def_id)
Expand Down Expand Up @@ -2117,7 +2128,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
leading_segments.iter(),
GenericsArgsErrExtend::OpaqueTy,
);
let args = self.lower_generic_args_of_path_segment(span, did, segment);
let (args, _) = self.lower_generic_args_of_path_segment(span, did, segment);
Ty::new_opaque(tcx, did, args)
}
Res::Def(
Expand Down Expand Up @@ -2742,7 +2753,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let [leading_segments @ .., segment] = path.segments else { bug!() };
let _ = self
.prohibit_generic_args(leading_segments.iter(), GenericsArgsErrExtend::None);
let args = self.lower_generic_args_of_path_segment(span, did, segment);
let (args, _) = self.lower_generic_args_of_path_segment(span, did, segment);
ty::Const::new_unevaluated(
tcx,
ty::UnevaluatedConst::new(
Expand Down Expand Up @@ -2770,7 +2781,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
CtorOf::Variant => tcx.parent(parent_did),
CtorOf::Struct => parent_did,
};
let args = self.lower_generic_args_of_path_segment(
let (args, _) = self.lower_generic_args_of_path_segment(
span,
generics_did,
&path.segments[generic_segments[0].1],
Expand Down Expand Up @@ -2801,7 +2812,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} else {
parent_did
};
let args = self.lower_generic_args_of_path_segment(
let (args, _) = self.lower_generic_args_of_path_segment(
span,
generics_did,
&path.segments[generic_segments[0].1],
Expand Down Expand Up @@ -2831,12 +2842,30 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// FIXME(const_generics): create real const to allow fn items as const paths
Res::Def(DefKind::Fn | DefKind::AssocFn, did) => {
self.dcx().span_delayed_bug(span, "function items cannot be used as const args");
let args = self.lower_generic_args_of_path_segment(
let (args, inferred_synth_arg) = self.lower_generic_args_of_path_segment(
span,
did,
path.segments.last().unwrap(),
);
ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args))
if let Some(param_id) = inferred_synth_arg {
let fn_name = path.segments.last().unwrap().ident;
let anon_param = tcx.def_path_str(param_id);
let fn_span = tcx.hir_span_if_local(did);

let e = tcx
.dcx()
.create_err(ConstParamCannotInferAnonTypeParam {
span,
fn_name,
anon_param,
fn_span,
})
.emit();

ty::Const::new_error(tcx, e)
Comment on lines +2850 to +2865

@fmease fmease Jun 23, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is a hyper specific band-aid solution to a lowering step that's simply incorrect (introduced in #155096) and only happens to work for delegation because of the way it gets lowered.

This function should not need to know which APITs were inferred uprooted.

Copy/pasting https://github.com/rust-lang/rust/pull/155096/changes#r3462851183:

+ } else if synthetic {
+     Ty::new_param(tcx, param.index, param.name).into()

This is not correct. It's injecting type parameters into a whole different context, it's uprooting a parameter from the def site and planting it at the use site (semi related: things like EarlyBinder solely exist to force people to think about this context change..).

I guess it only happens to work for delegation specifically because the generated delegate either reuses or has a copy of the generic parameters of the delegated function.

It's basically doing this:

fn f() {
    g();
    // ==>
    g::<T>(); // but `T` is not in scope!
}

fn g<T>() {}

View changes since the review

} else {
ty::Const::zero_sized(tcx, Ty::new_fn_def(tcx, did, args))
}
}

// Exhaustive match to be clear about what exactly we're considering to be
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Regression test for https://github.com/rust-lang/rust/issues/158152
//
// Using a function with an anonymous type parameter as a const parameter would ICE because the
// anonymous type parameter would be lowered to a type parameter when it should error if being used
// as a const generic argument, later it would fail an assertion in trait matching because it would
// try to instantiate with no provided arguments when the function has an anonymous type parameter

#![expect(incomplete_features)]
#![feature(min_generic_const_args)]

trait A<T> {}
trait Trait {}

impl A<[usize; fn_item]> for () {}
//~^ ERROR cannot infer anonymous type parameter of `fn_item`

fn fn_item(_: impl Trait) {}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error: cannot infer anonymous type parameter of `fn_item`
--> $DIR/unexpected_fn_item_with_anon_type_param.rs:14:16
|
LL | impl A<[usize; fn_item]> for () {}
| ^^^^^^^ cannot infer type `fn_item::impl Trait`
...
LL | fn fn_item(_: impl Trait) {}
| ------------------------- anonymous type parameter declared here

error: aborting due to 1 previous error

Loading