Auto merge of #123468 - compiler-errors:precise-capturing, r=oli-obk

Implement syntax for `impl Trait` to specify its captures explicitly (`feature(precise_capturing)`)

Implements `impl use<'a, 'b, T, U> Sized` syntax that allows users to explicitly list the captured parameters for an opaque, rather than inferring it from the opaque's bounds (or capturing *all* lifetimes under 2024-edition capture rules). This allows us to exclude some implicit captures, so this syntax may be used as a migration strategy for changes due to #117587.

We represent this list of captured params as `PreciseCapturingArg` in AST and HIR, resolving them between `rustc_resolve` and `resolve_bound_vars`. Later on, we validate that the opaques only capture the parameters in this list.

We artificially limit the feature to *require* mentioning all type and const parameters, since we don't currently have support for non-lifetime bivariant generics. This can be relaxed in the future.

We also may need to limit this to require naming *all* lifetime parameters for RPITIT, since GATs have no variance. I have to investigate this. This can also be relaxed in the future.

r? `@oli-obk`

Tracking issue:

- https://github.com/rust-lang/rust/issues/123432
This commit is contained in:
bors 2024-04-16 11:22:35 +00:00
commit 4e1f5d90bc
55 changed files with 1033 additions and 71 deletions

View file

@ -1,10 +1,10 @@
use crate::check::intrinsicck::InlineAsmCtxt;
use crate::errors::LinkageType;
use super::compare_impl_item::check_type_bounds;
use super::compare_impl_item::{compare_impl_method, compare_impl_ty};
use super::*;
use rustc_attr as attr;
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_errors::{codes::*, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind};
@ -12,6 +12,7 @@ use rustc_hir::Node;
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
use rustc_infer::traits::{Obligation, TraitEngineExt as _};
use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::fold::BottomUpFolder;
@ -474,6 +475,133 @@ fn sanity_check_found_hidden_type<'tcx>(
}
}
/// Check that the opaque's precise captures list is valid (if present).
/// We check this for regular `impl Trait`s and also RPITITs, even though the latter
/// are technically GATs.
///
/// This function is responsible for:
/// 1. Checking that all type/const params are mention in the captures list.
/// 2. Checking that all lifetimes that are implicitly captured are mentioned.
/// 3. Asserting that all parameters mentioned in the captures list are invariant.
fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) {
let hir::OpaqueTy { precise_capturing_args, .. } =
*tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
let Some(precise_capturing_args) = precise_capturing_args else {
// No precise capturing args; nothing to validate
return;
};
let mut expected_captures = UnordSet::default();
let mut seen_params = UnordMap::default();
let mut prev_non_lifetime_param = None;
for arg in precise_capturing_args {
let (hir_id, ident) = match *arg {
hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg {
hir_id,
ident,
..
}) => {
if prev_non_lifetime_param.is_none() {
prev_non_lifetime_param = Some(ident);
}
(hir_id, ident)
}
hir::PreciseCapturingArg::Lifetime(&hir::Lifetime { hir_id, ident, .. }) => {
if let Some(prev_non_lifetime_param) = prev_non_lifetime_param {
tcx.dcx().emit_err(errors::LifetimesMustBeFirst {
lifetime_span: ident.span,
name: ident.name,
other_span: prev_non_lifetime_param.span,
});
}
(hir_id, ident)
}
};
let ident = ident.normalize_to_macros_2_0();
if let Some(span) = seen_params.insert(ident, ident.span) {
tcx.dcx().emit_err(errors::DuplicatePreciseCapture {
name: ident.name,
first_span: span,
second_span: ident.span,
});
}
match tcx.named_bound_var(hir_id) {
Some(ResolvedArg::EarlyBound(def_id)) => {
expected_captures.insert(def_id);
}
_ => {
tcx.dcx().span_delayed_bug(
tcx.hir().span(hir_id),
"parameter should have been resolved",
);
}
}
}
let variances = tcx.variances_of(opaque_def_id);
let mut def_id = Some(opaque_def_id.to_def_id());
while let Some(generics) = def_id {
let generics = tcx.generics_of(generics);
def_id = generics.parent;
for param in &generics.params {
if expected_captures.contains(&param.def_id) {
assert_eq!(
variances[param.index as usize],
ty::Invariant,
"precise captured param should be invariant"
);
continue;
}
match param.kind {
ty::GenericParamDefKind::Lifetime => {
// Check if the lifetime param was captured but isn't named in the precise captures list.
if variances[param.index as usize] == ty::Invariant {
let param_span =
if let ty::ReEarlyParam(ty::EarlyParamRegion { def_id, .. })
| ty::ReLateParam(ty::LateParamRegion {
bound_region: ty::BoundRegionKind::BrNamed(def_id, _),
..
}) = *tcx
.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local())
{
Some(tcx.def_span(def_id))
} else {
None
};
// FIXME(precise_capturing): Structured suggestion for this would be useful
tcx.dcx().emit_err(errors::LifetimeNotCaptured {
use_span: tcx.def_span(param.def_id),
param_span,
opaque_span: tcx.def_span(opaque_def_id),
});
continue;
}
}
ty::GenericParamDefKind::Type { .. } => {
// 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: "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",
});
}
}
}
}
}
fn is_enum_of_nonnullable_ptr<'tcx>(
tcx: TyCtxt<'tcx>,
adt_def: AdtDef<'tcx>,
@ -499,7 +627,7 @@ fn check_static_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) {
ty::Adt(adt_def, args) => !is_enum_of_nonnullable_ptr(tcx, *adt_def, *args),
_ => true,
} {
tcx.dcx().emit_err(LinkageType { span: tcx.def_span(def_id) });
tcx.dcx().emit_err(errors::LinkageType { span: tcx.def_span(def_id) });
}
}
}
@ -566,6 +694,8 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
check_union(tcx, def_id);
}
DefKind::OpaqueTy => {
check_opaque_precise_captures(tcx, def_id);
let origin = tcx.opaque_type_origin(def_id);
if let hir::OpaqueTyOrigin::FnReturn(fn_def_id)
| hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin

View file

@ -557,6 +557,50 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
}
}
fn visit_precise_capturing_arg(
&mut self,
arg: &'tcx hir::PreciseCapturingArg<'tcx>,
) -> Self::Result {
match *arg {
hir::PreciseCapturingArg::Lifetime(lt) => match lt.res {
LifetimeName::Param(def_id) => {
self.resolve_lifetime_ref(def_id, lt);
}
LifetimeName::Error => {}
LifetimeName::ImplicitObjectLifetimeDefault
| LifetimeName::Infer
| LifetimeName::Static => {
self.tcx.dcx().emit_err(errors::BadPreciseCapture {
span: lt.ident.span,
kind: "lifetime",
found: format!("`{}`", lt.ident.name),
});
}
},
hir::PreciseCapturingArg::Param(param) => match param.res {
Res::Def(DefKind::TyParam | DefKind::ConstParam, def_id)
| Res::SelfTyParam { trait_: def_id } => {
self.resolve_type_ref(def_id.expect_local(), param.hir_id);
}
Res::Err => {}
Res::SelfTyAlias { alias_to, .. } => {
self.tcx.dcx().emit_err(errors::PreciseCaptureSelfAlias {
span: param.ident.span,
self_span: self.tcx.def_span(alias_to),
what: self.tcx.def_descr(alias_to),
});
}
res => {
self.tcx.dcx().emit_err(errors::BadPreciseCapture {
span: param.ident.span,
kind: "type or const",
found: res.descr().to_string(),
});
}
},
}
}
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
match item.kind {
hir::ForeignItemKind::Fn(_, _, generics) => {

View file

@ -10,6 +10,9 @@ use rustc_span::{symbol::Ident, Span, Symbol};
mod pattern_types;
pub use pattern_types::*;
mod precise_captures;
pub(crate) use precise_captures::*;
#[derive(Diagnostic)]
#[diag(hir_analysis_ambiguous_assoc_item)]
pub struct AmbiguousAssocItem<'a> {

View file

@ -0,0 +1,63 @@
use rustc_macros::Diagnostic;
use rustc_span::{Span, Symbol};
#[derive(Diagnostic)]
#[diag(hir_analysis_param_not_captured)]
#[note]
pub struct ParamNotCaptured {
#[primary_span]
pub param_span: Span,
#[label]
pub opaque_span: Span,
pub kind: &'static str,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_lifetime_not_captured)]
pub struct LifetimeNotCaptured {
#[primary_span]
pub use_span: Span,
#[label(hir_analysis_param_label)]
pub param_span: Option<Span>,
#[label]
pub opaque_span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_bad_precise_capture)]
pub struct BadPreciseCapture {
#[primary_span]
pub span: Span,
pub kind: &'static str,
pub found: String,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_precise_capture_self_alias)]
pub struct PreciseCaptureSelfAlias {
#[primary_span]
pub span: Span,
#[label]
pub self_span: Span,
pub what: &'static str,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_duplicate_precise_capture)]
pub struct DuplicatePreciseCapture {
#[primary_span]
pub first_span: Span,
pub name: Symbol,
#[label]
pub second_span: Span,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_lifetime_must_be_first)]
pub struct LifetimesMustBeFirst {
#[primary_span]
pub lifetime_span: Span,
pub name: Symbol,
#[label]
pub other_span: Span,
}