diff --git a/src/analyze/basic_block/visitor/rust_call.rs b/src/analyze/basic_block/visitor/rust_call.rs index b46c0467..191f5b14 100644 --- a/src/analyze/basic_block/visitor/rust_call.rs +++ b/src/analyze/basic_block/visitor/rust_call.rs @@ -108,6 +108,11 @@ impl<'a, 'tcx, 'ctx> mir::visit::MutVisitor<'tcx> for RustCallVisitor<'a, 'tcx, let borrowed_closure_local = self.insert_immut_borrow(arg_closure_place, arg_closure_ty); args[0].node = mir::Operand::Copy(borrowed_closure_local.into()); + // As in case 3, the closure is no longer consumed by the call, so its + // drop obligation, which `moved_locals` stole, must be restored. + if arg_closure_place.projection.is_empty() { + self.analyzer.drop_after_terminator(arg_closure_place.local); + } tracing::debug!("applied immut-borrow for closure argument"); } } else if matches!( diff --git a/src/analyze/crate_.rs b/src/analyze/crate_.rs index fe0a3271..c12ae6f1 100644 --- a/src/analyze/crate_.rs +++ b/src/analyze/crate_.rs @@ -114,6 +114,7 @@ impl<'tcx, 'ctx> Analyzer<'tcx, 'ctx> { #[tracing::instrument(skip(self), fields(def_id = %self.tcx.def_path_str(local_def_id)))] fn refine_fn_def(&mut self, local_def_id: LocalDefId) { let sig = self.ctx.fn_sig(local_def_id.to_def_id()); + let has_constrained_type_param = !self.constrained_type_params(local_def_id).is_empty(); let mut analyzer = self.ctx.local_def_analyzer(local_def_id); @@ -147,7 +148,9 @@ impl<'tcx, 'ctx> Analyzer<'tcx, 'ctx> { }; use mir_ty::TypeVisitableExt as _; - if sig.has_param() { + // A trait-bounded type parameter must stay generic even when the signature does not + // mention it, since the body depends on which impl it is instantiated with. + if sig.has_param() || has_constrained_type_param { // TODO: needs clear criteria on whether extern_spec'ed target fn is analyzed or not if target_def_id.as_local().is_none_or(|def_id| { self.skip_analysis.contains(&def_id) || !self.tcx.is_mir_available(def_id) @@ -196,11 +199,16 @@ impl<'tcx, 'ctx> Analyzer<'tcx, 'ctx> { } } - fn placeholder_generic_args(&self, local_def_id: LocalDefId) -> mir_ty::GenericArgsRef<'tcx> { + /// Indices of the type parameters bound by a trait other than `Sized`, including bounds + /// inherited from the parent impl or trait. + fn constrained_type_params(&self, local_def_id: LocalDefId) -> HashSet { let mut constrained_params = HashSet::new(); - let predicates = self.tcx.predicates_of(local_def_id); + let predicates = self + .tcx + .predicates_of(local_def_id) + .instantiate_identity(self.tcx); let sized_trait = self.tcx.lang_items().sized_trait().unwrap(); - for (clause, _) in predicates.predicates { + for clause in predicates.predicates { let mir_ty::ClauseKind::Trait(pred) = clause.kind().skip_binder() else { continue; }; @@ -217,6 +225,11 @@ impl<'tcx, 'ctx> Analyzer<'tcx, 'ctx> { constrained_params.insert(param_ty.index); } } + constrained_params + } + + fn placeholder_generic_args(&self, local_def_id: LocalDefId) -> mir_ty::GenericArgsRef<'tcx> { + let constrained_params = self.constrained_type_params(local_def_id); let mut args: Vec> = Vec::new(); diff --git a/tests/ui/fail/closure_fn_once_fn_kind.rs b/tests/ui/fail/closure_fn_once_fn_kind.rs new file mode 100644 index 00000000..520bf3f3 --- /dev/null +++ b/tests/ui/fail/closure_fn_once_fn_kind.rs @@ -0,0 +1,15 @@ +//@error-in-other-file: Unsat +//@compile-flags: -C debug-assertions=off + +fn call_once i64>(f: F) -> i64 { + f() +} + +fn main() { + let mut x = 1; + let r = &mut x; + *r = 5; + let c = move || *r; + call_once(c); + assert!(x == 6); +} diff --git a/tests/ui/fail/generic_impl_static_method.rs b/tests/ui/fail/generic_impl_static_method.rs new file mode 100644 index 00000000..a47ceec5 --- /dev/null +++ b/tests/ui/fail/generic_impl_static_method.rs @@ -0,0 +1,30 @@ +//@error-in-other-file: Unsat +//@compile-flags: -C debug-assertions=off + +trait K { + fn k() -> i64; +} + +impl K for i32 { + fn k() -> i64 { + 0 + } +} + +impl K for i64 { + fn k() -> i64 { + 1 + } +} + +struct W(T); + +impl W { + fn kk() -> i64 { + T::k() + } +} + +fn main() { + assert!(W::::kk() == 0); +} diff --git a/tests/ui/pass/closure_fn_once_fn_kind.rs b/tests/ui/pass/closure_fn_once_fn_kind.rs new file mode 100644 index 00000000..6f54f630 --- /dev/null +++ b/tests/ui/pass/closure_fn_once_fn_kind.rs @@ -0,0 +1,15 @@ +//@check-pass +//@compile-flags: -C debug-assertions=off + +fn call_once i64>(f: F) -> i64 { + f() +} + +fn main() { + let mut x = 1; + let r = &mut x; + *r = 5; + let c = move || *r; + call_once(c); + assert!(x == 5); +} diff --git a/tests/ui/pass/generic_impl_static_method.rs b/tests/ui/pass/generic_impl_static_method.rs new file mode 100644 index 00000000..dd2c65b6 --- /dev/null +++ b/tests/ui/pass/generic_impl_static_method.rs @@ -0,0 +1,30 @@ +//@check-pass +//@compile-flags: -C debug-assertions=off + +trait K { + fn k() -> i64; +} + +impl K for i32 { + fn k() -> i64 { + 0 + } +} + +impl K for i64 { + fn k() -> i64 { + 1 + } +} + +struct W(T); + +impl W { + fn kk() -> i64 { + T::k() + } +} + +fn main() { + assert!(W::::kk() == 1); +}