Reject early-bound params in the type of assoc const bindings

This commit is contained in:
León Orell Valerian Liehr 2023-12-28 18:51:53 +01:00
parent 3b85d2c7fc
commit d26c5723e7
No known key found for this signature in database
GPG key ID: D17A07215F68E713
5 changed files with 270 additions and 10 deletions

View file

@ -1,12 +1,13 @@
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::{codes::*, struct_span_code_err};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::ty::{self as ty, Ty};
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
use rustc_span::symbol::Ident;
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::traits;
use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
use smallvec::SmallVec;
use crate::astconv::{AstConv, OnlySelfBounds, PredicateFilter};
@ -438,14 +439,8 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
binding.kind
{
let ty = alias_ty.map_bound(|ty| tcx.type_of(ty.def_id).instantiate(tcx, ty.args));
// Since the arguments passed to the alias type above may contain early-bound
// generic parameters, the instantiated type may contain some as well.
// Therefore wrap it in `EarlyBinder`.
// FIXME(fmease): Reject escaping late-bound vars.
tcx.feed_anon_const_type(
anon_const.def_id,
ty::EarlyBinder::bind(ty.skip_binder()),
);
let ty = check_assoc_const_binding_type(tcx, assoc_ident, ty, binding.hir_id);
tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty));
}
alias_ty
@ -537,3 +532,96 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
Ok(())
}
}
/// Detect and reject early-bound generic params in the type of associated const bindings.
///
/// FIXME(const_generics): This is a temporary and semi-artifical restriction until the
/// arrival of *generic const generics*[^1].
///
/// It might actually be possible that we can already support early-bound generic params
/// in such types if we just lifted some more checks in other places, too, for example
/// inside [`ty::Const::from_anon_const`]. However, even if that were the case, we should
/// probably gate this behind another feature flag.
///
/// [^1]: <https://github.com/rust-lang/project-const-generics/issues/28>.
fn check_assoc_const_binding_type<'tcx>(
tcx: TyCtxt<'tcx>,
assoc_const: Ident,
ty: ty::Binder<'tcx, Ty<'tcx>>,
hir_id: hir::HirId,
) -> Ty<'tcx> {
// We can't perform the checks for early-bound params during name resolution unlike E0770
// because this information depends on *type* resolution.
// FIXME(fmease): Reject escaping late-bound vars.
let ty = ty.skip_binder();
if !ty.has_param() {
return ty;
}
let mut collector = GenericParamCollector { params: Default::default() };
ty.visit_with(&mut collector);
let mut guar = None;
let ty_note = ty
.make_suggestable(tcx, false)
.map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty });
let enclosing_item_owner_id = tcx
.hir()
.parent_owner_iter(hir_id)
.find_map(|(owner_id, parent)| parent.generics().map(|_| owner_id))
.unwrap();
let generics = tcx.generics_of(enclosing_item_owner_id);
for index in collector.params {
let param = generics.param_at(index as _, tcx);
let is_self_param = param.name == rustc_span::symbol::kw::SelfUpper;
guar.get_or_insert(tcx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding {
span: assoc_const.span,
assoc_const,
param_name: param.name,
param_def_kind: tcx.def_descr(param.def_id),
param_category: if is_self_param {
"self"
} else if param.kind.is_synthetic() {
"synthetic"
} else {
"normal"
},
param_defined_here_label:
(!is_self_param).then(|| tcx.def_ident_span(param.def_id).unwrap()),
ty_note,
}));
}
let guar = guar.unwrap_or_else(|| bug!("failed to find gen params in ty"));
Ty::new_error(tcx, guar)
}
struct GenericParamCollector {
params: FxIndexSet<u32>,
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamCollector {
fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
if let ty::Param(param) = ty.kind() {
self.params.insert(param.index);
} else if ty.has_param() {
ty.super_visit_with(self)
}
}
fn visit_region(&mut self, re: ty::Region<'tcx>) -> Self::Result {
if let ty::ReEarlyParam(param) = re.kind() {
self.params.insert(param.index);
}
}
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> Self::Result {
if let ty::ConstKind::Param(param) = ct.kind() {
self.params.insert(param.index);
} else if ct.has_param() {
ct.super_visit_with(self)
}
}
}

View file

@ -295,6 +295,29 @@ pub struct AssocTypeBindingNotAllowed {
pub fn_trait_expansion: Option<ParenthesizedFnTraitExpansion>,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_param_in_ty_of_assoc_const_binding)]
pub(crate) struct ParamInTyOfAssocConstBinding<'tcx> {
#[primary_span]
#[label]
pub span: Span,
pub assoc_const: Ident,
pub param_name: Symbol,
pub param_def_kind: &'static str,
pub param_category: &'static str,
#[label(hir_analysis_param_defined_here_label)]
pub param_defined_here_label: Option<Span>,
#[subdiagnostic]
pub ty_note: Option<TyOfAssocConstBindingNote<'tcx>>,
}
#[derive(Subdiagnostic, Clone, Copy)]
#[note(hir_analysis_ty_of_assoc_const_binding_note)]
pub(crate) struct TyOfAssocConstBindingNote<'tcx> {
pub assoc_const: Ident,
pub ty: Ty<'tcx>,
}
#[derive(Subdiagnostic)]
#[help(hir_analysis_parenthesized_fn_trait_expansion)]
pub struct ParenthesizedFnTraitExpansion {