rust-lang/rust · #163798
validate generic parameter remapping in delegation
compiler/rustc_hir_analysis/src/delegation.rs107 + / 37 −
@@ -10,7 +10,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{DelegationSelfTyPropagationKind, PathSegment}; use rustc_middle::ty::{ self, ConstKind, EarlyBinder, GenericArg, GenericArgKind, RegionKind, Ty, TyCtxt, TypeFoldable,- TypeFolder, TypeSuperFoldable, TypeVisitableExt,+ TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast, }; use rustc_span::{ErrorGuaranteed, Span, kw}; @@ -23,46 +23,65 @@ struct ParamIndexRemapper<'tcx> { tcx: TyCtxt<'tcx>, remap_table: RemapTable, delegation_parent_consts: FxHashSet<ty::ParamConst>,+ arg_kinds: Vec<ty::GenericArgKind<'tcx>>, } -impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ParamIndexRemapper<'tcx> {+impl<'tcx> ParamIndexRemapper<'tcx> {+ fn try_remap(+ &self,+ source_index: u32,+ expected_kind: ty::GenericArgKind<'tcx>,+ ) -> Result<u32, ()> {+ self.remap_table+ .get(&source_index)+ .copied()+ .filter(|&idx| {+ self.arg_kinds.get(idx as usize).is_some_and(|kind| {+ std::mem::discriminant(&expected_kind) == std::mem::discriminant(kind)+ })+ })+ .ok_or(())+ }+}++impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for ParamIndexRemapper<'tcx> {+ type Error = ();+ fn cx(&self) -> TyCtxt<'tcx> { self.tcx } - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {+ fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> { if !ty.has_param() {- return ty;+ return Ok(ty); } - if let ty::Param(param) = ty.kind()- && let Some(index) = self.remap_table.get(¶m.index)- {- return Ty::new_param(self.tcx, *index, param.name);+ if let ty::Param(param) = ty.kind() {+ let index = self.try_remap(param.index, ty::GenericArgKind::Type(ty))?;+ return Ok(Ty::new_param(self.tcx, index, param.name)); }- ty.super_fold_with(self)++ ty.try_super_fold_with(self) } - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {- if let ty::ReEarlyParam(param) = r.kind()- && let Some(index) = self.remap_table.get(¶m.index).copied()- {- return ty::Region::new_early_param(+ fn try_fold_region(&mut self, r: ty::Region<'tcx>) -> Result<ty::Region<'tcx>, Self::Error> {+ if let ty::ReEarlyParam(param) = r.kind() {+ let index = self.try_remap(param.index, ty::GenericArgKind::Lifetime(r))?;+ return Ok(ty::Region::new_early_param( self.tcx, ty::EarlyParamRegion { index, name: param.name },- );+ )); }- r+ Ok(r) } - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {- if let ty::ConstKind::Param(param) = ct.kind()- && let Some(idx) = self.remap_table.get(¶m.index)- {- let param = ty::ParamConst::new(*idx, param.name);- return ty::Const::new_param(self.tcx, param);+ fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {+ if let ty::ConstKind::Param(param) = ct.kind() {+ let idx = self.try_remap(param.index, ty::GenericArgKind::Const(ct))?;+ let param = ty::ParamConst::new(idx, param.name);+ return Ok(ty::Const::new_param(self.tcx, param)); }- ct.super_fold_with(self)+ ct.try_super_fold_with(self) } } @@ -479,7 +498,7 @@ pub(crate) fn inherit_clauses_for_delegation_item<'tcx>( mut self, f: impl Fn(DefId) -> ty::GenericClauses<'tcx>, def_id: DefId,- ) -> Self {+ ) -> Result<Self, ()> { let clauses = f(def_id); let args = self.args.as_slice(); @@ -521,8 +540,9 @@ pub(crate) fn inherit_clauses_for_delegation_item<'tcx>( // it came from delegation parent, and if so we do not process its `ConstArgHasType` // clause. // (Issue #158675).+ let new_clause = clause.0.try_fold_with(&mut self.folder)?; if let ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) =- clause.0.as_predicate().fold_with(&mut self.folder).kind().skip_binder()+ new_clause.as_predicate().kind().skip_binder() { let unnorm_const = EarlyBinder::bind(self.tcx, ct).instantiate(self.tcx, args); if let ty::ConstKind::Param(param) = unnorm_const.skip_norm_wip().kind()@@ -532,7 +552,6 @@ pub(crate) fn inherit_clauses_for_delegation_item<'tcx>( } } - let new_clause = clause.0.fold_with(&mut self.folder); self.clauses.push(( EarlyBinder::bind(self.tcx, new_clause) .instantiate(self.tcx, args)@@ -541,17 +560,17 @@ pub(crate) fn inherit_clauses_for_delegation_item<'tcx>( )); } - self+ Ok(self) } fn with_clauses( mut self, f: impl Fn(DefId) -> ty::GenericClauses<'tcx> + Copy, def_id: DefId,- ) -> Self {+ ) -> Result<Self, ()> { let preds = f(def_id); if let Some(parent_def_id) = preds.parent {- self = self.with_own_clauses(f, parent_def_id);+ self = self.with_own_clauses(f, parent_def_id)?; } self.with_own_clauses(f, def_id)@@ -572,16 +591,47 @@ pub(crate) fn inherit_clauses_for_delegation_item<'tcx>( // `explicit_clauses_of` is used here to avoid copying `Self: Trait` clause. // Note: `clauses_of` query can also add inferred outlives clauses, but that // is not the case here as `sig_id` is either a trait or a function.- let clauses = match inh_kind {+ let collector = match inh_kind { InheritanceKind::WithParent(false) => { collector.with_clauses(|def_id| tcx.explicit_clauses_of(def_id), sig_id) } InheritanceKind::WithParent(true) => { collector.with_clauses(|def_id| tcx.clauses_of(def_id), sig_id) } InheritanceKind::Own => collector.with_own_clauses(|def_id| tcx.clauses_of(def_id), sig_id),- }- .clauses;+ };++ let clauses = match collector {+ Ok(collector) => collector.clauses,+ Err(()) => {+ tcx.dcx().emit_err(crate::diagnostics::DelegationInheritanceFailed {+ span: tcx.def_span(def_id),+ kind: "predicates",+ callee_span: tcx.def_span(sig_id),+ });++ // Even on error, the delegated item's const parameters need their+ // types in the parameter environment.+ tcx.generics_of(def_id)+ .own_params+ .iter()+ .filter_map(|param| {+ if !matches!(param.kind, ty::GenericParamDefKind::Const { .. }) {+ return None;+ }++ let ct =+ ty::Const::new_param(tcx, ty::ParamConst::new(param.index, param.name));+ let ct_ty = tcx.type_of(param.def_id).instantiate_identity().skip_norm_wip();++ Some((+ ty::ClauseKind::ConstArgHasType(ct, ct_ty).upcast(tcx),+ tcx.def_span(param.def_id),+ ))+ })+ .collect::<Vec<_>>()+ }+ }; ty::GenericClauses { parent, clauses: tcx.arena.alloc_from_iter(clauses) } }@@ -607,7 +657,8 @@ fn create_folder_and_args<'tcx>( }) .collect(); - (ParamIndexRemapper { tcx, remap_table, delegation_parent_consts }, args)+ let arg_kinds = args.iter().map(|arg| arg.kind()).collect();+ (ParamIndexRemapper { tcx, remap_table, delegation_parent_consts, arg_kinds }, args) } fn check_constraints<'tcx>(@@ -633,21 +684,40 @@ fn check_constraints<'tcx>( ret } +fn error_sig_for_delegation<'tcx>(+ tcx: TyCtxt<'tcx>,+ sig_id: DefId,+ err: ErrorGuaranteed,+) -> &'tcx [Ty<'tcx>] {+ let sig_len = tcx.fn_sig(sig_id).instantiate_identity().skip_binder().inputs().len() + 1;+ let err_type = Ty::new_error(tcx, err);+ tcx.arena.alloc_from_iter((0..sig_len).map(|_| err_type))+}+ pub(crate) fn inherit_sig_for_delegation_item<'tcx>( tcx: TyCtxt<'tcx>, def_id: LocalDefId, ) -> &'tcx [Ty<'tcx>] { let sig_id = tcx.hir_opt_delegation_sig_id(def_id).expect("Delegation must have sig_id"); let caller_sig = tcx.fn_sig(sig_id); if let Err(err) = check_constraints(tcx, def_id, sig_id) {- let sig_len = caller_sig.instantiate_identity().skip_binder().inputs().len() + 1;- let err_type = Ty::new_error(tcx, err);- return tcx.arena.alloc_from_iter((0..sig_len).map(|_| err_type));+ return error_sig_for_delegation(tcx, sig_id, err); } let (parent_args, child_args) = tcx.delegation_user_specified_args(def_id); let (mut folder, args) = create_folder_and_args(tcx, def_id, sig_id, parent_args, child_args);- let caller_sig = EarlyBinder::bind(tcx, caller_sig.skip_binder().fold_with(&mut folder));+ let folded_sig = match caller_sig.skip_binder().try_fold_with(&mut folder) {+ Ok(sig) => sig,+ Err(()) => {+ let err = tcx.dcx().emit_err(crate::diagnostics::DelegationInheritanceFailed {+ span: tcx.def_span(def_id),+ kind: "signature",+ callee_span: tcx.def_span(sig_id),+ });+ return error_sig_for_delegation(tcx, sig_id, err);+ }+ };+ let caller_sig = EarlyBinder::bind(tcx, folded_sig); let sig = caller_sig.instantiate(tcx, args.as_slice()).skip_binder(); let output = std::iter::once(sig.output());compiler/rustc_hir_analysis/src/diagnostics.rs10 + / 0 −
@@ -1706,6 +1706,16 @@ pub(crate) struct OnlyCurrentTraitsPointerSugg<'a> { pub ptr_ty: Ty<'a>, } +#[derive(Diagnostic)]+#[diag("failed to inherit delegation {$kind}")]+pub(crate) struct DelegationInheritanceFailed {+ #[primary_span]+ pub span: Span,+ pub kind: &'static str,+ #[label("callee defined here")]+ pub callee_span: Span,+}+ #[derive(Diagnostic)] #[diag("{$descr}")] pub(crate) struct UnsupportedDelegation<'a> {tests/ui/delegation/generics/const-param-trait-name-collision-ice-163868.rsadded18 + / 0 −
@@ -0,0 +1,18 @@+#![feature(fn_delegation)]++trait C {}++struct S<const C: usize>;++impl<const C: usize> S<C> {+ //~^ ERROR type provided when a constant was expected+ fn foo_self<T>(self) {}+}++trait Trait {+ reuse S::<1>::foo_self::<_> as foo_self_4;+ //~^ ERROR failed to inherit delegation predicates+ //~| ERROR the size for values of type `T` cannot be known+}++fn main() {}tests/ui/delegation/generics/const-param-trait-name-collision-ice-163868.stderradded40 + / 0 −
@@ -0,0 +1,40 @@+error[E0747]: type provided when a constant was expected+ --> $DIR/const-param-trait-name-collision-ice-163868.rs:7:24+ |+LL | impl<const C: usize> S<C> {+ | ^++error: failed to inherit delegation predicates+ --> $DIR/const-param-trait-name-collision-ice-163868.rs:13:19+ |+LL | fn foo_self<T>(self) {}+ | -------------------- callee defined here+...+LL | reuse S::<1>::foo_self::<_> as foo_self_4;+ | ^^^^^^^^++error[E0277]: the size for values of type `T` cannot be known at compilation time+ --> $DIR/const-param-trait-name-collision-ice-163868.rs:13:19+ |+LL | reuse S::<1>::foo_self::<_> as foo_self_4;+ | ^^^^^^^^+ | |+ | doesn't have a size known at compile-time+ | this type parameter needs to be `Sized`+ |+note: required by an implicit `Sized` bound in `S::<{const error}>::foo_self`+ --> $DIR/const-param-trait-name-collision-ice-163868.rs:9:17+ |+LL | impl<const C: usize> S<C> {+LL |+LL | fn foo_self<T>(self) {}+ | ^ required by the implicit `Sized` requirement on this type parameter in `S::<{const error}>::foo_self`+help: consider relaxing the implicit `Sized` restriction+ |+LL | fn foo_self<T: ?Sized>(self) {}+ | ++++++++++error: aborting due to 3 previous errors++Some errors have detailed explanations: E0277, E0747.+For more information about an error, try `rustc --explain E0277`.tests/ui/delegation/generics/const-param-type-name-collision-ice-162776.rsadded16 + / 0 −
@@ -0,0 +1,16 @@+#![feature(fn_delegation)]++struct S<A, const C: usize>;+//~^ ERROR: type parameter `A` is never used++impl<'b, 'c, C, const C: usize> S<C, C> {+//~^ ERROR: the name `C` is already used for a generic parameter in this item's generic parameters+//~| ERROR: type provided when a constant was expected+ fn foo_self<T, const B: bool>() {}+}++reuse S::<(), 123>::foo_self;+//~^ ERROR: failed to inherit delegation predicates+//~| ERROR: the size for values of type `T` cannot be known at compilation time++fn main() {}tests/ui/delegation/generics/const-param-type-name-collision-ice-162776.stderradded63 + / 0 −
@@ -0,0 +1,63 @@+error[E0403]: the name `C` is already used for a generic parameter in this item's generic parameters+ --> $DIR/const-param-type-name-collision-ice-162776.rs:6:23+ |+LL | impl<'b, 'c, C, const C: usize> S<C, C> {+ | - ^ already used+ | |+ | first use of `C`++error[E0747]: type provided when a constant was expected+ --> $DIR/const-param-type-name-collision-ice-162776.rs:6:38+ |+LL | impl<'b, 'c, C, const C: usize> S<C, C> {+ | ^+ |+help: consider changing this type parameter to a const parameter+ |+LL - impl<'b, 'c, C, const C: usize> S<C, C> {+LL + impl<'b, 'c, const C: usize, const C: usize> S<C, C> {+ |++error[E0392]: type parameter `A` is never used+ --> $DIR/const-param-type-name-collision-ice-162776.rs:3:10+ |+LL | struct S<A, const C: usize>;+ | ^ unused type parameter+ |+ = help: consider removing `A`, referring to it in a field, or using a marker such as `PhantomData`+ = help: if you intended `A` to be a const parameter, use `const A: /* Type */` instead++error: failed to inherit delegation predicates+ --> $DIR/const-param-type-name-collision-ice-162776.rs:12:21+ |+LL | fn foo_self<T, const B: bool>() {}+ | ------------------------------- callee defined here+...+LL | reuse S::<(), 123>::foo_self;+ | ^^^^^^^^++error[E0277]: the size for values of type `T` cannot be known at compilation time+ --> $DIR/const-param-type-name-collision-ice-162776.rs:12:21+ |+LL | reuse S::<(), 123>::foo_self;+ | ^^^^^^^^+ | |+ | doesn't have a size known at compile-time+ | this type parameter needs to be `Sized`+ |+note: required by an implicit `Sized` bound in `S::<C, {const error}>::foo_self`+ --> $DIR/const-param-type-name-collision-ice-162776.rs:9:17+ |+LL | impl<'b, 'c, C, const C: usize> S<C, C> {+...+LL | fn foo_self<T, const B: bool>() {}+ | ^ required by the implicit `Sized` requirement on this type parameter in `S::<C, {const error}>::foo_self`+help: consider relaxing the implicit `Sized` restriction+ |+LL | fn foo_self<T: ?Sized, const B: bool>() {}+ | ++++++++++error: aborting due to 5 previous errors++Some errors have detailed explanations: E0277, E0392, E0403, E0747.+For more information about an error, try `rustc --explain E0277`.