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:
commit
4e1f5d90bc
55 changed files with 1033 additions and 71 deletions
|
@ -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(¶m.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
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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> {
|
||||
|
|
63
compiler/rustc_hir_analysis/src/errors/precise_captures.rs
Normal file
63
compiler/rustc_hir_analysis/src/errors/precise_captures.rs
Normal 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,
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue