1
Fork 0

Fix ICE in opt_suggest_box_span

This commit is contained in:
Michael Goulet 2022-09-09 05:50:25 +00:00
parent 0a605d33cd
commit 5599a45e58
5 changed files with 114 additions and 50 deletions

View file

@ -73,7 +73,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// for opaque types, and then use that kind to fix the spans for type errors // for opaque types, and then use that kind to fix the spans for type errors
// that we see later on. // that we see later on.
let ty_var = self.next_ty_var(TypeVariableOrigin { let ty_var = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeInference, kind: TypeVariableOriginKind::OpaqueTypeInference(def_id),
span, span,
}); });
obligations.extend( obligations.extend(

View file

@ -122,6 +122,7 @@ pub enum TypeVariableOriginKind {
MiscVariable, MiscVariable,
NormalizeProjectionType, NormalizeProjectionType,
TypeInference, TypeInference,
OpaqueTypeInference(DefId),
TypeParameterDefinition(Symbol, Option<DefId>), TypeParameterDefinition(Symbol, Option<DefId>),
/// One of the upvars or closure kind parameters in a `ClosureSubsts` /// One of the upvars or closure kind parameters in a `ClosureSubsts`

View file

@ -4,7 +4,7 @@ use rustc_errors::{Applicability, MultiSpan};
use rustc_hir::{self as hir, ExprKind}; use rustc_hir::{self as hir, ExprKind};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::traits::Obligation; use rustc_infer::traits::Obligation;
use rustc_middle::ty::{self, ToPredicate, Ty}; use rustc_middle::ty::{self, Subst, ToPredicate, Ty};
use rustc_span::Span; use rustc_span::Span;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{ use rustc_trait_selection::traits::{
@ -143,7 +143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.find_by_def_id(self.body_id.owner) .find_by_def_id(self.body_id.owner)
.and_then(|owner| owner.fn_decl()) .and_then(|owner| owner.fn_decl())
.map(|decl| decl.output.span()) .map(|decl| decl.output.span())
else { return; }; else { return; };
let Expectation::IsLast(stmt) = orig_expected else { let Expectation::IsLast(stmt) = orig_expected else {
return return
}; };
@ -472,64 +472,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} }
} }
// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` /// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
// we check if the different arms would work with boxed trait objects instead and /// we check if the different arms would work with boxed trait objects instead and
// provide a structured suggestion in that case. /// provide a structured suggestion in that case.
pub(crate) fn opt_suggest_box_span( pub(crate) fn opt_suggest_box_span(
&self, &self,
first_ty: Ty<'tcx>, first_ty: Ty<'tcx>,
second_ty: Ty<'tcx>, second_ty: Ty<'tcx>,
orig_expected: Expectation<'tcx>, orig_expected: Expectation<'tcx>,
) -> Option<Span> { ) -> Option<Span> {
// FIXME(compiler-errors): This really shouldn't need to be done during the
// "good" path of typeck, but here we are.
match orig_expected { match orig_expected {
Expectation::ExpectHasType(expected) Expectation::ExpectHasType(expected) => {
if self.in_tail_expr let TypeVariableOrigin {
&& self.return_type_has_opaque span,
&& self.can_coerce(first_ty, expected) kind: TypeVariableOriginKind::OpaqueTypeInference(rpit_def_id),
&& self.can_coerce(second_ty, expected) => ..
{ } = self.type_var_origin(expected)? else { return None; };
let obligations = self.fulfillment_cx.borrow().pending_obligations();
let mut suggest_box = !obligations.is_empty(); let sig = *self
'outer: for o in obligations { .typeck_results
for outer_ty in &[first_ty, second_ty] { .borrow()
match o.predicate.kind().skip_binder() { .liberated_fn_sigs()
ty::PredicateKind::Trait(t) => { .get(hir::HirId::make_owner(self.body_id.owner))?;
let pred = ty::Binder::dummy(ty::PredicateKind::Trait(
ty::TraitPredicate { let substs = sig.output().walk().find_map(|arg| {
trait_ref: ty::TraitRef { if let ty::GenericArgKind::Type(ty) = arg.unpack()
def_id: t.def_id(), && let ty::Opaque(def_id, substs) = *ty.kind()
substs: self.tcx.mk_substs_trait(*outer_ty, &[]), && def_id == rpit_def_id
}, {
constness: t.constness, Some(substs)
polarity: t.polarity, } else {
}, None
)); }
let obl = Obligation::new( })?;
o.cause.clone(), let opaque_ty = self.tcx.mk_opaque(rpit_def_id, substs);
self.param_env,
pred.to_predicate(self.tcx), if !self.can_coerce(first_ty, expected) || !self.can_coerce(second_ty, expected) {
); return None;
suggest_box &= self.predicate_must_hold_modulo_regions(&obl); }
if !suggest_box {
// We've encountered some obligation that didn't hold, so the for ty in [first_ty, second_ty] {
// return expression can't just be boxed. We don't need to for pred in self.tcx.bound_explicit_item_bounds(rpit_def_id).transpose_iter() {
// evaluate the rest of the obligations. let pred = pred.map_bound(|(pred, _)| *pred).subst(self.tcx, substs);
break 'outer; let pred = match pred.kind().skip_binder() {
} ty::PredicateKind::Trait(mut trait_pred) => {
assert_eq!(trait_pred.trait_ref.self_ty(), opaque_ty);
trait_pred.trait_ref.substs =
self.tcx.mk_substs_trait(ty, &trait_pred.trait_ref.substs[1..]);
pred.kind().rebind(trait_pred).to_predicate(self.tcx)
} }
_ => {} ty::PredicateKind::Projection(mut proj_pred) => {
assert_eq!(proj_pred.projection_ty.self_ty(), opaque_ty);
proj_pred.projection_ty.substs = self
.tcx
.mk_substs_trait(ty, &proj_pred.projection_ty.substs[1..]);
pred.kind().rebind(proj_pred).to_predicate(self.tcx)
}
_ => continue,
};
if !self.predicate_must_hold_modulo_regions(&Obligation::new(
ObligationCause::misc(span, self.body_id),
self.param_env,
pred,
)) {
return None;
} }
} }
} }
if suggest_box {
self.tcx Some(span)
.hir()
.find_by_def_id(self.body_id.owner)
.and_then(|owner| owner.fn_decl())
.map(|decl| decl.output.span())
} else {
None
}
} }
_ => None, _ => None,
} }

View file

@ -0,0 +1,25 @@
#![feature(trait_alias)]
struct B;
struct C;
trait Tr {}
impl Tr for B {}
impl Tr for C {}
trait Tr2<S> = Into<S>;
fn foo2<T: Tr2<()>>() {}
fn foo() -> impl Tr {
let x = foo2::<_>();
match true {
true => B,
false => C,
//~^ `match` arms have incompatible types
}
}
fn main() {}

View file

@ -0,0 +1,25 @@
error[E0308]: `match` arms have incompatible types
--> $DIR/issue-101465.rs:20:18
|
LL | / match true {
LL | | true => B,
| | - this is found to be of type `B`
LL | | false => C,
| | ^ expected struct `B`, found struct `C`
LL | |
LL | | }
| |_____- `match` arms have incompatible types
|
help: you could change the return type to be a boxed trait object
|
LL | fn foo() -> Box<dyn Tr> {
| ~~~~~~~ +
help: if you change the return type to expect trait objects, box the returned expressions
|
LL ~ true => Box::new(B),
LL ~ false => Box::new(C),
|
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.