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

Implement feature(precise_capturing_of_types) #138473

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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
4 changes: 3 additions & 1 deletion compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -598,8 +598,10 @@ declare_features! (
(incomplete, pin_ergonomics, "1.83.0", Some(130494)),
/// Allows postfix match `expr.match { ... }`
(unstable, postfix_match, "1.79.0", Some(121618)),
/// Allows `use<..>` precise capturign on impl Trait in traits.
/// Allows `use<..>` precise capturing on impl Trait in traits.
(unstable, precise_capturing_in_traits, "1.83.0", Some(130044)),
/// Allows `use<..>` precise capturing to omit type and const parameters.
(incomplete, precise_capturing_of_types, "1.83.0", Some(130043)),
/// Allows macro attributes on expressions, statements and non-inline modules.
(unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
/// Allows the use of raw-dylibs on ELF platforms
Expand Down
16 changes: 8 additions & 8 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -284,16 +284,9 @@ hir_analysis_late_bound_lifetime_in_apit = `impl Trait` can only mention lifetim
hir_analysis_late_bound_type_in_apit = `impl Trait` can only mention type parameters from an fn or impl
.label = type parameter declared here

hir_analysis_lifetime_implicitly_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
.param_label = all lifetime parameters originating from a trait are captured implicitly

hir_analysis_lifetime_must_be_first = lifetime parameter `{$name}` must be listed before non-lifetime parameters
.label = move the lifetime before this parameter

hir_analysis_lifetime_not_captured = `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
.label = lifetime captured due to being mentioned in the bounds of the `impl Trait`
.param_label = this lifetime parameter is captured

hir_analysis_lifetimes_or_bounds_mismatch_on_trait =
lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration
.label = lifetimes do not match {$item_kind} in trait
Expand Down Expand Up @@ -412,6 +405,9 @@ hir_analysis_opaque_captures_higher_ranked_lifetime = `impl Trait` cannot captur
.label = `impl Trait` implicitly captures all lifetimes in scope
.note = lifetime declared here

hir_analysis_param_implicitly_captured = `impl Trait` captures {$kind} parameter, but it is not mentioned in `use<...>` precise captures list
.param_label = all parameters originating from a trait are captured implicitly

hir_analysis_param_in_ty_of_assoc_const_binding =
the type of the associated constant `{$assoc_const}` must not depend on {$param_category ->
[self] `Self`
Expand All @@ -428,7 +424,11 @@ hir_analysis_param_in_ty_of_assoc_const_binding =
*[normal] the {$param_def_kind} `{$param_name}` is defined here
}

hir_analysis_param_not_captured = `impl Trait` must mention all {$kind} parameters in scope in `use<...>`
hir_analysis_param_not_captured = `impl Trait` captures {$kind} parameter, but it is not mentioned in `use<...>` precise captures list
.label = {$kind} captured due to being mentioned in the bounds of the `impl Trait`
.param_label = this {$kind} parameter is captured

hir_analysis_param_not_captured_forced = `impl Trait` must mention all {$kind} parameters in scope in `use<...>`
.label = {$kind} parameter is implicitly captured by this `impl Trait`
.note = currently, all {$kind} parameters are required to be mentioned in the precise captures list

Expand Down
125 changes: 81 additions & 44 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,71 +609,108 @@ fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDe
}

match param.kind {
ty::GenericParamDefKind::Lifetime => {
let use_span = tcx.def_span(param.def_id);
let opaque_span = tcx.def_span(opaque_def_id);
// Check if the lifetime param was captured but isn't named in the precise captures list.
if variances[param.index as usize] == ty::Invariant {
if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id))
&& let Some(def_id) = tcx
.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local())
.opt_param_def_id(tcx, tcx.parent(opaque_def_id.to_def_id()))
{
tcx.dcx().emit_err(errors::LifetimeNotCaptured {
opaque_span,
use_span,
param_span: tcx.def_span(def_id),
});
} else {
if tcx.def_kind(tcx.parent(param.def_id)) == DefKind::Trait {
tcx.dcx().emit_err(errors::LifetimeImplicitlyCaptured {
opaque_span,
param_span: tcx.def_span(param.def_id),
});
} else {
// If the `use_span` is actually just the param itself, then we must
// have not duplicated the lifetime but captured the original.
// The "effective" `use_span` will be the span of the opaque itself,
// and the param span will be the def span of the param.
tcx.dcx().emit_err(errors::LifetimeNotCaptured {
opaque_span,
use_span: opaque_span,
param_span: use_span,
});
}
}
continue;
}
}
ty::GenericParamDefKind::Lifetime => check_captured_arg_is_mentioned(
tcx,
opaque_def_id,
variances,
param,
"lifetime",
),
ty::GenericParamDefKind::Type { .. } => {
if matches!(tcx.def_kind(param.def_id), DefKind::Trait | DefKind::TraitAlias) {
if tcx.features().precise_capturing_of_types() {
check_captured_arg_is_mentioned(
tcx,
opaque_def_id,
variances,
param,
"type",
)
} else if matches!(
tcx.def_kind(param.def_id),
DefKind::Trait | DefKind::TraitAlias
) {
// FIXME(precise_capturing): Structured suggestion for this would be useful
tcx.dcx().emit_err(errors::SelfTyNotCaptured {
trait_span: tcx.def_span(param.def_id),
opaque_span: tcx.def_span(opaque_def_id),
});
} else {
// FIXME(precise_capturing): Structured suggestion for this would be useful
tcx.dcx().emit_err(errors::ParamNotCaptured {
tcx.dcx().emit_err(errors::ParamNotCapturedForced {
param_span: tcx.def_span(param.def_id),
opaque_span: tcx.def_span(opaque_def_id),
kind: "type",
});
}
}
ty::GenericParamDefKind::Const { .. } => {
// FIXME(precise_capturing): Structured suggestion for this would be useful
tcx.dcx().emit_err(errors::ParamNotCaptured {
param_span: tcx.def_span(param.def_id),
opaque_span: tcx.def_span(opaque_def_id),
kind: "const",
});
if tcx.features().precise_capturing_of_types() {
check_captured_arg_is_mentioned(
tcx,
opaque_def_id,
variances,
param,
"const",
)
} else {
// FIXME(precise_capturing): Structured suggestion for this would be useful
tcx.dcx().emit_err(errors::ParamNotCapturedForced {
param_span: tcx.def_span(param.def_id),
opaque_span: tcx.def_span(opaque_def_id),
kind: "const",
});
}
}
}
}
}
}

fn check_captured_arg_is_mentioned<'tcx>(
tcx: TyCtxt<'tcx>,
opaque_def_id: LocalDefId,
variances: &[ty::Variance],
param: &ty::GenericParamDef,
kind: &'static str,
) {
let use_span = tcx.def_span(param.def_id);
let opaque_span = tcx.def_span(opaque_def_id);
// Check if the lifetime param was captured but isn't named in the precise captures list.
if variances[param.index as usize] == ty::Invariant {
if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id))
&& let Some(def_id) = tcx
.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local())
.opt_param_def_id(tcx, tcx.parent(opaque_def_id.to_def_id()))
{
tcx.dcx().emit_err(errors::ParamNotCaptured {
opaque_span,
use_span,
param_span: tcx.def_span(def_id),
kind,
});
} else {
if tcx.def_kind(tcx.parent(param.def_id)) == DefKind::Trait {
tcx.dcx().emit_err(errors::ParamImplicitlyCaptured {
opaque_span,
param_span: tcx.def_span(param.def_id),
kind,
});
} else {
// If the `use_span` is actually just the param itself, then we must
// have not duplicated the lifetime but captured the original.
// The "effective" `use_span` will be the span of the opaque itself,
// and the param span will be the def span of the param.
tcx.dcx().emit_err(errors::ParamNotCaptured {
opaque_span,
use_span: opaque_span,
param_span: use_span,
kind,
});
}
}
}
}

fn is_enum_of_nonnullable_ptr<'tcx>(
tcx: TyCtxt<'tcx>,
adt_def: AdtDef<'tcx>,
Expand Down
14 changes: 8 additions & 6 deletions compiler/rustc_hir_analysis/src/errors/precise_captures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use rustc_macros::Diagnostic;
use rustc_span::{Span, Symbol};

#[derive(Diagnostic)]
#[diag(hir_analysis_param_not_captured)]
#[diag(hir_analysis_param_not_captured_forced)]
#[note]
pub(crate) struct ParamNotCaptured {
pub(crate) struct ParamNotCapturedForced {
#[primary_span]
pub opaque_span: Span,
#[label]
Expand All @@ -24,23 +24,25 @@ pub(crate) struct SelfTyNotCaptured {
}

#[derive(Diagnostic)]
#[diag(hir_analysis_lifetime_not_captured)]
pub(crate) struct LifetimeNotCaptured {
#[diag(hir_analysis_param_not_captured)]
pub(crate) struct ParamNotCaptured {
#[primary_span]
pub use_span: Span,
#[label(hir_analysis_param_label)]
pub param_span: Span,
#[label]
pub opaque_span: Span,
pub kind: &'static str,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_lifetime_implicitly_captured)]
pub(crate) struct LifetimeImplicitlyCaptured {
#[diag(hir_analysis_param_implicitly_captured)]
pub(crate) struct ParamImplicitlyCaptured {
#[primary_span]
pub opaque_span: Span,
#[label(hir_analysis_param_label)]
pub param_span: Span,
pub kind: &'static str,
}

#[derive(Diagnostic)]
Expand Down
12 changes: 8 additions & 4 deletions compiler/rustc_hir_analysis/src/variance/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
}
DefKind::AssocTy => match tcx.opt_rpitit_info(item_def_id.to_def_id()) {
Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => {
return variance_of_opaque(
return variances_of_opaque(
tcx,
opaque_def_id.expect_local(),
ForceCaptureTraitArgs::Yes,
Expand All @@ -83,7 +83,7 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] {
ForceCaptureTraitArgs::No
};

return variance_of_opaque(tcx, item_def_id, force_capture_trait_args);
return variances_of_opaque(tcx, item_def_id, force_capture_trait_args);
}
_ => {}
}
Expand All @@ -99,7 +99,7 @@ enum ForceCaptureTraitArgs {
}

#[instrument(level = "trace", skip(tcx), ret)]
fn variance_of_opaque(
fn variances_of_opaque(
tcx: TyCtxt<'_>,
item_def_id: LocalDefId,
force_capture_trait_args: ForceCaptureTraitArgs,
Expand Down Expand Up @@ -177,7 +177,11 @@ fn variance_of_opaque(
variances[param.index as usize] = ty::Bivariant;
}
ty::GenericParamDefKind::Type { .. }
| ty::GenericParamDefKind::Const { .. } => {}
| ty::GenericParamDefKind::Const { .. } => {
if tcx.features().precise_capturing_of_types() {
variances[param.index as usize] = ty::Bivariant;
}
}
}
}
}
Expand Down
3 changes: 0 additions & 3 deletions compiler/rustc_middle/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ middle_consider_type_length_limit =
middle_const_eval_non_int =
constant evaluation of enum discriminant resulted in non-integer

middle_const_not_used_in_type_alias =
const parameter `{$ct}` is part of concrete type but not used in parameter list for the `impl Trait` type alias

middle_deprecated = use of deprecated {$kind} `{$path}`{$has_note ->
[true] : {$note}
*[other] {""}
Expand Down
8 changes: 0 additions & 8 deletions compiler/rustc_middle/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,6 @@ pub(crate) struct RequiresLangItem {
pub name: Symbol,
}

#[derive(Diagnostic)]
#[diag(middle_const_not_used_in_type_alias)]
pub(super) struct ConstNotUsedTraitAlias {
pub ct: String,
#[primary_span]
pub span: Span,
}

pub struct CustomSubdiagnostic<'a> {
pub msg: fn() -> DiagMessage,
pub add_args: Box<dyn FnOnce(&mut dyn FnMut(DiagArgName, DiagArgValue)) + 'a>,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,10 @@ impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_featu
fn associated_const_equality(self) -> bool {
self.associated_const_equality()
}

fn precise_capturing_of_types(self) -> bool {
self.precise_capturing_of_types()
}
}

impl<'tcx> rustc_type_ir::inherent::Span<TyCtxt<'tcx>> for Span {
Expand Down
28 changes: 22 additions & 6 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -823,16 +823,32 @@ impl<'tcx> OpaqueHiddenType<'tcx> {
let id_args = GenericArgs::identity_for_item(tcx, def_id);
debug!(?id_args);

// This zip may have several times the same lifetime in `args` paired with a different
// lifetime from `id_args`. Simply `collect`ing the iterator is the correct behaviour:
// it will pick the last one, which is the one we introduced in the impl-trait desugaring.
let map = args.iter().zip(id_args).collect();
debug!("map = {:#?}", map);
let mut mapping = FxHashMap::default();
let mut uncaptured_args = FxHashSet::default();
for ((arg, identity_arg), &variance) in
args.iter().zip(id_args).zip(tcx.variances_of(def_id))
{
match variance {
ty::Invariant => {
mapping.insert(arg, identity_arg);
}
ty::Bivariant => {
uncaptured_args.insert(arg);
}
_ => unreachable!(),
}
}

// Convert the type from the function into a type valid outside
// the function, by replacing invalid regions with 'static,
// after producing an error for each of them.
self.fold_with(&mut opaque_types::ReverseMapper::new(tcx, map, self.span, ignore_errors))
self.fold_with(&mut opaque_types::ReverseMapper::new(
tcx,
mapping,
uncaptured_args,
self.span,
ignore_errors,
))
}
}

Expand Down
Loading
Loading