]> git.lizzy.rs Git - rust.git/commitdiff
allow inference vars in type_implements_trait
authorNiko Matsakis <niko@alum.mit.edu>
Sun, 4 Jul 2021 15:26:32 +0000 (11:26 -0400)
committerNiko Matsakis <niko@alum.mit.edu>
Sun, 4 Jul 2021 15:28:20 +0000 (11:28 -0400)
compiler/rustc_middle/src/query/mod.rs
compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs
compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
compiler/rustc_trait_selection/src/traits/mod.rs
compiler/rustc_typeck/src/check/cast.rs
compiler/rustc_typeck/src/check/upvar.rs
src/test/ui/async-await/issue-84841.rs
src/test/ui/async-await/issue-84841.stderr [new file with mode: 0644]
src/tools/clippy/clippy_utils/src/ty.rs

index 23ee0e05062723b0275e8a6cd3d7948e184eac8e..0986a46572969b4293dd6902c5d7b6f6fc6ed086 100644 (file)
         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 }
     }
 
index a0c9b43d5afee1e1862084dcd20544888e9d6acb..c1c875eac5589873d1e374261ca061b07a7b4d3a 100644 (file)
@@ -1331,7 +1331,9 @@ fn try_report_cannot_return_reference_to_local(
             // 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 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(
index 5c35b515f3d027f9ba60db1e4ab0a8e447699080..dc765f5228b9dc39a39b8e1268ecdb768d28dad6 100644 (file)
@@ -2396,7 +2396,9 @@ fn suggest_await_before_try(
                     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(
index d65a596a8276f1bca70d7a6ddb22c3f072a4f601..c5c1da2d2c0283151c8938ac71efcd21700cc9c8 100644 (file)
@@ -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: 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) {
index 3cbc3d231f847eda831051f4ba638dd159e0643e..7ff4a108dabcf83a925f23a5fa06c7a0c732d9c7 100644 (file)
@@ -444,12 +444,15 @@ fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) {
                             // 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,
-                                ))
+                                && fcx
+                                    .tcx
+                                    .type_implements_trait((
+                                        from_trait,
+                                        ty,
+                                        ty_params,
+                                        fcx.param_env,
+                                    ))
+                                    .must_apply_modulo_regions()
                             {
                                 label = false;
                                 err.span_suggestion(
index 41b4172781cf82a22b03e379e20c139ee3ed0e9f..e5f18778f43e62171fd089c97972133ac20cd328 100644 (file)
@@ -961,12 +961,14 @@ fn has_significant_drop_outside_of_captures(
         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);
index 2336778832301c2bf6a157a14511f46ddd3c20bc..ba3a1617b9c1d57fe4c2f97c3d5c12225ec979af 100644 (file)
@@ -1,8 +1,14 @@
 // edition:2018
 
-async fn main() {
+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
diff --git a/src/test/ui/async-await/issue-84841.stderr b/src/test/ui/async-await/issue-84841.stderr
new file mode 100644 (file)
index 0000000..170dcf5
--- /dev/null
@@ -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`.
index a92d3be5d3cf2b595d831769b6efbe142ccc15b6..add487593c6d2a480e220e21dbbe7b7d19bcd72a 100644 (file)
@@ -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`.
@@ -144,22 +146,26 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     match ty.kind() {
         ty::Adt(adt, _) => must_use_attr(cx.tcx.get_attrs(adt.did)).is_some(),
         ty::Foreign(ref did) => must_use_attr(cx.tcx.get_attrs(*did)).is_some(),
-        ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
+        ty::Slice(ty)
+        | ty::Array(ty, _)
+        | ty::RawPtr(ty::TypeAndMut { ty, .. })
+        | ty::Ref(_, ty, _) => {
             // for the Array case we don't need to care for the len == 0 case
             // because we don't want to lint functions returning empty arrays
             is_must_use_ty(cx, *ty)
-        },
+        }
         ty::Tuple(substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
         ty::Opaque(ref def_id, _) => {
             for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
-                if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() {
+                if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder()
+                {
                     if must_use_attr(cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
                         return true;
                     }
                 }
             }
             false
-        },
+        }
         ty::Dynamic(binder, _) => {
             for predicate in binder.iter() {
                 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
@@ -169,7 +175,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
                 }
             }
             false
-        },
+        }
         _ => false,
     }
 }
@@ -179,7 +185,11 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 // not succeed
 /// Checks if `Ty` is normalizable. This function is useful
 /// to avoid crashes on `layout_of`.
-pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
+pub fn is_normalizable<'tcx>(
+    cx: &LateContext<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    ty: Ty<'tcx>,
+) -> bool {
     is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
 }
 
@@ -199,15 +209,14 @@ fn is_normalizable_helper<'tcx>(
         if infcx.at(&cause, param_env).normalize(ty).is_ok() {
             match ty.kind() {
                 ty::Adt(def, substs) => def.variants.iter().all(|variant| {
-                    variant
-                        .fields
-                        .iter()
-                        .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
+                    variant.fields.iter().all(|field| {
+                        is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache)
+                    })
                 }),
                 _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
                     GenericArgKind::Type(inner_ty) if inner_ty != ty => {
                         is_normalizable_helper(cx, param_env, inner_ty, cache)
-                    },
+                    }
                     _ => true, // if inner_ty == ty, we've already checked it
                 }),
             }
@@ -225,7 +234,9 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
     match ty.kind() {
         ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
         ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
-        ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
+        ty::Array(inner_type, _) | ty::Slice(inner_type) => {
+            is_recursively_primitive_type(inner_type)
+        }
         ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
         _ => false,
     }
@@ -269,11 +280,7 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
 /// removed.
 pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
     fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
-        if let ty::Ref(_, ty, _) = ty.kind() {
-            peel(ty, count + 1)
-        } else {
-            (ty, count)
-        }
+        if let ty::Ref(_, ty, _) = ty.kind() { peel(ty, count + 1) } else { (ty, count) }
     }
     peel(ty, 0)
 }
@@ -328,17 +335,18 @@ pub fn same_type_and_consts(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
                 return false;
             }
 
-            substs_a
-                .iter()
-                .zip(substs_b.iter())
-                .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) {
-                    (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
+            substs_a.iter().zip(substs_b.iter()).all(|(arg_a, arg_b)| {
+                match (arg_a.unpack(), arg_b.unpack()) {
+                    (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => {
+                        inner_a == inner_b
+                    }
                     (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
                         same_type_and_consts(type_a, type_b)
-                    },
+                    }
                     _ => true,
-                })
-        },
+                }
+            })
+        }
         _ => a == b,
     }
 }