about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2022-08-07 21:10:22 +0200
committerGitHub <noreply@github.com>2022-08-07 21:10:22 +0200
commit7be359e51be1d95e84937438f05a6ce05004ab5d (patch)
tree02aade133a2cd3644765d600725e17fbcf78a36d
parent9ee22ff7e82a0a806de72516a6a942bad2202dca (diff)
parent82f2c08200dabfecf82ad7eecd3bc6ff246bda5c (diff)
downloadrust-7be359e51be1d95e84937438f05a6ce05004ab5d.tar.gz
rust-7be359e51be1d95e84937438f05a6ce05004ab5d.zip
Rollup merge of #100019 - TaKO8Ki:suggest-boxed-trait-objects-instead-of-impl-trait, r=compiler-errors
Revive suggestions for boxed trait objects instead of impl Trait

The suggestion implemented in #75608 was not working properly, so I fixed it.
-rw-r--r--compiler/rustc_typeck/src/check/_match.rs63
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs2
-rw-r--r--src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr29
-rw-r--r--src/test/ui/mismatched_types/do-not-suggest-boxed-trait-objects-instead-of-impl-trait.rs23
-rw-r--r--src/test/ui/mismatched_types/do-not-suggest-boxed-trait-objects-instead-of-impl-trait.stderr26
-rw-r--r--src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.fixed28
-rw-r--r--src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.rs28
-rw-r--r--src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.stderr47
8 files changed, 217 insertions, 29 deletions
diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs
index 1b13c98e4c3..2d50412007d 100644
--- a/compiler/rustc_typeck/src/check/_match.rs
+++ b/compiler/rustc_typeck/src/check/_match.rs
@@ -4,7 +4,7 @@ use rustc_errors::{Applicability, MultiSpan};
 use rustc_hir::{self as hir, ExprKind};
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::traits::Obligation;
-use rustc_middle::ty::{self, ToPredicate, Ty, TypeVisitable};
+use rustc_middle::ty::{self, ToPredicate, Ty};
 use rustc_span::Span;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::{
@@ -94,7 +94,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
             all_arms_diverge &= self.diverges.get();
 
-            let opt_suggest_box_span = self.opt_suggest_box_span(arm_ty, orig_expected);
+            let opt_suggest_box_span = prior_arm.and_then(|(_, prior_arm_ty, _)| {
+                self.opt_suggest_box_span(prior_arm_ty, arm_ty, orig_expected)
+            });
 
             let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind {
                 (Some(blk.hir_id), self.find_block_span(blk))
@@ -473,43 +475,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     // provide a structured suggestion in that case.
     pub(crate) fn opt_suggest_box_span(
         &self,
-        outer_ty: Ty<'tcx>,
+        first_ty: Ty<'tcx>,
+        second_ty: Ty<'tcx>,
         orig_expected: Expectation<'tcx>,
     ) -> Option<Span> {
         match orig_expected {
             Expectation::ExpectHasType(expected)
                 if self.in_tail_expr
-                    && self.ret_coercion.as_ref()?.borrow().merged_ty().has_opaque_types()
-                    && self.can_coerce(outer_ty, expected) =>
+                    && self.return_type_has_opaque
+                    && self.can_coerce(first_ty, expected)
+                    && self.can_coerce(second_ty, expected) =>
             {
                 let obligations = self.fulfillment_cx.borrow().pending_obligations();
                 let mut suggest_box = !obligations.is_empty();
-                for o in obligations {
-                    match o.predicate.kind().skip_binder() {
-                        ty::PredicateKind::Trait(t) => {
-                            let pred =
-                                ty::Binder::dummy(ty::PredicateKind::Trait(ty::TraitPredicate {
-                                    trait_ref: ty::TraitRef {
-                                        def_id: t.def_id(),
-                                        substs: self.tcx.mk_substs_trait(outer_ty, &[]),
+                'outer: for o in obligations {
+                    for outer_ty in &[first_ty, second_ty] {
+                        match o.predicate.kind().skip_binder() {
+                            ty::PredicateKind::Trait(t) => {
+                                let pred = ty::Binder::dummy(ty::PredicateKind::Trait(
+                                    ty::TraitPredicate {
+                                        trait_ref: ty::TraitRef {
+                                            def_id: t.def_id(),
+                                            substs: self.tcx.mk_substs_trait(*outer_ty, &[]),
+                                        },
+                                        constness: t.constness,
+                                        polarity: t.polarity,
                                     },
-                                    constness: t.constness,
-                                    polarity: t.polarity,
-                                }));
-                            let obl = Obligation::new(
-                                o.cause.clone(),
-                                self.param_env,
-                                pred.to_predicate(self.tcx),
-                            );
-                            suggest_box &= self.predicate_must_hold_modulo_regions(&obl);
-                            if !suggest_box {
-                                // We've encountered some obligation that didn't hold, so the
-                                // return expression can't just be boxed. We don't need to
-                                // evaluate the rest of the obligations.
-                                break;
+                                ));
+                                let obl = Obligation::new(
+                                    o.cause.clone(),
+                                    self.param_env,
+                                    pred.to_predicate(self.tcx),
+                                );
+                                suggest_box &= self.predicate_must_hold_modulo_regions(&obl);
+                                if !suggest_box {
+                                    // We've encountered some obligation that didn't hold, so the
+                                    // return expression can't just be boxed. We don't need to
+                                    // evaluate the rest of the obligations.
+                                    break 'outer;
+                                }
                             }
+                            _ => {}
                         }
-                        _ => {}
                     }
                 }
                 // If all the obligations hold (or there are no obligations) the tail expression
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 6e97b0bf2ab..6d61f3dbb41 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -1002,7 +1002,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let else_ty = self.check_expr_with_expectation(else_expr, expected);
             let else_diverges = self.diverges.get();
 
-            let opt_suggest_box_span = self.opt_suggest_box_span(else_ty, orig_expected);
+            let opt_suggest_box_span = self.opt_suggest_box_span(then_ty, else_ty, orig_expected);
             let if_cause = self.if_cause(
                 sp,
                 cond_expr.span,
diff --git a/src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr b/src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr
index 11c1072f02c..3c65fd998c5 100644
--- a/src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr
+++ b/src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr
@@ -52,6 +52,16 @@ LL | |         1u32
 LL | |     }
    | |_____- `if` and `else` have incompatible types
    |
+help: you could change the return type to be a boxed trait object
+   |
+LL | fn qux() -> Box<dyn std::fmt::Display> {
+   |             ~~~~~~~                  +
+help: if you change the return type to expect trait objects, box the returned expressions
+   |
+LL ~         Box::new(0i32)
+LL |     } else {
+LL ~         Box::new(1u32)
+   |
 help: change the type of the numeric literal from `u32` to `i32`
    |
 LL |         1i32
@@ -114,6 +124,15 @@ LL | |         _ => 2u32,
 LL | |     }
    | |_____- `match` arms have incompatible types
    |
+help: you could change the return type to be a boxed trait object
+   |
+LL | fn dog() -> Box<dyn std::fmt::Display> {
+   |             ~~~~~~~                  +
+help: if you change the return type to expect trait objects, box the returned expressions
+   |
+LL ~         0 => Box::new(0i32),
+LL ~         1 => Box::new(1u32),
+   |
 help: change the type of the numeric literal from `u32` to `i32`
    |
 LL |         1 => 1i32,
@@ -131,6 +150,16 @@ LL | |         1u32
 LL | |     }
    | |_____- `if` and `else` have incompatible types
    |
+help: you could change the return type to be a boxed trait object
+   |
+LL | fn apt() -> Box<dyn std::fmt::Display> {
+   |             ~~~~~~~                  +
+help: if you change the return type to expect trait objects, box the returned expressions
+   |
+LL ~         Box::new(0i32)
+LL |     } else {
+LL ~         Box::new(1u32)
+   |
 help: change the type of the numeric literal from `u32` to `i32`
    |
 LL |         1i32
diff --git a/src/test/ui/mismatched_types/do-not-suggest-boxed-trait-objects-instead-of-impl-trait.rs b/src/test/ui/mismatched_types/do-not-suggest-boxed-trait-objects-instead-of-impl-trait.rs
new file mode 100644
index 00000000000..d302dc99bd8
--- /dev/null
+++ b/src/test/ui/mismatched_types/do-not-suggest-boxed-trait-objects-instead-of-impl-trait.rs
@@ -0,0 +1,23 @@
+struct S;
+struct Y;
+
+trait Trait {}
+
+impl Trait for Y {}
+
+fn foo() -> impl Trait {
+    if true {
+        S
+    } else {
+        Y //~ ERROR `if` and `else` have incompatible types
+    }
+}
+
+fn bar() -> impl Trait {
+    match true {
+        true => S,
+        false => Y, //~ ERROR `match` arms have incompatible types
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/mismatched_types/do-not-suggest-boxed-trait-objects-instead-of-impl-trait.stderr b/src/test/ui/mismatched_types/do-not-suggest-boxed-trait-objects-instead-of-impl-trait.stderr
new file mode 100644
index 00000000000..2f814445bba
--- /dev/null
+++ b/src/test/ui/mismatched_types/do-not-suggest-boxed-trait-objects-instead-of-impl-trait.stderr
@@ -0,0 +1,26 @@
+error[E0308]: `if` and `else` have incompatible types
+  --> $DIR/do-not-suggest-boxed-trait-objects-instead-of-impl-trait.rs:12:9
+   |
+LL | /     if true {
+LL | |         S
+   | |         - expected because of this
+LL | |     } else {
+LL | |         Y
+   | |         ^ expected struct `S`, found struct `Y`
+LL | |     }
+   | |_____- `if` and `else` have incompatible types
+
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/do-not-suggest-boxed-trait-objects-instead-of-impl-trait.rs:19:18
+   |
+LL | /     match true {
+LL | |         true => S,
+   | |                 - this is found to be of type `S`
+LL | |         false => Y,
+   | |                  ^ expected struct `S`, found struct `Y`
+LL | |     }
+   | |_____- `match` arms have incompatible types
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.fixed b/src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.fixed
new file mode 100644
index 00000000000..f30feaed055
--- /dev/null
+++ b/src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.fixed
@@ -0,0 +1,28 @@
+// run-rustfix
+
+#![allow(dead_code)]
+
+struct S;
+struct Y;
+
+trait Trait {}
+
+impl Trait for S {}
+impl Trait for Y {}
+
+fn foo() -> Box<dyn Trait> {
+    if true {
+        Box::new(S)
+    } else {
+        Box::new(Y) //~ ERROR `if` and `else` have incompatible types
+    }
+}
+
+fn bar() -> Box<dyn Trait> {
+    match true {
+        true => Box::new(S),
+        false => Box::new(Y), //~ ERROR `match` arms have incompatible types
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.rs b/src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.rs
new file mode 100644
index 00000000000..2bd8146e289
--- /dev/null
+++ b/src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.rs
@@ -0,0 +1,28 @@
+// run-rustfix
+
+#![allow(dead_code)]
+
+struct S;
+struct Y;
+
+trait Trait {}
+
+impl Trait for S {}
+impl Trait for Y {}
+
+fn foo() -> impl Trait {
+    if true {
+        S
+    } else {
+        Y //~ ERROR `if` and `else` have incompatible types
+    }
+}
+
+fn bar() -> impl Trait {
+    match true {
+        true => S,
+        false => Y, //~ ERROR `match` arms have incompatible types
+    }
+}
+
+fn main() {}
diff --git a/src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.stderr b/src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.stderr
new file mode 100644
index 00000000000..f58b9c3ec16
--- /dev/null
+++ b/src/test/ui/mismatched_types/suggest-boxed-trait-objects-instead-of-impl-trait.stderr
@@ -0,0 +1,47 @@
+error[E0308]: `if` and `else` have incompatible types
+  --> $DIR/suggest-boxed-trait-objects-instead-of-impl-trait.rs:17:9
+   |
+LL | /     if true {
+LL | |         S
+   | |         - expected because of this
+LL | |     } else {
+LL | |         Y
+   | |         ^ expected struct `S`, found struct `Y`
+LL | |     }
+   | |_____- `if` and `else` have incompatible types
+   |
+help: you could change the return type to be a boxed trait object
+   |
+LL | fn foo() -> Box<dyn Trait> {
+   |             ~~~~~~~      +
+help: if you change the return type to expect trait objects, box the returned expressions
+   |
+LL ~         Box::new(S)
+LL |     } else {
+LL ~         Box::new(Y)
+   |
+
+error[E0308]: `match` arms have incompatible types
+  --> $DIR/suggest-boxed-trait-objects-instead-of-impl-trait.rs:24:18
+   |
+LL | /     match true {
+LL | |         true => S,
+   | |                 - this is found to be of type `S`
+LL | |         false => Y,
+   | |                  ^ expected struct `S`, found struct `Y`
+LL | |     }
+   | |_____- `match` arms have incompatible types
+   |
+help: you could change the return type to be a boxed trait object
+   |
+LL | fn bar() -> Box<dyn Trait> {
+   |             ~~~~~~~      +
+help: if you change the return type to expect trait objects, box the returned expressions
+   |
+LL ~         true => Box::new(S),
+LL ~         false => Box::new(Y),
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.