about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-07-04 17:39:37 +0000
committerbors <bors@rust-lang.org>2021-07-04 17:39:37 +0000
commit23c652dfe3043369e6f23a79f8efb77f391c0e64 (patch)
treefad97ad30b360c54055b8583fb00f4cc55efd9f5
parent90442458ac46b1d5eed752c316da25450f67285b (diff)
parent492ba34a91612005336b8fbcc3cc16447ebc343a (diff)
downloadrust-23c652dfe3043369e6f23a79f8efb77f391c0e64.tar.gz
rust-23c652dfe3043369e6f23a79f8efb77f391c0e64.zip
Auto merge of #86866 - nikomatsakis:issue-84841, r=oli-obk
Hack: Ignore inference variables in certain queries

Fixes #84841
Fixes #86753

Some queries are not built to accept types with inference variables, which can lead to ICEs. These queries probably ought to be converted to canonical form, but as a quick workaround, we can return conservative results in the case that inference variables are found.

We should file a follow-up issue (and update the FIXMEs...) to do the proper refactoring.

cc `@arora-aman`

r? `@oli-obk`
-rw-r--r--compiler/rustc_infer/src/infer/canonical/canonicalizer.rs29
-rw-r--r--compiler/rustc_middle/src/query/mod.rs15
-rw-r--r--compiler/rustc_middle/src/ty/normalize_erasing_regions.rs17
-rw-r--r--compiler/rustc_middle/src/ty/util.rs9
-rw-r--r--compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs26
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs16
-rw-r--r--compiler/rustc_typeck/src/check/cast.rs14
-rw-r--r--compiler/rustc_typeck/src/check/upvar.rs14
-rw-r--r--src/test/ui/async-await/issue-84841.rs16
-rw-r--r--src/test/ui/async-await/issue-84841.stderr28
-rw-r--r--src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs34
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs4
13 files changed, 168 insertions, 58 deletions
diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
index c68705da413..ac953f4305c 100644
--- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
+++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
@@ -46,13 +46,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
     {
         self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed);
 
-        Canonicalizer::canonicalize(
-            value,
-            Some(self),
-            self.tcx,
-            &CanonicalizeAllFreeRegions,
-            query_state,
-        )
+        Canonicalizer::canonicalize(value, self, self.tcx, &CanonicalizeAllFreeRegions, query_state)
     }
 
     /// Canonicalizes a query *response* `V`. When we canonicalize a
