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
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,8 @@ declare_features! (
(unstable, lahfsahf_target_feature, "1.78.0", Some(150251)),
/// Allows setting the threshold for the `large_assignments` lint.
(unstable, large_assignments, "1.52.0", Some(83518)),
/// Allow late-bound lifetimes to be specified explicitly using turbofish syntax.
(unstable, late_bound_turbofishing, "1.98.0", Some(156581)),
/// Allows using `#[link(kind = "link-arg", name = "...")]`
/// to pass custom arguments to the linker.
(unstable, link_arg_attribute, "1.76.0", Some(99427)),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,7 @@ pub enum GenericParamKind<'hir> {
/// A lifetime definition (e.g., `'a: 'b + 'c + 'd`).
Lifetime {
kind: LifetimeParamKind,
// FIXME(addiesh): add late_bound: bool,
},
Type {
default: Option<&'hir Ty<'hir>>,
Expand Down
61 changes: 46 additions & 15 deletions compiler/rustc_hir_analysis/src/collect/generics_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
parent: Some(trait_def_id),
parent_count,
own_params,
own_lifetime_params: opaque_ty_generics.own_lifetime_params.clone(),
param_def_id_to_index,
has_self: opaque_ty_generics.has_self,
has_late_bound_regions: opaque_ty_generics.has_late_bound_regions,
own_late_bound_regions: opaque_ty_generics.own_late_bound_regions.clone(),
};
}

Expand Down Expand Up @@ -155,9 +156,10 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
parent: generics.parent,
parent_count: generics.parent_count,
own_params,
own_lifetime_params: generics.own_lifetime_params.clone(),
param_def_id_to_index,
has_self: generics.has_self,
has_late_bound_regions: generics.has_late_bound_regions,
own_late_bound_regions: generics.own_late_bound_regions.clone(),
};
}
ty::AnonConstKind::GCE => Some(parent_did),
Expand Down Expand Up @@ -269,6 +271,22 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
own_params.push(opt_self);
}

let own_lifetime_params = hir_generics
.params
.iter()
.enumerate()
.filter_map(|(i, param)| match param.kind {
GenericParamKind::Lifetime { .. } => Some(ty::GenericParamDef {
name: param.name.ident().name,
index: own_start + i as u32,
def_id: param.def_id.to_def_id(),
pure_wrt_drop: param.pure_wrt_drop,
kind: ty::GenericParamDefKind::Lifetime,
}),
_ => None,
})
.collect::<Vec<_>>();

let early_lifetimes = super::early_bound_lifetimes_from_generics(tcx, hir_generics);
own_params.extend(early_lifetimes.enumerate().map(|(i, param)| ty::GenericParamDef {
name: param.name.ident().name,
Expand Down Expand Up @@ -394,9 +412,10 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
parent: parent_def_id.map(LocalDefId::to_def_id),
parent_count,
own_params,
own_lifetime_params,
param_def_id_to_index,
has_self: has_self || parent_has_self,
has_late_bound_regions: has_late_bound_regions(tcx, node),
own_late_bound_regions: late_bound_regions(tcx, node),
}
}

Expand Down Expand Up @@ -442,7 +461,7 @@ fn param_default_policy(node: Node<'_>) -> Option<ParamDefaultPolicy> {
})
}

fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<Span> {
fn late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Vec<Span> {
struct LateBoundRegionsDetector<'tcx> {
tcx: TyCtxt<'tcx>,
outer_index: ty::DebruijnIndex,
Expand Down Expand Up @@ -495,25 +514,37 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
}
}

