Skip to content
Open
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
34 changes: 29 additions & 5 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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();

Expand All @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl GenericParamDef {
}
}

#[derive(Default)]
#[derive(Debug, Default)]
pub struct GenericParamCount {
pub lifetimes: usize,
pub types: usize,
Expand Down
68 changes: 68 additions & 0 deletions tests/ui/lifetimes/turbofishing-invisible-lifetimes-154490.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
trait EvilTrait {
type EvilAssoc<'a>;
fn evil_assoc_1(_: Option<Self::EvilAssoc<'_>>) -> &i32 {
todo!()
}
}

// zero explicit generic lifetimes
fn evil_early_bound_1<T: EvilTrait>(_: Option<<T as EvilTrait>::EvilAssoc<'_>>) -> &i32 {
todo!()
}

fn evil_early_bound_2<T: EvilTrait>(_: Option<<T as EvilTrait>::EvilAssoc<'_>>) -> &i32 {
todo!()
}

fn evil_multi_bound_3<'b, T: EvilTrait>(
_: Option<<T as EvilTrait>::EvilAssoc<'_>>
) -> (&i32, &'b i64) {
todo!()
}

fn evil_early_bound_4<T: EvilTrait>(_: Option<<T as EvilTrait>::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<T: EvilTrait>() {
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::<T>(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]
<T as EvilTrait>::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() {}
82 changes: 82 additions & 0 deletions tests/ui/lifetimes/turbofishing-invisible-lifetimes-154490.stderr
Original file line number Diff line number Diff line change
@@ -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<T: EvilTrait>(_: Option<<T as EvilTrait>::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<T: EvilTrait>(_: Option<<T as EvilTrait>::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 | <T as EvilTrait>::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<Self::EvilAssoc<'_>>) -> &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`.
Loading