@@ -87,7 +81,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
         let mut query_state = OriginalQueryValues::default();
         Canonicalizer::canonicalize(
             value,
-            Some(self),
+            self,
             self.tcx,
             &CanonicalizeQueryResponse,
             &mut query_state,
@@ -101,7 +95,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
         let mut query_state = OriginalQueryValues::default();
         Canonicalizer::canonicalize(
             value,
-            Some(self),
+            self,
             self.tcx,
             &CanonicalizeUserTypeAnnotation,
             &mut query_state,
@@ -133,7 +127,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
 
         Canonicalizer::canonicalize(
             value,
-            Some(self),
+            self,
             self.tcx,
             &CanonicalizeFreeRegionsOtherThanStatic,
             query_state,
@@ -275,7 +269,7 @@ impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic {
 }
 
 struct Canonicalizer<'cx, 'tcx> {
-    infcx: Option<&'cx InferCtxt<'cx, 'tcx>>,
+    infcx: &'cx InferCtxt<'cx, 'tcx>,
     tcx: TyCtxt<'tcx>,
     variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>,
     query_state: &'cx mut OriginalQueryValues<'tcx>,
@@ -316,7 +310,6 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
             ty::ReVar(vid) => {
                 let resolved_vid = self
                     .infcx
-                    .unwrap()
                     .inner
                     .borrow_mut()
                     .unwrap_region_constraints()
@@ -343,7 +336,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
         match *t.kind() {
             ty::Infer(ty::TyVar(vid)) => {
                 debug!("canonical: type var found with vid {:?}", vid);
-                match self.infcx.unwrap().probe_ty_var(vid) {
+                match self.infcx.probe_ty_var(vid) {
                     // `t` could be a float / int variable; canonicalize that instead.
                     Ok(t) => {
                         debug!("(resolved to {:?})", t);
@@ -429,7 +422,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
         match ct.val {
             ty::ConstKind::Infer(InferConst::Var(vid)) => {
                 debug!("canonical: const var found with vid {:?}", vid);
-                match self.infcx.unwrap().probe_const_var(vid) {
+                match self.infcx.probe_const_var(vid) {
                     Ok(c) => {
                         debug!("(resolved to {:?})", c);
                         return self.fold_const(c);
@@ -476,7 +469,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
     /// `canonicalize_query` and `canonicalize_response`.
     fn canonicalize<V>(
         value: V,
-        infcx: Option<&InferCtxt<'_, 'tcx>>,
+        infcx: &InferCtxt<'_, 'tcx>,
         tcx: TyCtxt<'tcx>,
         canonicalize_region_mode: &dyn CanonicalizeRegionMode,
         query_state: &mut OriginalQueryValues<'tcx>,
@@ -610,7 +603,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
 
     /// Returns the universe in which `vid` is defined.
     fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex {
-        self.infcx.unwrap().inner.borrow_mut().unwrap_region_constraints().var_universe(vid)
+        self.infcx.inner.borrow_mut().unwrap_region_constraints().var_universe(vid)
     }
 
     /// Creates a canonical variable (with the given `info`)
@@ -631,7 +624,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
     /// *that*. Otherwise, create a new canonical variable for
     /// `ty_var`.
     fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo<'tcx>, ty_var: Ty<'tcx>) -> Ty<'tcx> {
-        let infcx = self.infcx.expect("encountered ty-var without infcx");
+        let infcx = self.infcx;
         let bound_to = infcx.shallow_resolve(ty_var);
         if bound_to != ty_var {
             self.fold_ty(bound_to)
@@ -650,7 +643,7 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
         info: CanonicalVarInfo<'tcx>,
         const_var: &'tcx ty::Const<'tcx>,
     ) -> &'tcx ty::Const<'tcx> {
-        let infcx = self.infcx.expect("encountered const-var without infcx");
+        let infcx = self.infcx;
         let bound_to = infcx.shallow_resolve(const_var);
         if bound_to != const_var {
             self.fold_const(bound_to)
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 23ee0e05062..0986a465729 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1559,9 +1559,22 @@ rustc_queries! {
         desc { "evaluating trait selection obligation `{}`", goal.value }
     }
 
+    /// Evaluates whether the given type implements the given trait
+    /// in the given environment.
+    ///
+    /// The inputs are:
+    ///
+    /// - the def-id of the trait
+    /// - the self type
+    /// - the *other* type parameters of the trait, excluding the self-type
+    /// - the parameter environment
+    ///
+    /// FIXME. If the type, trait, or environment has inference variables,
+    /// this yields `EvaluatedToUnknown`. It should be refactored
+    /// to use canonicalization, really.
     query type_implements_trait(
         key: (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>, )
-    ) -> bool {
+    ) -> traits::EvaluationResult {
         desc { "evaluating `type_implements_trait` `{:?}`", key }
     }
 
diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs
index a4f736654af..11399506b96 100644
--- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs
+++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs
@@ -88,23 +88,32 @@ struct NormalizeAfterErasingRegionsFolder<'tcx> {
     param_env: ty::ParamEnv<'tcx>,
 }
 
+impl<'tcx> NormalizeAfterErasingRegionsFolder<'tcx> {
+    fn normalize_generic_arg_after_erasing_regions(
+        &self,
+        arg: ty::GenericArg<'tcx>,
+    ) -> ty::GenericArg<'tcx> {
+        let arg = self.param_env.and(arg);
+        self.tcx.normalize_generic_arg_after_erasing_regions(arg)
+    }
+}
+
 impl TypeFolder<'tcx> for NormalizeAfterErasingRegionsFolder<'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.tcx
     }
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        let arg = self.param_env.and(ty.into());
-        self.tcx.normalize_generic_arg_after_erasing_regions(arg).expect_ty()
+        self.normalize_generic_arg_after_erasing_regions(ty.into()).expect_ty()
     }
 
     fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
-        let arg = self.param_env.and(c.into());
-        self.tcx.normalize_generic_arg_after_erasing_regions(arg).expect_const()
+        self.normalize_generic_arg_after_erasing_regions(c.into()).expect_const()
     }
 
     #[inline]
     fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> {
+        // FIXME: This *probably* needs canonicalization too!
         let arg = self.param_env.and(c);
         self.tcx.normalize_mir_const_after_erasing_regions(arg)
     }
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 24a9cea6d21..6344327e6aa 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -816,6 +816,15 @@ impl<'tcx> ty::TyS<'tcx> {
                     [component_ty] => component_ty,
                     _ => self,
                 };
+
+                // FIXME(#86868): We should be canonicalizing, or else moving this to a method of inference
+                // context, or *something* like that, but for now just avoid passing inference
+                // variables to queries that can't cope with them. Instead, conservatively
+                // return "true" (may change drop order).
+                if query_ty.needs_infer() {
+                    return true;
+                }
+
                 // This doesn't depend on regions, so try to minimize distinct
                 // query keys used.
                 let erased = tcx.normalize_erasing_regions(param_env, query_ty);
diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
index a0c9b43d5af..4be4372c039 100644
--- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
@@ -9,7 +9,7 @@ use rustc_middle::mir::{
     FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
     ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm,
 };
-use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TypeFoldable};
+use rustc_middle::ty::{self, suggest_constraining_type_param, Ty};
 use rustc_span::source_map::DesugaringKind;
 use rustc_span::symbol::sym;
 use rustc_span::{Span, DUMMY_SP};
@@ -1329,18 +1329,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             let return_ty = tcx.erase_regions(return_ty);
 
             // to avoid panics
-            if !return_ty.has_infer_types() {
-                if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
-                    if tcx.type_implements_trait((iter_trait, return_ty, ty_params, self.param_env))
-                    {
-                        if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
-                            err.span_suggestion_hidden(
-                                return_span,
-                                "use `.collect()` to allocate the iterator",
-                                format!("{}{}", snippet, ".collect::<Vec<_>>()"),
-                                Applicability::MaybeIncorrect,
-                            );
-                        }
+            if let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) {
+                if tcx
+                    .type_implements_trait((iter_trait, return_ty, ty_params, self.param_env))
+                    .must_apply_modulo_regions()
+                {
+                    if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(return_span) {
+                        err.span_suggestion_hidden(
+                            return_span,
+                            "use `.collect()` to allocate the iterator",
+                            format!("{}{}", snippet, ".collect::<Vec<_>>()"),
+                            Applicability::MaybeIncorrect,
+                        );
                     }
                 }
             }
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 5c35b515f3d..dc765f5228b 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -2396,7 +2396,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     normalized_ty,
                 );
                 debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation);
-                if self.predicate_may_hold(&try_obligation) && impls_future {
+                if self.predicate_may_hold(&try_obligation)
+                    && impls_future.must_apply_modulo_regions()
+                {
                     if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
                         if snippet.ends_with('?') {
                             err.span_suggestion_verbose(
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index d65a596a827..e48aab6f46f 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -542,8 +542,7 @@ fn vtable_trait_first_method_offset<'tcx>(
 }
 
 /// Check whether a `ty` implements given trait(trait_def_id).
-///
-/// NOTE: Always return `false` for a type which needs inference.
+/// See query definition for details.
 fn type_implements_trait<'tcx>(
     tcx: TyCtxt<'tcx>,
     key: (
@@ -552,7 +551,7 @@ fn type_implements_trait<'tcx>(
         SubstsRef<'tcx>,
         ParamEnv<'tcx>,
     ),
-) -> bool {
+) -> EvaluationResult {
     let (trait_def_id, ty, params, param_env) = key;
 
     debug!(
@@ -562,13 +561,22 @@ fn type_implements_trait<'tcx>(
 
     let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, params) };
 
+    // FIXME(#86868): If there are inference variables anywhere, just give up and assume
+    // we don't know the answer. This works around the ICEs that would result from
+    // using those inference variables within the `infer_ctxt` we create below.
+    // Really we should be using canonicalized variables, or perhaps removing
+    // this query altogether.
+    if (trait_ref, param_env).needs_infer() {
+        return EvaluationResult::EvaluatedToUnknown;
+    }
+
     let obligation = Obligation {
         cause: ObligationCause::dummy(),
         param_env,
         recursion_depth: 0,
         predicate: trait_ref.without_const().to_predicate(tcx),
     };
-    tcx.infer_ctxt().enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
+    tcx.infer_ctxt().enter(|infcx| infcx.evaluate_obligation_no_overflow(&obligation))
 }
 
 pub fn provide(providers: &mut ty::query::Providers) {
diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs
index 3cbc3d231f8..a09fad3b6b7 100644
--- a/compiler/rustc_typeck/src/check/cast.rs
+++ b/compiler/rustc_typeck/src/check/cast.rs
@@ -440,16 +440,10 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                             let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
                             let expr_ty = fcx.tcx.erase_regions(expr_ty);
                             let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]);
-                            // Check for infer types because cases like `Option<{integer}>` would
-                            // panic otherwise.
-                            if !expr_ty.has_infer_types()
-                                && !ty.has_infer_types()
-                                && fcx.tcx.type_implements_trait((
-                                    from_trait,
-                                    ty,
-                                    ty_params,
-                                    fcx.param_env,
-                                ))
+                            if fcx
+                                .tcx
+                                .type_implements_trait((from_trait, ty, ty_params, fcx.param_env))
+                                .must_apply_modulo_regions()
                             {
                                 label = false;
                                 err.span_suggestion(
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index 41b4172781c..e5f18778f43 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -961,12 +961,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let is_drop_defined_for_ty = |ty: Ty<'tcx>| {
             let drop_trait = self.tcx.require_lang_item(hir::LangItem::Drop, Some(closure_span));
             let ty_params = self.tcx.mk_substs_trait(base_path_ty, &[]);
-            self.tcx.type_implements_trait((
-                drop_trait,
-                ty,
-                ty_params,
-                self.tcx.param_env(closure_def_id.expect_local()),
-            ))
+            self.tcx
+                .type_implements_trait((
+                    drop_trait,
+                    ty,
+                    ty_params,
+                    self.tcx.param_env(closure_def_id.expect_local()),
+                ))
+                .must_apply_modulo_regions()
         };
 
         let is_drop_defined_for_ty = is_drop_defined_for_ty(base_path_ty);
diff --git a/src/test/ui/async-await/issue-84841.rs b/src/test/ui/async-await/issue-84841.rs
new file mode 100644
index 00000000000..ba3a1617b9c
--- /dev/null
+++ b/src/test/ui/async-await/issue-84841.rs
@@ -0,0 +1,16 @@
+// edition:2018
+
+fn main() {
+
+}
+
+async fn foo() {
+    // Adding an .await here avoids the ICE
+    test()?;
+    //~^ ERROR the `?` operator can only be applied to values that implement `Try`
+    //~| ERROR the `?` operator can only be used in an async function that returns
+}
+
+// Removing the const generic parameter here avoids the ICE
+async fn test<const N: usize>() {
+}
diff --git a/src/test/ui/async-await/issue-84841.stderr b/src/test/ui/async-await/issue-84841.stderr
new file mode 100644
index 00000000000..170dcf581ed
--- /dev/null
+++ b/src/test/ui/async-await/issue-84841.stderr
@@ -0,0 +1,28 @@
+error[E0277]: the `?` operator can only be applied to values that implement `Try`
+  --> $DIR/issue-84841.rs:9:5
+   |
+LL |     test()?;
+   |     ^^^^^^^ the `?` operator cannot be applied to type `impl Future`
+   |
+   = help: the trait `Try` is not implemented for `impl Future`
+   = note: required by `branch`
+
+error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `FromResidual`)
+  --> $DIR/issue-84841.rs:9:11
+   |
+LL |   async fn foo() {
+   |  ________________-
+LL | |     // Adding an .await here avoids the ICE
+LL | |     test()?;
+   | |           ^ cannot use the `?` operator in an async function that returns `()`
+LL | |
+LL | |
+LL | | }
+   | |_- this function should return `Result` or `Option` to accept `?`
+   |
+   = help: the trait `FromResidual<_>` is not implemented for `()`
+   = note: required by `from_residual`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs b/src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs
new file mode 100644
index 00000000000..fce9cac627b
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-86753.rs
@@ -0,0 +1,34 @@
+// edition:2018
+// check-pass
+
+#![warn(rust_2021_compatibility)]
+
+use std::future::Future;
+
+struct Runtime;
+
+impl Runtime {
+    pub fn block_on<F: Future>(&self, _future: F) -> F::Output {
+        unimplemented!()
+    }
+}
+
+pub fn http<F, Fut>(_func: F)
+where
+    F: Fn() -> Fut,
+    Fut: Future<Output = ()>,
+{
+    let rt = Runtime {};
+    let srv = rt.block_on(async move { serve(move || async move { unimplemented!() }) });
+    let _ = || rt.block_on(async { srv });
+}
+
+pub struct Server<S> {
+    _marker: std::marker::PhantomData<S>,
+}
+
+pub fn serve<S>(_new_service: S) -> Server<S> {
+    unimplemented!()
+}
+
+fn main() { }
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index a92d3be5d3c..2808fc35e2a 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -128,7 +128,9 @@ pub fn implements_trait<'tcx>(
         return false;
     }
     let ty_params = cx.tcx.mk_substs(ty_params.iter());
-    cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env))
+    cx.tcx
+        .type_implements_trait((trait_id, ty, ty_params, cx.param_env))
+        .must_apply_modulo_regions()
 }
 
 /// Checks whether this type implements `Drop`.