Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make late_bound_lifetime_arguments a hard error. #108782

Closed
wants to merge 3 commits into from
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
95 changes: 54 additions & 41 deletions compiler/rustc_hir_analysis/src/astconv/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,14 @@ use crate::astconv::{
use crate::errors::AssocTypeBindingNotAllowed;
use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
use rustc_ast::ast::ParamKindOrd;
use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::GenericArg;
use rustc_middle::ty::{
self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt,
};
use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
use rustc_span::{symbol::kw, Span};
use smallvec::SmallVec;

Expand Down Expand Up @@ -383,25 +382,13 @@ pub fn check_generic_arg_count_for_call(
seg: &hir::PathSegment<'_>,
is_method_call: IsMethodCall,
) -> GenericArgCountResult {
let empty_args = hir::GenericArgs::none();
let gen_args = seg.args.unwrap_or(&empty_args);
let gen_pos = match is_method_call {
IsMethodCall::Yes => GenericArgPosition::MethodCall,
IsMethodCall::No => GenericArgPosition::Value,
};
let has_self = generics.parent.is_none() && generics.has_self;

check_generic_arg_count(
tcx,
span,
def_id,
seg,
generics,
gen_args,
gen_pos,
has_self,
seg.infer_args,
)
check_generic_arg_count(tcx, span, def_id, seg, generics, gen_pos, has_self)
}

/// Checks that the correct number of generic arguments have been provided.
Expand All @@ -413,13 +400,12 @@ pub(crate) fn check_generic_arg_count(
def_id: DefId,
seg: &hir::PathSegment<'_>,
gen_params: &ty::Generics,
gen_args: &hir::GenericArgs<'_>,
gen_pos: GenericArgPosition,
has_self: bool,
infer_args: bool,
) -> GenericArgCountResult {
let default_counts = gen_params.own_defaults();
let param_counts = gen_params.own_counts();
let gen_args = seg.args();

// Subtracting from param count to ensure type params synthesized from `impl Trait`
// cannot be explicitly specified.
Expand All @@ -430,14 +416,13 @@ pub(crate) fn check_generic_arg_count(
.count();
let named_type_param_count = param_counts.types - has_self as usize - synth_type_param_count;
let infer_lifetimes =
(gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params();
(gen_pos != GenericArgPosition::Type || seg.infer_args) && !gen_args.has_lifetime_params();

if gen_pos != GenericArgPosition::Type && let Some(b) = gen_args.bindings.first() {
prohibit_assoc_ty_binding(tcx, b.span);
}

let explicit_late_bound =
prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos);
let explicit_late_bound = prohibit_explicit_late_bound_lifetimes(tcx, gen_params, seg, gen_pos);

let mut invalid_args = vec![];

Expand Down Expand Up @@ -561,7 +546,7 @@ pub(crate) fn check_generic_arg_count(
};

let args_correct = {
let expected_min = if infer_args {
let expected_min = if seg.infer_args {
0
} else {
param_counts.consts + named_type_param_count
Expand Down Expand Up @@ -599,10 +584,10 @@ pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) {
pub(crate) fn prohibit_explicit_late_bound_lifetimes(
tcx: TyCtxt<'_>,
def: &ty::Generics,
args: &hir::GenericArgs<'_>,
seg: &hir::PathSegment<'_>,
position: GenericArgPosition,
) -> ExplicitLateBound {
let param_counts = def.own_counts();
let args = seg.args();
let infer_lifetimes = position != GenericArgPosition::Type && !args.has_lifetime_params();

if infer_lifetimes {
Expand All @@ -611,27 +596,55 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes(

if let Some(span_late) = def.has_late_bound_regions {
let msg = "cannot specify lifetime arguments explicitly \
if late bound lifetime parameters are present";
if late bound lifetime parameters are present";
let note = "the late bound lifetime parameter is introduced here";
let span = args.args[0].span();

if position == GenericArgPosition::Value
&& args.num_lifetime_params() != param_counts.lifetimes
{
let mut err = tcx.sess.struct_span_err(span, msg);
err.span_note(span_late, note);
err.emit();
let help = format!(
"remove the explicit lifetime argument{}",
rustc_errors::pluralize!(args.num_lifetime_params())
);
let spans: Vec<_> = args
.args
.iter()
.filter_map(|arg| match arg {
hir::GenericArg::Lifetime(l) => Some(l.ident.span),
_ => None,
})
.collect();

let mut err = tcx.sess.struct_span_err(spans, msg);
err.span_note(span_late, note);

let mut suggestions = vec![];
if let Some(span_ext) = args.span_ext() {
if args.num_lifetime_params() == args.args.len()
&& span_ext.ctxt() == seg.ident.span.ctxt()
{
// We only specify lifetime args, so suggest to remove everything.
suggestions.push((seg.ident.span.shrink_to_hi().to(span_ext), String::new()));
} else {
for [arg, next_arg] in args.args.array_windows() {
if let hir::GenericArg::Lifetime(lifetime) = arg
&& let Some(arg_span) = lifetime.ident.span.find_ancestor_inside(span_ext)
&& let Some(next_span_lo) = next_arg.span().shrink_to_lo().find_ancestor_inside(span_ext)
{
suggestions.push((arg_span.with_hi(next_span_lo.lo()), String::new()));
}
}

if let Some(arg) = args.args.last()
&& let hir::GenericArg::Lifetime(lifetime) = arg
&& let Some(arg_span) = lifetime.ident.span.find_ancestor_inside(span_ext)
{
suggestions.push((arg_span, String::new()));
}
}
}
if !suggestions.is_empty() {
err.multipart_suggestion_verbose(help, suggestions, Applicability::MachineApplicable);
} else {
let mut multispan = MultiSpan::from_span(span);
multispan.push_span_label(span_late, note);
tcx.struct_span_lint_hir(
LATE_BOUND_LIFETIME_ARGUMENTS,
args.args[0].hir_id(),
multispan,
msg,
|lint| lint,
);
err.help(help);
}
err.emit();

ExplicitLateBound::Yes
} else {
Expand Down
39 changes: 11 additions & 28 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
def_id,
&[],
item_segment,
item_segment.args(),
item_segment.infer_args,
None,
ty::BoundConstness::NotConst,
);
Expand Down Expand Up @@ -332,14 +330,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
/// type itself: `['a]`. The returned `SubstsRef` concatenates these two
/// lists: `[Vec<u8>, u8, 'a]`.
#[instrument(level = "debug", skip(self, span), ret)]
fn create_substs_for_ast_path<'a>(
fn create_substs_for_ast_path(
&self,
span: Span,
def_id: DefId,
parent_substs: &[subst::GenericArg<'tcx>],
seg: &hir::PathSegment<'_>,
generic_args: &'a hir::GenericArgs<'_>,
infer_args: bool,
self_ty: Option<Ty<'tcx>>,
constness: ty::BoundConstness,
) -> (SubstsRef<'tcx>, GenericArgCountResult) {
Expand Down Expand Up @@ -370,10 +366,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
def_id,
seg,
generics,
generic_args,
GenericArgPosition::Type,
self_ty.is_some(),
infer_args,
);

// Skip processing if type has no generic parameters.
Expand Down Expand Up @@ -540,9 +534,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
astconv: self,
def_id,
span,
generic_args,
generic_args: seg.args(),
inferred_params: vec![],
infer_args,
infer_args: seg.infer_args,
};
let substs = create_substs_for_generic_args(
tcx,
Expand Down Expand Up @@ -623,8 +617,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
item_def_id,
parent_substs,
item_segment,
item_segment.args(),
item_segment.infer_args,
None,
ty::BoundConstness::NotConst,
);
Expand Down Expand Up @@ -671,17 +663,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
trait_ref_span: Span,
trait_def_id: DefId,
trait_segment: &hir::PathSegment<'_>,
args: &GenericArgs<'_>,
infer_args: bool,
self_ty: Ty<'tcx>,
) -> GenericArgCountResult {
let (substs, arg_count) = self.create_substs_for_ast_path(
trait_ref_span,
trait_def_id,
&[],
trait_segment,
args,
infer_args,
Some(self_ty),
constness,
);
Expand All @@ -690,7 +678,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let bound_vars = tcx.late_bound_vars(hir_id);
debug!(?bound_vars);

let assoc_bindings = self.create_assoc_bindings_for_generic_args(args);
let assoc_bindings = self.create_assoc_bindings_for_generic_args(trait_segment.args());

let poly_trait_ref =
ty::Binder::bind_with_vars(tcx.mk_trait_ref(trait_def_id, substs), bound_vars);
Expand Down Expand Up @@ -751,8 +739,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let trait_ref_span = trait_ref.path.span;
let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise());
let trait_segment = trait_ref.path.segments.last().unwrap();
let args = trait_segment.args();
let infer_args = trait_segment.infer_args;

self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false);
Expand All @@ -767,8 +753,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
trait_ref_span,
trait_def_id,
trait_segment,
args,
infer_args,
self_ty,
)
}
Expand All @@ -787,8 +771,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let speculative = false;
let trait_ref_span = span;
let trait_def_id = self.tcx().require_lang_item(lang_item, Some(span));
let trait_segment = &hir::PathSegment::invalid();
let infer_args = false;
let trait_segment = &hir::PathSegment {
ident: Ident::empty(),
hir_id: hir::HirId::INVALID,
res: Res::Err,
args: Some(args),
infer_args: false,
};

self.instantiate_poly_trait_ref_inner(
hir_id,
Expand All @@ -800,8 +789,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
trait_ref_span,
trait_def_id,
trait_segment,
args,
infer_args,
self_ty,
);
}
Expand Down Expand Up @@ -846,8 +833,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
trait_def_id,
&[],
trait_segment,
trait_segment.args(),
trait_segment.infer_args,
Some(self_ty),
constness,
)
Expand Down Expand Up @@ -3074,8 +3059,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
def_id,
&[],
&hir::PathSegment::invalid(),
&GenericArgs::none(),
true,
None,
ty::BoundConstness::NotConst,
);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ This API is completely unstable and subject to change.

#![allow(rustc::potential_query_instability)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(array_windows)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(drain_filter)]
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,11 @@ fn register_builtins(store: &mut LintStore) {
"converted into hard error, see issue #82523 \
<https://github.com/rust-lang/rust/issues/82523> for more information",
);
store.register_removed(
"late_bound_lifetime_arguments",
"converted into hard error, see issue #42868 \
<https://github.com/rust-lang/rust/issues/42868> for more information",
);
}

fn register_internals(store: &mut LintStore) {
Expand Down
42 changes: 0 additions & 42 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1350,47 +1350,6 @@ declare_lint! {
};
}

declare_lint! {
/// The `late_bound_lifetime_arguments` lint detects generic lifetime
/// arguments in path segments with late bound lifetime parameters.
///
/// ### Example
///
/// ```rust
/// struct S;
///
/// impl S {
/// fn late(self, _: &u8, _: &u8) {}
/// }
///
/// fn main() {
/// S.late::<'static>(&0, &0);
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// It is not clear how to provide arguments for early-bound lifetime
/// parameters if they are intermixed with late-bound parameters in the
/// same list. For now, providing any explicit arguments will trigger this
/// lint if late-bound parameters are present, so in the future a solution
/// can be adopted without hitting backward compatibility issues. This is
/// a [future-incompatible] lint to transition this to a hard error in the
/// future. See [issue #42868] for more details, along with a description
/// of the difference between early and late-bound parameters.
///
/// [issue #42868]: https://github.com/rust-lang/rust/issues/42868
/// [future-incompatible]: ../index.md#future-incompatible-lints
pub LATE_BOUND_LIFETIME_ARGUMENTS,
Warn,
"detects generic lifetime arguments in path segments with late bound lifetime parameters",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #42868 <https://github.com/rust-lang/rust/issues/42868>",
};
}

declare_lint! {
/// The `order_dependent_trait_objects` lint detects a trait coherency
/// violation that would allow creating two trait impls for the same
Expand Down Expand Up @@ -3266,7 +3225,6 @@ declare_lint_pass! {
CONST_ITEM_MUTATION,
PATTERNS_IN_FNS_WITHOUT_BODY,
MISSING_FRAGMENT_SPECIFIER,
LATE_BOUND_LIFETIME_ARGUMENTS,
ORDER_DEPENDENT_TRAIT_OBJECTS,
COHERENCE_LEAK_CHECK,
DEPRECATED,
Expand Down
Loading