fn has_late_bound_regions<'tcx>(
fn late_bound_regions<'tcx>(
tcx: TyCtxt<'tcx>,
generics: &'tcx hir::Generics<'tcx>,
decl: &'tcx hir::FnDecl<'tcx>,
) -> Option<Span> {
) -> Vec<Span> {
let mut visitor = LateBoundRegionsDetector { tcx, outer_index: ty::INNERMOST };
for param in generics.params {
if let GenericParamKind::Lifetime { .. } = param.kind {
if tcx.is_late_bound(param.hir_id) {
return Some(param.span);

let spans = generics
.params
.iter()
.flat_map(|param| {
if let GenericParamKind::Lifetime { .. } = param.kind
&& tcx.is_late_bound(param.hir_id)
{
Some(param.span)
} else {
None
}
}
})
.collect::<Vec<_>>();

if !spans.is_empty() {
spans
} else {
visitor.visit_fn_decl(decl).break_value().map_or_default(|val| vec![val])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

wouldn't we generally want to append these to spans?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I thought so, but it turns out that doing it unconditionally causes cycles in a bunch of places...

error[E0391]: cycle detected when computing generics of `IntFactory::stream`
  --> /home/addie/rust/tests/ui/parallel-rustc/fn-sig-cycle-ice-154560.rs:9:5
   |
LL |     fn stream(&self) -> impl IntFactory<stream(..): Send>;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: ...which requires looking up a named region inside `IntFactory::stream`...
   = note: ...which requires resolving lifetimes for `IntFactory::stream`...
   = note: ...which again requires computing generics of `IntFactory::stream`, completing the cycle
note: cycle used when computing generics of `IntFactory::stream::{opaque#0}`

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ok... apparently it also just... Breaks the late-bound var checking too. why does this tiny, almost one-liner change make Literally Everything Explode??

}
visitor.visit_fn_decl(decl).break_value()
}

let decl = node.fn_decl()?;
let generics = node.generics()?;
has_late_bound_regions(tcx, generics, decl)
let Some(decl) = node.fn_decl() else { return vec![] };
let Some(generics) = node.generics() else { return vec![] };
late_bound_regions(tcx, generics, decl)
}

struct AnonConstInParamTyDetector {
Expand Down
69 changes: 59 additions & 10 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,33 @@ 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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

name a test in this comment that shows this

//
// see: <tests/ui/lifetimes/turbofishing-invisible-lifetimes-154490.rs>

let hidden_early_lifetimes =
gen_params.own_params.iter().filter(|x| x.is_anonymous_lifetime()).count();

let hidden_late_lifetimes =
gen_params.own_lifetime_params.iter().filter(|x| x.is_anonymous_lifetime()).count()
- hidden_early_lifetimes;

debug!(?hidden_early_lifetimes, ?hidden_late_lifetimes);

let late_bound_lt_count = gen_params.own_late_bound_regions.len();

if kind.is_fn_like() {
debug!("we are using fn-like {:?} ({gen_pos:?})", tcx.item_name(def_id));
}
debug!(?late_bound_lt_count);
debug!("gen_args count = {}", gen_args.args.len());
debug!("lb+eb lifetimes={:?}", gen_params.own_lifetime_params);

// Suppress this warning for delegations as it is compiler generated and lifetimes are
// propagated while late-bound lifetimes may be present.
Expand All @@ -449,7 +477,7 @@ pub(crate) fn check_generic_arg_count(
return Ok(());
}

if late_bounds_ignore {
if late_bounds_ignore && !tcx.features().late_bound_turbofishing() {
return Ok(());
}

Expand All @@ -476,9 +504,22 @@ 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 mut max_expected_lifetime_args = param_counts.lifetimes - hidden_early_lifetimes;

// FIXME: under certain circumstances (which I have had trouble replicating)
// this leads to subtraction with overflow
if tcx.features().late_bound_turbofishing() {
max_expected_lifetime_args =
max_expected_lifetime_args + late_bound_lt_count - hidden_late_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 +583,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 Expand Up @@ -633,20 +674,27 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes(
) -> ExplicitLateBound {
struct LifetimeArgsIssue {
msg: &'static str,
note: &'static str,
}

impl<'a> Diagnostic<'a, ()> for LifetimeArgsIssue {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
let Self { msg } = self;
Diag::new(dcx, level, msg)
let Self { msg, note } = self;
Diag::new(dcx, level, msg).with_note(note)
}
}

let param_counts = def.own_counts();

if let Some(span_late) = def.has_late_bound_regions
// FIXME(addiesh): just turning off the diagnostic is probably not enough to solve the problem. see:
// https://rust-lang.zulipchat.com/#narrow/channel/600108-t-types.2Fearly-late-cleanup/topic/turbofishing.20elided.20lifetimes/near/607748866
if cx.tcx().features().late_bound_turbofishing() {
ExplicitLateBound::Yes
} else if let Some(span_late) = def.own_late_bound_regions.first().copied()
&& args.has_lifetime_args()
{
let gone_turbofishing = "this may change in the future; see issue #156581 <https://github.com/rust-lang/rust/issues/156581> for more information";

let msg = "cannot specify lifetime arguments explicitly \
if late bound lifetime parameters are present";
let note = "the late bound lifetime parameter is introduced here";
Expand All @@ -657,6 +705,7 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes(
{
struct_span_code_err!(cx.dcx(), span, E0794, "{}", msg)
.with_span_note(span_late, note)
.with_note(gone_turbofishing)
.emit();
} else {
let mut multispan = MultiSpan::from_span(span);
Expand All @@ -665,7 +714,7 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes(
LATE_BOUND_LIFETIME_ARGUMENTS,
args.args[0].hir_id(),
multispan,
LifetimeArgsIssue { msg },
LifetimeArgsIssue { msg, note: gone_turbofishing },
);
}

Expand Down
22 changes: 19 additions & 3 deletions 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 All @@ -121,14 +121,30 @@ pub struct GenericParamCount {
pub struct Generics {
pub parent: Option<DefId>,
pub parent_count: usize,
// FIXME: eventually we should probably include
// late-bound lifetimes in ty::Generics.
// LBLTs _are still lifetimes_ and
// I think it's kind of weird how hard it is
// to look them up without access to HIR.
/// The generic params of the item/method (excluding late-bound lifetimes)
pub own_params: Vec<GenericParamDef>,

/// **You probably want to use `own_params` instead.**
///
/// Contains all lifetime parameters
/// _(including late-bound lifetimes)_
/// on the item/method.
/// This is only used for behavior
/// involving late-bound lifetimes and will probably be
/// removed entirely in the future.
pub own_lifetime_params: Vec<GenericParamDef>,

/// Reverse map to the `index` field of each `GenericParamDef`.
#[stable_hash(ignore)]
pub param_def_id_to_index: FxHashMap<DefId, u32>,

pub has_self: bool,
pub has_late_bound_regions: Option<Span>,
pub own_late_bound_regions: Vec<Span>,
}

impl std::fmt::Debug for Generics {
Expand All @@ -143,7 +159,7 @@ impl std::fmt::Debug for Generics {
.field("own_params", &self.own_params)
.field("param_def_id_to_index", &stabilized_hashmap)
.field("has_self", &self.has_self)
.field("has_late_bound_regions", &self.has_late_bound_regions)
.field("own_late_bound_regions", &self.own_late_bound_regions)
.finish()
}
}
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_public/src/unstable/convert/stable/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -681,8 +681,10 @@ impl<'tcx> Stable<'tcx> for ty::Generics {
params,
param_def_id_to_index,
has_self: self.has_self,
// FIXME: this type def has not been updated in rustc public
has_late_bound_regions: self
.has_late_bound_regions
.own_late_bound_regions
.first()
.as_ref()
.map(|late_bound_regions| late_bound_regions.stable(tables, cx)),
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_ty_utils/src/assoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,9 +356,10 @@ fn associated_type_for_impl_trait_in_impl(
parent: Some(impl_local_def_id.to_def_id()),
parent_count,
own_params,
own_lifetime_params: trait_assoc_generics.own_lifetime_params.clone(),
param_def_id_to_index,
has_self: false,
has_late_bound_regions: trait_assoc_generics.has_late_bound_regions,
own_late_bound_regions: trait_assoc_generics.own_late_bound_regions.clone(),
}
});

Expand Down
4 changes: 2 additions & 2 deletions tests/incremental/hashes/inherent_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ impl Foo {
// ----------------------------------------------------------
// -----------------------------------------------------------
// ----------------------------------------------------------
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------------------
// -------------------------
// ----------------------------------------------------------------------------------
// -------------------------
Expand All @@ -443,7 +443,7 @@ impl Foo {
// if we lower generics before the body, then the `HirId` for
// things in the body will be affected. So if you start to see
// `typeck_root` appear dirty, that might be the cause. -nmatsakis
#[rustc_clean(cfg="bpass2", except="hir_owner,fn_sig,type_of")]
#[rustc_clean(cfg="bpass2", except="hir_owner,fn_sig,type_of,generics_of")]
#[rustc_clean(cfg="bpass3")]
#[rustc_clean(cfg="bpass5", except="hir_owner,fn_sig,type_of,generics_of")]
#[rustc_clean(cfg="bpass6")]
Expand Down
1 change: 1 addition & 0 deletions tests/ui/attributes/dump_generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const fn i_dare_you<'a: 'a, 'b, const N: usize, T, U>(you_can: &'a bool, _: &'b
let _to_find_it = if *you_can { 1 } else { 2 };

let we_got_to_find_the_foundation =
// FIXME: it isn't doing anything here
#[rustc_dump_generics]
|| {};
// and you gotta help us!
Expand Down
Loading