about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-05-06 16:28:43 +1000
committerGitHub <noreply@github.com>2025-05-06 16:28:43 +1000
commitf14b4e69244c90096674b90b05d84b9914e8386a (patch)
treed9233a2832b69f3a7719ddfabb661b1ad6e762d6
parentd36e719fbca5c0a5654da12f84fead340074e661 (diff)
parentc0dfa44c485bb8a7a30f2b4b69827028b3d55c98 (diff)
downloadrust-f14b4e69244c90096674b90b05d84b9914e8386a.tar.gz
rust-f14b4e69244c90096674b90b05d84b9914e8386a.zip
Rollup merge of #140678 - compiler-errors:dont-ice-on-infer-in-upvar, r=lcnr
Be a bit more relaxed about not yet constrained infer vars in closure upvar analysis

See the writeup in `tests/ui/closures/opaque-upvar.rs`.

TL;DR is that this has to do with the fact that the recursive revealing uses, which have not yet been constrained from the defining use by the time that closure upvar inference is performed, remain as infer vars during upvar analysis. We don't really care, though, since anywhere we structurally match on a type in upvar analysis, we already call `structurally_resolve_type` right before `.kind()`, which would emit a true ambiguity error.

Fixes https://github.com/rust-lang/trait-system-refactor-initiative/issues/197

r? lcnr
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs49
-rw-r--r--tests/crashes/131758.rs11
-rw-r--r--tests/ui/closures/opaque-upvar.rs19
-rw-r--r--tests/ui/unboxed-closures/arg-constrained-after-closure-inference.rs16
4 files changed, 54 insertions, 41 deletions
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index 17e13ec0a37..3493d359028 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -158,7 +158,7 @@ pub trait TypeInformationCtxt<'tcx> {
 
     fn resolve_vars_if_possible<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T;
 
-    fn try_structurally_resolve_type(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>;
+    fn structurally_resolve_type(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>;
 
     fn report_bug(&self, span: Span, msg: impl ToString) -> Self::Error;
 
@@ -191,8 +191,8 @@ impl<'tcx> TypeInformationCtxt<'tcx> for &FnCtxt<'_, 'tcx> {
         self.infcx.resolve_vars_if_possible(t)
     }
 
-    fn try_structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
-        (**self).try_structurally_resolve_type(sp, ty)
+    fn structurally_resolve_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+        (**self).structurally_resolve_type(sp, ty)
     }
 
     fn report_bug(&self, span: Span, msg: impl ToString) -> Self::Error {
@@ -236,7 +236,7 @@ impl<'tcx> TypeInformationCtxt<'tcx> for (&LateContext<'tcx>, LocalDefId) {
         self.0.maybe_typeck_results().expect("expected typeck results")
     }
 
-    fn try_structurally_resolve_type(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+    fn structurally_resolve_type(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
         // FIXME: Maybe need to normalize here.
         ty
     }
@@ -776,7 +776,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
 
         // Select just those fields of the `with`
         // expression that will actually be used
-        match self.cx.try_structurally_resolve_type(with_expr.span, with_place.place.ty()).kind() {
+        match self.cx.structurally_resolve_type(with_expr.span, with_place.place.ty()).kind() {
             ty::Adt(adt, args) if adt.is_struct() => {
                 // Consume those fields of the with expression that are needed.
                 for (f_index, with_field) in adt.non_enum_variant().fields.iter_enumerated() {
@@ -1176,7 +1176,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
 /// two operations: a dereference to reach the array data and then an index to
 /// jump forward to the relevant item.
 impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx, Cx, D> {
-    fn resolve_type_vars_or_bug(
+    fn expect_and_resolve_type(
         &self,
         id: HirId,
         ty: Option<Ty<'tcx>>,
@@ -1185,12 +1185,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
             Some(ty) => {
                 let ty = self.cx.resolve_vars_if_possible(ty);
                 self.cx.error_reported_in_ty(ty)?;
-                if ty.is_ty_var() {
-                    debug!("resolve_type_vars_or_bug: infer var from {:?}", ty);
-                    Err(self.cx.report_bug(self.cx.tcx().hir_span(id), "encountered type variable"))
-                } else {
-                    Ok(ty)
-                }
+                Ok(ty)
             }
             None => {
                 // FIXME: We shouldn't be relying on the infcx being tainted.
@@ -1201,15 +1196,15 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
     }
 
     fn node_ty(&self, hir_id: HirId) -> Result<Ty<'tcx>, Cx::Error> {
-        self.resolve_type_vars_or_bug(hir_id, self.cx.typeck_results().node_type_opt(hir_id))
+        self.expect_and_resolve_type(hir_id, self.cx.typeck_results().node_type_opt(hir_id))
     }
 
     fn expr_ty(&self, expr: &hir::Expr<'_>) -> Result<Ty<'tcx>, Cx::Error> {
-        self.resolve_type_vars_or_bug(expr.hir_id, self.cx.typeck_results().expr_ty_opt(expr))
+        self.expect_and_resolve_type(expr.hir_id, self.cx.typeck_results().expr_ty_opt(expr))
     }
 
     fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> Result<Ty<'tcx>, Cx::Error> {
-        self.resolve_type_vars_or_bug(
+        self.expect_and_resolve_type(
             expr.hir_id,
             self.cx.typeck_results().expr_ty_adjusted_opt(expr),
         )
@@ -1264,10 +1259,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
                     // a bind-by-ref means that the base_ty will be the type of the ident itself,
                     // but what we want here is the type of the underlying value being borrowed.
                     // So peel off one-level, turning the &T into T.
-                    match self
-                        .cx
-                        .try_structurally_resolve_type(pat.span, base_ty)
-                        .builtin_deref(false)
+                    match self.cx.structurally_resolve_type(pat.span, base_ty).builtin_deref(false)
                     {
                         Some(ty) => Ok(ty),
                         None => {
@@ -1513,10 +1505,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
             if node_ty != place_ty
                 && self
                     .cx
-                    .try_structurally_resolve_type(
-                        self.cx.tcx().hir_span(base_place.hir_id),
-                        place_ty,
-                    )
+                    .structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), place_ty)
                     .is_impl_trait()
             {
                 projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty });
@@ -1538,7 +1527,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         let base_ty = self.expr_ty_adjusted(base)?;
 
         let ty::Ref(region, _, mutbl) =
-            *self.cx.try_structurally_resolve_type(base.span, base_ty).kind()
+            *self.cx.structurally_resolve_type(base.span, base_ty).kind()
         else {
             span_bug!(expr.span, "cat_overloaded_place: base is not a reference");
         };
@@ -1556,7 +1545,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         let base_curr_ty = base_place.place.ty();
         let deref_ty = match self
             .cx
-            .try_structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), base_curr_ty)
+            .structurally_resolve_type(self.cx.tcx().hir_span(base_place.hir_id), base_curr_ty)
             .builtin_deref(true)
         {
             Some(ty) => ty,
@@ -1584,7 +1573,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
     ) -> Result<VariantIdx, Cx::Error> {
         let res = self.cx.typeck_results().qpath_res(qpath, pat_hir_id);
         let ty = self.cx.typeck_results().node_type(pat_hir_id);
-        let ty::Adt(adt_def, _) = self.cx.try_structurally_resolve_type(span, ty).kind() else {
+        let ty::Adt(adt_def, _) = self.cx.structurally_resolve_type(span, ty).kind() else {
             return Err(self
                 .cx
                 .report_bug(span, "struct or tuple struct pattern not applied to an ADT"));
@@ -1616,7 +1605,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
         span: Span,
     ) -> Result<usize, Cx::Error> {
         let ty = self.cx.typeck_results().node_type(pat_hir_id);
-        match self.cx.try_structurally_resolve_type(span, ty).kind() {
+        match self.cx.structurally_resolve_type(span, ty).kind() {
             ty::Adt(adt_def, _) => Ok(adt_def.variant(variant_index).fields.len()),
             _ => {
                 self.cx
@@ -1631,7 +1620,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
     /// Here `pat_hir_id` is the HirId of the pattern itself.
     fn total_fields_in_tuple(&self, pat_hir_id: HirId, span: Span) -> Result<usize, Cx::Error> {
         let ty = self.cx.typeck_results().node_type(pat_hir_id);
-        match self.cx.try_structurally_resolve_type(span, ty).kind() {
+        match self.cx.structurally_resolve_type(span, ty).kind() {
             ty::Tuple(args) => Ok(args.len()),
             _ => Err(self.cx.report_bug(span, "tuple pattern not applied to a tuple")),
         }
@@ -1820,7 +1809,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
             PatKind::Slice(before, ref slice, after) => {
                 let Some(element_ty) = self
                     .cx
-                    .try_structurally_resolve_type(pat.span, place_with_id.place.ty())
+                    .structurally_resolve_type(pat.span, place_with_id.place.ty())
                     .builtin_index()
                 else {
                     debug!("explicit index of non-indexable type {:?}", place_with_id);
@@ -1890,7 +1879,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
     }
 
     fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool {
-        if let ty::Adt(def, _) = self.cx.try_structurally_resolve_type(span, ty).kind() {
+        if let ty::Adt(def, _) = self.cx.structurally_resolve_type(span, ty).kind() {
             // Note that if a non-exhaustive SingleVariant is defined in another crate, we need
             // to assume that more cases will be added to the variant in the future. This mean
             // that we should handle non-exhaustive SingleVariant the same way we would handle
diff --git a/tests/crashes/131758.rs b/tests/crashes/131758.rs
deleted file mode 100644
index 942c5fd7a50..00000000000
--- a/tests/crashes/131758.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//@ known-bug: #131758
-#![feature(unboxed_closures)]
-trait Foo {}
-
-impl<T: Fn<(i32,)>> Foo for T {}
-
-fn baz<T: Foo>(_: T) {}
-
-fn main() {
-    baz(|x| ());
-}
diff --git a/tests/ui/closures/opaque-upvar.rs b/tests/ui/closures/opaque-upvar.rs
new file mode 100644
index 00000000000..90e7c9ccb46
--- /dev/null
+++ b/tests/ui/closures/opaque-upvar.rs
@@ -0,0 +1,19 @@
+//@ check-pass
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+
+// Regression test for <https://github.com/rust-lang/trait-system-refactor-initiative/issues/197>.
+// This is only an issue in the new solver, but I'm testing it in both solvers for now.
+// This has to do with the fact that the recursive `walk_dir` is a revealing use, which has not
+// yet been constrained from the defining use by the time that closure signature inference is
+// performed. We don't really care, though, since anywhere we structurally match on a type in
+// upvar analysis, we already call `structurally_resolve_type` right before `.kind()`.
+
+fn walk_dir(cb: &()) -> impl Sized {
+    || {
+        let fut = walk_dir(cb);
+    };
+}
+
+fn main() {}
diff --git a/tests/ui/unboxed-closures/arg-constrained-after-closure-inference.rs b/tests/ui/unboxed-closures/arg-constrained-after-closure-inference.rs
new file mode 100644
index 00000000000..343a27616d1
--- /dev/null
+++ b/tests/ui/unboxed-closures/arg-constrained-after-closure-inference.rs
@@ -0,0 +1,16 @@
+#![feature(unboxed_closures)]
+
+//@ check-pass
+
+// Regression test for #131758. We only know the type of `x` after closure upvar
+// inference is done, even if we don't need to structurally resolve the type of `x`.
+
+trait Foo {}
+
+impl<T: Fn<(i32,)>> Foo for T {}
+
+fn baz<T: Foo>(_: T) {}
+
+fn main() {
+    baz(|x| ());
+}