Skip to content

Commit 7995a2b

Browse files
author
yimiliya
committed
Add try_instantiate to EarlyBinder for fallible instantiation
This adds a fallible version of EarlyBinder::instantiate that returns a Result instead of panicking when parameter instantiation fails. The new try_instantiate method and InstantiationError enum allow callers to gracefully handle cases where: - Type/const/region parameters are out of range - Wrong kind of argument is provided (e.g., const when type expected) - No args provided but the value has parameters This is useful in error recovery paths where we want to continue compilation and report the actual error instead of ICE-ing.
1 parent 9f0615c commit 7995a2b

File tree

1 file changed

+248
-0
lines changed

1 file changed

+248
-0
lines changed

compiler/rustc_type_ir/src/binder.rs

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,22 @@ impl<I: Interner, T: TypeFoldable<I>> ty::EarlyBinder<I, T> {
666666
self.value.fold_with(&mut folder)
667667
}
668668

669+
/// Tries to instantiate the early bound parameters with the given arguments.
670+
/// Returns an error instead of panicking when parameter instantiation fails.
671+
pub fn try_instantiate<A>(self, cx: I, args: A) -> Result<T, InstantiationError<I>>
672+
where
673+
A: SliceLike<Item = I::GenericArg>,
674+
{
675+
if args.is_empty() {
676+
if self.value.has_param() {
677+
return Err(InstantiationError::MissingArgs);
678+
}
679+
return Ok(self.value);
680+
}
681+
let mut folder = TryArgFolder { cx, args: args.as_slice(), binders_passed: 0 };
682+
self.value.try_fold_with(&mut folder)
683+
}
684+
669685
/// Makes the identity replacement `T0 => T0, ..., TN => TN`.
670686
/// Conceptually, this converts universally bound variables into placeholders
671687
/// when inside of a given item.
@@ -687,6 +703,238 @@ impl<I: Interner, T: TypeFoldable<I>> ty::EarlyBinder<I, T> {
687703
///////////////////////////////////////////////////////////////////////////
688704
// The actual instantiation engine itself is a type folder.
689705

706+
/// Error type for instantiation failures.
707+
pub enum InstantiationError<I: Interner> {
708+
/// A type parameter was referenced that is not in the provided args.
709+
TypeParamOutOfRange {
710+
param: I::ParamTy,
711+
param_index: usize,
712+
args_len: usize,
713+
},
714+
/// A const parameter was referenced that is not in the provided args.
715+
ConstParamOutOfRange {
716+
param: I::ParamConst,
717+
param_index: usize,
718+
args_len: usize,
719+
},
720+
/// A region parameter was referenced that is not in the provided args.
721+
RegionParamOutOfRange {
722+
param_index: usize,
723+
args_len: usize,
724+
},
725+
/// Expected a type but found a different kind.
726+
TypeParamExpected {
727+
param: I::ParamTy,
728+
found: ty::GenericArgKind<I>,
729+
},
730+
/// Expected a const but found a different kind.
731+
ConstParamExpected {
732+
param: I::ParamConst,
733+
found: ty::GenericArgKind<I>,
734+
},
735+
/// Expected a region but found a different kind.
736+
RegionParamExpected {
737+
param_index: usize,
738+
found: ty::GenericArgKind<I>,
739+
},
740+
/// No args were provided but the value has parameters.
741+
MissingArgs,
742+
}
743+
744+
impl<I: Interner> std::fmt::Debug for InstantiationError<I> {
745+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
746+
match self {
747+
InstantiationError::TypeParamOutOfRange { param, param_index, args_len } => f
748+
.debug_struct("TypeParamOutOfRange")
749+
.field("param", &format_args!("{:?}", param))
750+
.field("param_index", param_index)
751+
.field("args_len", args_len)
752+
.finish(),
753+
InstantiationError::ConstParamOutOfRange { param, param_index, args_len } => f
754+
.debug_struct("ConstParamOutOfRange")
755+
.field("param", &format_args!("{:?}", param))
756+
.field("param_index", param_index)
757+
.field("args_len", args_len)
758+
.finish(),
759+
InstantiationError::RegionParamOutOfRange { param_index, args_len } => f
760+
.debug_struct("RegionParamOutOfRange")
761+
.field("param_index", param_index)
762+
.field("args_len", args_len)
763+
.finish(),
764+
InstantiationError::TypeParamExpected { param, found } => f
765+
.debug_struct("TypeParamExpected")
766+
.field("param", &format_args!("{:?}", param))
767+
.field("found", &format_args!("{:?}", found))
768+
.finish(),
769+
InstantiationError::ConstParamExpected { param, found } => f
770+
.debug_struct("ConstParamExpected")
771+
.field("param", &format_args!("{:?}", param))
772+
.field("found", &format_args!("{:?}", found))
773+
.finish(),
774+
InstantiationError::RegionParamExpected { param_index, found } => f
775+
.debug_struct("RegionParamExpected")
776+
.field("param_index", param_index)
777+
.field("found", &format_args!("{:?}", found))
778+
.finish(),
779+
InstantiationError::MissingArgs => write!(f, "MissingArgs"),
780+
}
781+
}
782+
}
783+
784+
struct TryArgFolder<'a, I: Interner> {
785+
cx: I,
786+
args: &'a [I::GenericArg],
787+
binders_passed: u32,
788+
}
789+
790+
impl<'a, I: Interner> FallibleTypeFolder<I> for TryArgFolder<'a, I> {
791+
type Error = InstantiationError<I>;
792+
793+
#[inline]
794+
fn cx(&self) -> I {
795+
self.cx
796+
}
797+
798+
fn try_fold_binder<T: TypeFoldable<I>>(
799+
&mut self,
800+
t: ty::Binder<I, T>,
801+
) -> Result<ty::Binder<I, T>, Self::Error> {
802+
self.binders_passed += 1;
803+
let result = t.try_super_fold_with(self);
804+
self.binders_passed -= 1;
805+
result
806+
}
807+
808+
fn try_fold_region(&mut self, r: I::Region) -> Result<I::Region, Self::Error> {
809+
match r.kind() {
810+
ty::ReEarlyParam(data) => {
811+
let rk = self.args.get(data.index() as usize).map(|arg| arg.kind());
812+
match rk {
813+
Some(ty::GenericArgKind::Lifetime(lt)) => {
814+
Ok(self.shift_region_through_binders(lt))
815+
}
816+
Some(other) => Err(InstantiationError::RegionParamExpected {
817+
param_index: data.index() as usize,
818+
found: other,
819+
}),
820+
None => Err(InstantiationError::RegionParamOutOfRange {
821+
param_index: data.index() as usize,
822+
args_len: self.args.len(),
823+
}),
824+
}
825+
}
826+
ty::ReBound(..)
827+
| ty::ReLateParam(_)
828+
| ty::ReStatic
829+
| ty::RePlaceholder(_)
830+
| ty::ReErased
831+
| ty::ReError(_) => Ok(r),
832+
ty::ReVar(_) => panic!("unexpected region: {r:?}"),
833+
}
834+
}
835+
836+
fn try_fold_ty(&mut self, t: I::Ty) -> Result<I::Ty, Self::Error> {
837+
if !t.has_param() {
838+
return Ok(t);
839+
}
840+
841+
match t.kind() {
842+
ty::Param(p) => self.ty_for_param(p, t),
843+
_ => t.try_super_fold_with(self),
844+
}
845+
}
846+
847+
fn try_fold_const(&mut self, c: I::Const) -> Result<I::Const, Self::Error> {
848+
if let ty::ConstKind::Param(p) = c.kind() {
849+
self.const_for_param(p, c)
850+
} else {
851+
c.try_super_fold_with(self)
852+
}
853+
}
854+
855+
fn try_fold_predicate(&mut self, p: I::Predicate) -> Result<I::Predicate, Self::Error> {
856+
if p.has_param() {
857+
p.try_super_fold_with(self)
858+
} else {
859+
Ok(p)
860+
}
861+
}
862+
863+
fn try_fold_clauses(&mut self, c: I::Clauses) -> Result<I::Clauses, Self::Error> {
864+
if c.has_param() {
865+
c.try_super_fold_with(self)
866+
} else {
867+
Ok(c)
868+
}
869+
}
870+
}
871+
872+
impl<'a, I: Interner> TryArgFolder<'a, I> {
873+
fn ty_for_param(&self, p: I::ParamTy, _source_ty: I::Ty) -> Result<I::Ty, InstantiationError<I>> {
874+
let opt_ty = self.args.get(p.index() as usize).map(|arg| arg.kind());
875+
let ty = match opt_ty {
876+
Some(ty::GenericArgKind::Type(ty)) => ty,
877+
Some(kind) => {
878+
return Err(InstantiationError::TypeParamExpected {
879+
param: p,
880+
found: kind,
881+
})
882+
}
883+
None => {
884+
return Err(InstantiationError::TypeParamOutOfRange {
885+
param: p,
886+
param_index: p.index() as usize,
887+
args_len: self.args.len(),
888+
})
889+
}
890+
};
891+
892+
Ok(self.shift_vars_through_binders(ty))
893+
}
894+
895+
fn const_for_param(
896+
&self,
897+
p: I::ParamConst,
898+
_source_ct: I::Const,
899+
) -> Result<I::Const, InstantiationError<I>> {
900+
let opt_ct = self.args.get(p.index() as usize).map(|arg| arg.kind());
901+
let ct = match opt_ct {
902+
Some(ty::GenericArgKind::Const(ct)) => ct,
903+
Some(kind) => {
904+
return Err(InstantiationError::ConstParamExpected {
905+
param: p,
906+
found: kind,
907+
})
908+
}
909+
None => {
910+
return Err(InstantiationError::ConstParamOutOfRange {
911+
param: p,
912+
param_index: p.index() as usize,
913+
args_len: self.args.len(),
914+
})
915+
}
916+
};
917+
918+
Ok(self.shift_vars_through_binders(ct))
919+
}
920+
921+
fn shift_vars_through_binders<T: TypeFoldable<I>>(&self, val: T) -> T {
922+
if self.binders_passed == 0 || !val.has_escaping_bound_vars() {
923+
val
924+
} else {
925+
ty::shift_vars(self.cx, val, self.binders_passed)
926+
}
927+
}
928+
929+
fn shift_region_through_binders(&self, region: I::Region) -> I::Region {
930+
if self.binders_passed == 0 || !region.has_escaping_bound_vars() {
931+
region
932+
} else {
933+
ty::shift_region(self.cx, region, self.binders_passed)
934+
}
935+
}
936+
}
937+
690938
struct ArgFolder<'a, I: Interner> {
691939
cx: I,
692940
args: &'a [I::GenericArg],

0 commit comments

Comments
 (0)