diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index 45c2ed205c74d..f5d582a8679db 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -399,7 +399,7 @@ pub fn check_generic_arg_count_for_value_path( /// Checks that the correct number of generic arguments have been provided. /// This is used both for datatypes and function calls. -#[instrument(skip(cx, gen_pos), level = "debug")] +#[instrument(skip(cx, gen_pos), level = "info")] pub(crate) fn check_generic_arg_count( cx: &dyn HirTyLowerer<'_>, def_id: DefId, @@ -409,6 +409,8 @@ pub(crate) fn check_generic_arg_count( has_self: bool, ) -> GenericArgCountResult { let gen_args = seg.args(); + let tcx = cx.tcx(); + let kind = tcx.def_kind(def_id); let default_counts = gen_params.own_defaults(); let param_counts = gen_params.own_counts(); @@ -430,7 +432,23 @@ pub(crate) fn check_generic_arg_count( prohibit_assoc_item_constraint(cx, c, None); } - let tcx = cx.tcx(); + // hidden lifetimes may not be specified explicitly. + // if it doesn't show up in the function signature, + // it can't be written as a lifetime arg. + // + // While most hidden lifetimes are late-bound (e.g. `fn(_: &u32)` ), + // there are some cases (complicated and involve associated types) + // where an early-bound lifetime parameter can be hidden from the function signature. + + let hidden_early_lifetimes = + gen_params.own_params.iter().filter(|x| x.is_anonymous_lifetime()).count(); + + debug!(?hidden_early_lifetimes); + + if kind.is_fn_like() { + debug!("we are using fn-like {:?} ({gen_pos:?})", tcx.item_name(def_id)); + } + debug!("# gen_args = {}", gen_args.args.len()); // Suppress this warning for delegations as it is compiler generated and lifetimes are // propagated while late-bound lifetimes may be present. @@ -476,9 +494,15 @@ pub(crate) fn check_generic_arg_count( Err(reported) }; - let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes }; - let max_expected_lifetime_args = param_counts.lifetimes; + let min_expected_lifetime_args = + if infer_lifetimes { 0 } else { param_counts.lifetimes - hidden_early_lifetimes }; + debug!(?min_expected_lifetime_args); + + let max_expected_lifetime_args = param_counts.lifetimes - hidden_early_lifetimes; + debug!(?max_expected_lifetime_args); + let num_provided_lifetime_args = gen_args.num_lifetime_args(); + debug!(?num_provided_lifetime_args); let lifetimes_correct = check_lifetime_args( min_expected_lifetime_args, @@ -542,7 +566,7 @@ pub(crate) fn check_generic_arg_count( .map(|param| param.name) .collect(); if constraint_names == param_names { - let has_assoc_ty_with_same_name = if let DefKind::Trait = tcx.def_kind(def_id) { + let has_assoc_ty_with_same_name = if let DefKind::Trait = kind { gen_args.constraints.iter().any(|constraint| { traits::supertrait_def_ids(tcx, def_id).any(|trait_did| { cx.probe_trait_that_defines_assoc_item( diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 0599f51305575..f1a65010ff688 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -105,7 +105,7 @@ impl GenericParamDef { } } -#[derive(Default)] +#[derive(Debug, Default)] pub struct GenericParamCount { pub lifetimes: usize, pub types: usize, diff --git a/tests/ui/lifetimes/turbofishing-invisible-lifetimes-154490.rs b/tests/ui/lifetimes/turbofishing-invisible-lifetimes-154490.rs new file mode 100644 index 0000000000000..76d7626ccbf37 --- /dev/null +++ b/tests/ui/lifetimes/turbofishing-invisible-lifetimes-154490.rs @@ -0,0 +1,68 @@ +trait EvilTrait { + type EvilAssoc<'a>; + fn evil_assoc_1(_: Option>) -> &i32 { + todo!() + } +} + +// zero explicit generic lifetimes +fn evil_early_bound_1(_: Option<::EvilAssoc<'_>>) -> &i32 { + todo!() +} + +fn evil_early_bound_2(_: Option<::EvilAssoc<'_>>) -> &i32 { + todo!() +} + +fn evil_multi_bound_3<'b, T: EvilTrait>( + _: Option<::EvilAssoc<'_>> +) -> (&i32, &'b i64) { + todo!() +} + +fn evil_early_bound_4(_: Option<::EvilAssoc<'_>>) -> i32 { + todo!() +} + +fn normal_early_bound<'eb: 'eb, T: EvilTrait>(_: &'eb i32 ) -> &'eb i32 { + todo!() +} + +fn normal_late_bound<'lb, T: EvilTrait>(_: &'lb i32 ) -> &'lb i32 { + todo!() +} + +struct LtWrapper<'a>(&'a i32); + +fn elide_struct_1(_: &i32) -> LtWrapper { + todo!() +} + +fn elide_struct_2(_: &i32) -> LtWrapper { + todo!() +} + +fn foo() { + static WHATEVER: i32 = 123; + + evil_early_bound_1::<'static, T>(None); + //~^ ERROR: function takes 0 lifetime arguments but 1 lifetime argument was supplied [E0107] + // ^ FIXME: should this have a better diagnostic? + evil_early_bound_2::(None); + evil_multi_bound_3::<'static, 'static, T>(None); + //~^ ERROR: function takes 1 lifetime argument but 2 lifetime arguments were supplied [E0107] + evil_early_bound_4::<'static, T>(None); + //~^ ERROR: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present [E0794] + ::evil_assoc_1::<'static>(None); + //~^ ERROR: associated function takes 0 lifetime arguments but 1 lifetime argument was supplied [E0107] + elide_struct_1(&WHATEVER); + elide_struct_2::<'static>(&WHATEVER); + //~^ ERROR: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present [E0794] + normal_early_bound::<'static, T>(&WHATEVER); + // ^ this is fine + normal_late_bound::<'static, T>(&WHATEVER); + //~^ ERROR: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present [E0794] +} + + +fn main() {} diff --git a/tests/ui/lifetimes/turbofishing-invisible-lifetimes-154490.stderr b/tests/ui/lifetimes/turbofishing-invisible-lifetimes-154490.stderr new file mode 100644 index 0000000000000..aa4bc1e5f0d1b --- /dev/null +++ b/tests/ui/lifetimes/turbofishing-invisible-lifetimes-154490.stderr @@ -0,0 +1,82 @@ +error[E0107]: function takes 0 lifetime arguments but 1 lifetime argument was supplied + --> $DIR/turbofishing-invisible-lifetimes-154490.rs:48:5 + | +LL | evil_early_bound_1::<'static, T>(None); + | ^^^^^^^^^^^^^^^^^^ ------- help: remove the lifetime argument + | | + | expected 0 lifetime arguments + | +note: function defined here, with 0 lifetime parameters + --> $DIR/turbofishing-invisible-lifetimes-154490.rs:9:4 + | +LL | fn evil_early_bound_1(_: Option<::EvilAssoc<'_>>) -> &i32 { + | ^^^^^^^^^^^^^^^^^^ + +error[E0107]: function takes 1 lifetime argument but 2 lifetime arguments were supplied + --> $DIR/turbofishing-invisible-lifetimes-154490.rs:52:5 + | +LL | evil_multi_bound_3::<'static, 'static, T>(None); + | ^^^^^^^^^^^^^^^^^^ --------- help: remove the lifetime argument + | | + | expected 1 lifetime argument + | +note: function defined here, with 1 lifetime parameter: `'b` + --> $DIR/turbofishing-invisible-lifetimes-154490.rs:17:4 + | +LL | fn evil_multi_bound_3<'b, T: EvilTrait>( + | ^^^^^^^^^^^^^^^^^^ -- + +error[E0794]: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/turbofishing-invisible-lifetimes-154490.rs:54:26 + | +LL | evil_early_bound_4::<'static, T>(None); + | ^^^^^^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/turbofishing-invisible-lifetimes-154490.rs:23:75 + | +LL | fn evil_early_bound_4(_: Option<::EvilAssoc<'_>>) -> i32 { + | ^^ + +error[E0107]: associated function takes 0 lifetime arguments but 1 lifetime argument was supplied + --> $DIR/turbofishing-invisible-lifetimes-154490.rs:56:23 + | +LL | ::evil_assoc_1::<'static>(None); + | ^^^^^^^^^^^^----------- help: remove the unnecessary generics + | | + | expected 0 lifetime arguments + | +note: associated function defined here, with 0 lifetime parameters + --> $DIR/turbofishing-invisible-lifetimes-154490.rs:3:8 + | +LL | fn evil_assoc_1(_: Option>) -> &i32 { + | ^^^^^^^^^^^^ + +error[E0794]: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/turbofishing-invisible-lifetimes-154490.rs:59:22 + | +LL | elide_struct_2::<'static>(&WHATEVER); + | ^^^^^^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/turbofishing-invisible-lifetimes-154490.rs:41:22 + | +LL | fn elide_struct_2(_: &i32) -> LtWrapper { + | ^ + +error[E0794]: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present + --> $DIR/turbofishing-invisible-lifetimes-154490.rs:63:25 + | +LL | normal_late_bound::<'static, T>(&WHATEVER); + | ^^^^^^^ + | +note: the late bound lifetime parameter is introduced here + --> $DIR/turbofishing-invisible-lifetimes-154490.rs:31:22 + | +LL | fn normal_late_bound<'lb, T: EvilTrait>(_: &'lb i32 ) -> &'lb i32 { + | ^^^ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0107, E0794. +For more information about an error, try `rustc --explain E0107`.