1use std::iter;
8
9use flux_middle::{
10 queries::QueryErr,
11 rty::{
12 self, BaseTy, EarlyBinder, Expr, Mutability, Path, PolyFnSig, PtrKind, Ty, TyKind,
13 fold::{TypeFoldable, TypeFolder, TypeSuperFoldable},
14 },
15};
16use itertools::{Itertools, izip};
17use rustc_hir::def_id::DefId;
18use rustc_span::Span;
19
20use crate::{
21 infer::{ConstrReason, InferCtxt, InferResult, LocEnv, SubtypeReason},
22 projections::NormalizeExt as _,
23};
24
25#[derive(Debug)]
29pub enum SubFn {
30 Poly(DefId, EarlyBinder<rty::PolyFnSig>, rty::GenericArgs),
31 Mono(rty::PolyFnSig),
32}
33
34impl SubFn {
35 pub fn poly_sig(&self) -> &rty::PolyFnSig {
36 match self {
37 SubFn::Poly(_, sig, _) => sig.skip_binder_ref(),
38 SubFn::Mono(sig) => sig,
39 }
40 }
41}
42
43pub fn check_fn_subtyping(
59 infcx: &mut InferCtxt,
60 env: &mut impl LocEnv,
61 sub_sig: SubFn,
62 super_sig: &rty::PolyFnSig,
63 span: Span,
64) -> InferResult {
65 let mut infcx = infcx.branch();
66 let mut infcx = infcx.at(span);
67 let tcx = infcx.genv.tcx();
68
69 let super_sig = super_sig
70 .try_replace_bound_vars(
71 |_| Ok::<_, QueryErr>(rty::ReErased),
72 |sort, _, kind| {
73 let sort =
74 sort.deeply_normalize_sorts(infcx.def_id, infcx.genv, infcx.region_infcx)?;
75 Ok(Expr::fvar(infcx.define_bound_reft_var(&sort, kind)))
76 },
77 )?
78 .deeply_normalize(&mut infcx)?;
79
80 let actuals = super_sig
82 .inputs()
83 .iter()
84 .map(|ty| infcx.unpack(ty))
85 .collect_vec();
86
87 let actuals = unfold_local_ptrs(&mut infcx, env, sub_sig.poly_sig(), &actuals)?;
88 let actuals = infer_under_mut_ref_hack(&mut infcx, &actuals[..], sub_sig.poly_sig());
89
90 let output = infcx.ensure_resolved_evars(|infcx| {
91 let sub_sig = match sub_sig {
94 SubFn::Poly(def_id, early_sig, sub_args) => {
95 let refine_args = infcx.instantiate_refine_args(def_id, &sub_args)?;
96 early_sig.instantiate(tcx, &sub_args, &refine_args)
97 }
98 SubFn::Mono(sig) => sig,
99 };
100 let sub_sig = sub_sig
102 .try_replace_bound_vars(
103 |_| Ok::<_, QueryErr>(rty::ReErased),
104 |sort, mode, _| {
105 let sort =
106 sort.deeply_normalize_sorts(infcx.def_id, infcx.genv, infcx.region_infcx)?;
107 Ok(infcx.fresh_infer_var(&sort, mode))
108 },
109 )?
110 .deeply_normalize(infcx)?;
111
112 for requires in super_sig.requires() {
114 infcx.assume_pred(requires);
115 }
116 infcx.check_pred(
117 Expr::implies(super_sig.no_panic(), sub_sig.no_panic()),
118 ConstrReason::Subtype(SubtypeReason::Input),
119 );
120 for (actual, formal) in iter::zip(actuals, sub_sig.inputs()) {
121 let reason = ConstrReason::Subtype(SubtypeReason::Input);
122 infcx.subtyping_with_env(env, &actual, formal, reason)?;
123 }
124 for requires in sub_sig.requires() {
127 let reason = ConstrReason::Subtype(SubtypeReason::Requires);
128 infcx.check_pred(requires, reason);
129 }
130
131 Ok(sub_sig.output())
132 })?;
133
134 let output = infcx
135 .fully_resolve_evars(&output)
136 .replace_bound_refts_with(|sort, _, kind| {
137 Expr::fvar(infcx.define_bound_reft_var(sort, kind))
138 });
139
140 infcx.ensure_resolved_evars(|infcx| {
142 let super_output = super_sig
143 .output()
144 .replace_bound_refts_with(|sort, mode, _| infcx.fresh_infer_var(sort, mode));
145 let reason = ConstrReason::Subtype(SubtypeReason::Output);
146 infcx.subtyping(&output.ret, &super_output.ret, reason)?;
147
148 env.assume_ensures(infcx, &output.ensures, span);
150 env.fold_local_ptrs(infcx)?;
151 env.check_ensures(
152 infcx,
153 &super_output.ensures,
154 ConstrReason::Subtype(SubtypeReason::Ensures),
155 )
156 })
157}
158
159pub fn unfold_local_ptrs(
170 infcx: &mut InferCtxt,
171 env: &mut impl LocEnv,
172 fn_sig: &PolyFnSig,
173 actuals: &[Ty],
174) -> InferResult<Vec<Ty>> {
175 let fn_sig = fn_sig.skip_binder_ref();
177 let mut tys = vec![];
178 for (actual, input) in izip!(actuals, fn_sig.inputs()) {
179 let actual = if let (
180 TyKind::Indexed(BaseTy::Ref(re, bound, Mutability::Mut), _),
181 TyKind::StrgRef(_, _, _),
182 ) = (actual.kind(), input.kind())
183 {
184 let loc = env.unfold_local_ptr(infcx, bound)?;
185 let path1 = Path::new(loc, rty::List::empty());
186 Ty::ptr(PtrKind::Mut(*re), path1)
187 } else {
188 actual.clone()
189 };
190 tys.push(actual);
191 }
192 Ok(tys)
193}
194
195struct SkipConstr;
196
197impl TypeFolder for SkipConstr {
198 fn fold_ty(&mut self, ty: &rty::Ty) -> rty::Ty {
199 if let rty::TyKind::Constr(_, inner_ty) = ty.kind() {
200 inner_ty.fold_with(self)
201 } else {
202 ty.super_fold_with(self)
203 }
204 }
205}
206
207fn is_indexed_mut_skipping_constr(ty: &Ty) -> bool {
208 let ty = SkipConstr.fold_ty(ty);
209 if let rty::Ref!(_, inner_ty, Mutability::Mut) = ty.kind()
210 && let TyKind::Indexed(..) = inner_ty.kind()
211 {
212 true
213 } else {
214 false
215 }
216}
217
218pub fn infer_under_mut_ref_hack(
223 rcx: &mut InferCtxt,
224 actuals: &[Ty],
225 fn_sig: &PolyFnSig,
226) -> Vec<Ty> {
227 iter::zip(actuals, fn_sig.skip_binder_ref().inputs())
228 .map(|(actual, formal)| {
229 if let rty::Ref!(re, deref_ty, Mutability::Mut) = actual.kind()
230 && is_indexed_mut_skipping_constr(formal)
231 {
232 rty::Ty::mk_ref(*re, rcx.unpack(deref_ty), Mutability::Mut)
233 } else {
234 actual.clone()
235 }
236 })
237 .collect()
238}