Skip to content

Commit 86f7858

Browse files
committed
Auto merge of rust-lang#138438 - compiler-errors:delay-coerce-err, r=<try>
[experiment] Delay coercion error rather than emitting error in `coerce_unsized` cc rust-lang#136127 (comment) r? `@ghost`
2 parents 249cb84 + 6551008 commit 86f7858

38 files changed

+341
-252
lines changed

compiler/rustc_hir_typeck/src/cast.rs

+43-14
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use rustc_ast::util::parser::ExprPrecedence;
3232
use rustc_data_structures::fx::FxHashSet;
3333
use rustc_errors::codes::*;
3434
use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
35+
use rustc_hir::def_id::DefId;
3536
use rustc_hir::{self as hir, ExprKind};
3637
use rustc_infer::infer::DefineOpaqueTypes;
3738
use rustc_macros::{TypeFoldable, TypeVisitable};
@@ -155,7 +156,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
155156
}
156157
}
157158

158-
#[derive(Copy, Clone, Debug)]
159+
#[derive(Debug)]
159160
enum CastError<'tcx> {
160161
ErrorGuaranteed(ErrorGuaranteed),
161162

@@ -182,6 +183,7 @@ enum CastError<'tcx> {
182183
/// when we're typechecking a type parameter with a ?Sized bound.
183184
IntToWideCast(Option<&'static str>),
184185
ForeignNonExhaustiveAdt,
186+
PtrPtrAddingAutoTrait(Vec<DefId>),
185187
}
186188

187189
impl From<ErrorGuaranteed> for CastError<'_> {
@@ -351,6 +353,14 @@ impl<'a, 'tcx> CastCheck<'tcx> {
351353
PointerKind::Thin,
352354
)
353355
| (PointerKind::Length, PointerKind::Length) => {
356+
if let Some(_guar) = fcx.emit_specialized_coerce_unsize_error(
357+
self.expr.span,
358+
self.expr_ty,
359+
self.cast_ty,
360+
) {
361+
return;
362+
}
363+
354364
span_bug!(self.span, "unexpected cast error: {e:?}")
355365
}
356366
}
@@ -399,6 +409,14 @@ impl<'a, 'tcx> CastCheck<'tcx> {
399409
err.emit();
400410
}
401411
CastError::NonScalar => {
412+
if let Some(_guar) = fcx.emit_specialized_coerce_unsize_error(
413+
self.expr.span,
414+
self.expr_ty,
415+
self.cast_ty,
416+
) {
417+
return;
418+
}
419+
402420
let mut err = type_error_struct!(
403421
fcx.dcx(),
404422
self.span,
@@ -548,6 +566,14 @@ impl<'a, 'tcx> CastCheck<'tcx> {
548566
err.emit();
549567
}
550568
CastError::SizedUnsizedCast => {
569+
if let Some(_guar) = fcx.emit_specialized_coerce_unsize_error(
570+
self.expr.span,
571+
self.expr_ty,
572+
self.cast_ty,
573+
) {
574+
return;
575+
}
576+
551577
let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty);
552578
let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty);
553579
fcx.dcx().emit_err(errors::CastThinPointerToWidePointer {
@@ -596,6 +622,21 @@ impl<'a, 'tcx> CastCheck<'tcx> {
596622
.with_note("cannot cast an enum with a non-exhaustive variant when it's defined in another crate")
597623
.emit();
598624
}
625+
CastError::PtrPtrAddingAutoTrait(added) => {
626+
fcx.dcx().emit_err(errors::PtrCastAddAutoToObject {
627+
span: self.span,
628+
traits_len: added.len(),
629+
traits: {
630+
let mut traits: Vec<_> = added
631+
.into_iter()
632+
.map(|trait_did| fcx.tcx.def_path_str(trait_did))
633+
.collect();
634+
635+
traits.sort();
636+
traits.into()
637+
},
638+
});
639+
}
599640
}
600641
}
601642

@@ -940,19 +981,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
940981
.collect::<Vec<_>>();
941982

942983
if !added.is_empty() {
943-
tcx.dcx().emit_err(errors::PtrCastAddAutoToObject {
944-
span: self.span,
945-
traits_len: added.len(),
946-
traits: {
947-
let mut traits: Vec<_> = added
948-
.into_iter()
949-
.map(|trait_did| tcx.def_path_str(trait_did))
950-
.collect();
951-
952-
traits.sort();
953-
traits.into()
954-
},
955-
});
984+
return Err(CastError::PtrPtrAddingAutoTrait(added));
956985
}
957986

958987
Ok(CastKind::PtrPtrCast)

compiler/rustc_hir_typeck/src/coercion.rs

+60-62
Original file line numberDiff line numberDiff line change
@@ -48,20 +48,20 @@ use rustc_infer::infer::relate::RelateResult;
4848
use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
4949
use rustc_infer::traits::{
5050
IfExpressionCause, MatchExpressionArmCause, Obligation, PredicateObligation,
51-
PredicateObligations,
51+
PredicateObligations, SelectionError,
5252
};
5353
use rustc_middle::span_bug;
5454
use rustc_middle::ty::adjustment::{
5555
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
5656
};
5757
use rustc_middle::ty::error::TypeError;
5858
use rustc_middle::ty::visit::TypeVisitableExt;
59-
use rustc_middle::ty::{self, AliasTy, GenericArgsRef, Ty, TyCtxt};
60-
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span};
59+
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt};
60+
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, ErrorGuaranteed, Span};
6161
use rustc_trait_selection::infer::InferCtxtExt as _;
6262
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
6363
use rustc_trait_selection::traits::{
64-
self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt,
64+
self, FulfillmentErrorCode, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt,
6565
};
6666
use smallvec::{SmallVec, smallvec};
6767
use tracing::{debug, instrument};
@@ -600,55 +600,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
600600
ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]),
601601
);
602602

603-
// If the root `Source: CoerceUnsized<Target>` obligation can't possibly hold,
604-
// we don't have to assume that this is unsizing coercion (it will always lead to an error)
605-
//
606-
// However, we don't want to bail early all the time, since the unholdable obligations
607-
// may be interesting for diagnostics (such as trying to coerce `&T` to `&dyn Id<This = U>`),
608-
// so we only bail if there (likely) is another way to convert the types.
609603
if !self.infcx.predicate_may_hold(&root_obligation) {
610-
if let Some(dyn_metadata_adt_def_id) = self.tcx.lang_items().get(LangItem::DynMetadata)
611-
&& let Some(metadata_type_def_id) = self.tcx.lang_items().get(LangItem::Metadata)
612-
{
613-
self.probe(|_| {
614-
let ocx = ObligationCtxt::new(&self.infcx);
615-
616-
// returns `true` if `<ty as Pointee>::Metadata` is `DynMetadata<_>`
617-
let has_dyn_trait_metadata = |ty| {
618-
let metadata_ty: Result<_, _> = ocx.structurally_normalize_ty(
619-
&ObligationCause::dummy(),
620-
self.fcx.param_env,
621-
Ty::new_alias(
622-
self.tcx,
623-
ty::AliasTyKind::Projection,
624-
AliasTy::new(self.tcx, metadata_type_def_id, [ty]),
625-
),
626-
);
627-
628-
metadata_ty.is_ok_and(|metadata_ty| {
629-
metadata_ty
630-
.ty_adt_def()
631-
.is_some_and(|d| d.did() == dyn_metadata_adt_def_id)
632-
})
633-
};
634-
635-
// If both types are raw pointers to a (wrapper over a) trait object,
636-
// this might be a cast like `*const W<dyn Trait> -> *const dyn Trait`.
637-
// So it's better to bail and try that. (even if the cast is not possible, for
638-
// example due to vtables not matching, cast diagnostic will likely still be better)
639-
//
640-
// N.B. use `target`, not `coerce_target` (the latter is a var)
641-
if let &ty::RawPtr(source_pointee, _) = coerce_source.kind()
642-
&& let &ty::RawPtr(target_pointee, _) = target.kind()
643-
&& has_dyn_trait_metadata(source_pointee)
644-
&& has_dyn_trait_metadata(target_pointee)
645-
{
646-
return Err(TypeError::Mismatch);
647-
}
648-
649-
Ok(())
650-
})?;
651-
}
604+
return Err(TypeError::Mismatch);
652605
}
653606

654607
// Use a FIFO queue for this custom fulfillment procedure.
@@ -725,17 +678,10 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
725678
return Err(TypeError::Mismatch);
726679
}
727680

728-
// Dyn-compatibility violations or miscellaneous.
681+
// Should have been filtered out by the `predicate_may_hold` check.
729682
Err(err) => {
730-
let guar = self.err_ctxt().report_selection_error(
731-
obligation.clone(),
732-
&obligation,
733-
&err,
734-
);
735-
self.fcx.set_tainted_by_errors(guar);
736-
// Treat this like an obligation and follow through
737-
// with the unsizing - the lack of a coercion should
738-
// be silent, as it causes a type mismatch later.
683+
debug!("coerce_unsized: early return - selection error: {err:?}");
684+
return Err(TypeError::Mismatch);
739685
}
740686

741687
Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),
@@ -1120,6 +1066,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11201066
})
11211067
}
11221068

1069+
pub(crate) fn emit_specialized_coerce_unsize_error(
1070+
&self,
1071+
span: Span,
1072+
source: Ty<'tcx>,
1073+
target: Ty<'tcx>,
1074+
) -> Option<ErrorGuaranteed> {
1075+
let ocx = ObligationCtxt::new_with_diagnostics(self);
1076+
let coerce_unsized_def_id = self.tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));
1077+
let unsize_def_id = self.tcx.require_lang_item(LangItem::Unsize, Some(span));
1078+
ocx.register_obligation(Obligation::new(
1079+
self.tcx,
1080+
self.cause(span, ObligationCauseCode::Coercion { source, target }),
1081+
self.param_env,
1082+
ty::TraitRef::new(self.tcx, coerce_unsized_def_id, [source, target]),
1083+
));
1084+
1085+
let mut errors = ocx.select_where_possible();
1086+
// Retain the errors that don't mention, but also as a HACK we will adjust their
1087+
// root obligation, too. This is a nasty hack to preserve diagnostic parity that
1088+
// should probably be fixed by emitting better errors for failed `CoerceUnsized`.
1089+
errors.retain_mut(|err| {
1090+
if matches!(
1091+
err.code,
1092+
FulfillmentErrorCode::Select(SelectionError::TraitDynIncompatible(_)),
1093+
) || err.obligation.predicate.as_trait_clause().is_none_or(|trait_clause| {
1094+
trait_clause.def_id() != coerce_unsized_def_id
1095+
&& trait_clause.def_id() != unsize_def_id
1096+
}) {
1097+
err.root_obligation = err.obligation.clone();
1098+
true
1099+
} else {
1100+
false
1101+
}
1102+
});
1103+
1104+
if errors.is_empty() {
1105+
None
1106+
} else {
1107+
let guar = self.err_ctxt().report_fulfillment_errors(errors);
1108+
self.set_tainted_by_errors(guar);
1109+
Some(guar)
1110+
}
1111+
}
1112+
11231113
/// Probe whether `expr_ty` can be coerced to `target_ty`. This has no side-effects,
11241114
/// and may return false positives if types are not yet fully constrained by inference.
11251115
///
@@ -1666,6 +1656,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
16661656
}
16671657
}
16681658
Err(coercion_error) => {
1659+
if let Some(_guar) = fcx.emit_specialized_coerce_unsize_error(
1660+
cause.span,
1661+
expression_ty,
1662+
self.merged_ty(),
1663+
) {
1664+
return;
1665+
}
1666+
16691667
// Mark that we've failed to coerce the types here to suppress
16701668
// any superfluous errors we might encounter while trying to
16711669
// emit or provide suggestions on how to fix the initial error.

compiler/rustc_hir_typeck/src/demand.rs

+6
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
268268
Err(e) => e,
269269
};
270270

271+
if let Some(guar) =
272+
self.emit_specialized_coerce_unsize_error(expr.span, checked_ty, expected)
273+
{
274+
return Ok(Ty::new_error(self.tcx, guar));
275+
}
276+
271277
self.adjust_expr_for_assert_eq_macro(&mut expr, &mut expected_ty_expr);
272278

273279
self.set_tainted_by_errors(self.dcx().span_delayed_bug(

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+24
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
972972
true
973973
});
974974

975+
// We need to handle type mismatches due to unsizing failures specially.
976+
errors.retain(|err| {
977+
if let Error::Invalid(
978+
provided_idx,
979+
expected_idx,
980+
Compatibility::Incompatible(Some(_)),
981+
) = *err
982+
{
983+
let (_, expected_ty) = formal_and_expected_inputs[expected_idx];
984+
let (provided_ty, provided_arg_span) = provided_arg_tys[provided_idx];
985+
986+
if let Some(guar) = self.emit_specialized_coerce_unsize_error(
987+
provided_arg_span,
988+
provided_ty,
989+
expected_ty,
990+
) {
991+
reported = Some(guar);
992+
return false;
993+
}
994+
}
995+
996+
true
997+
});
998+
975999
// We're done if we found errors, but we already emitted them.
9761000
if let Some(reported) = reported
9771001
&& errors.is_empty()

compiler/rustc_trait_selection/src/traits/select/confirmation.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
12371237
&obligation.cause,
12381238
);
12391239

1240+
let pointer_like_goal = normalize_with_depth_to(
1241+
self,
1242+
obligation.param_env,
1243+
obligation.cause.clone(),
1244+
obligation.recursion_depth,
1245+
pointer_like_goal,
1246+
&mut nested,
1247+
);
1248+
12401249
nested.push(predicate_to_obligation(pointer_like_goal.upcast(tcx)));
12411250
}
12421251
}

tests/ui/async-await/async-block-control-flow-static-semantics.stderr

+11-11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ LL | async {
1414
LL | break 0u8;
1515
| ^^^^^^^^^ cannot `break` inside `async` block
1616

17+
error[E0271]: expected `{async block@$DIR/async-block-control-flow-static-semantics.rs:23:17: 23:22}` to be a future that resolves to `()`, but it resolves to `u8`
18+
--> $DIR/async-block-control-flow-static-semantics.rs:26:39
19+
|
20+
LL | let _: &dyn Future<Output = ()> = &block;
21+
| ^^^^^^ expected `()`, found `u8`
22+
|
23+
= note: required for the cast from `&{async block@$DIR/async-block-control-flow-static-semantics.rs:23:17: 23:22}` to `&dyn Future<Output = ()>`
24+
1725
error[E0308]: mismatched types
1826
--> $DIR/async-block-control-flow-static-semantics.rs:21:58
1927
|
@@ -26,13 +34,13 @@ LL | | return 0u8;
2634
LL | | }
2735
| |_^ expected `u8`, found `()`
2836

29-
error[E0271]: expected `{async block@$DIR/async-block-control-flow-static-semantics.rs:23:17: 23:22}` to be a future that resolves to `()`, but it resolves to `u8`
30-
--> $DIR/async-block-control-flow-static-semantics.rs:26:39
37+
error[E0271]: expected `{async block@$DIR/async-block-control-flow-static-semantics.rs:14:17: 14:22}` to be a future that resolves to `()`, but it resolves to `u8`
38+
--> $DIR/async-block-control-flow-static-semantics.rs:17:39
3139
|
3240
LL | let _: &dyn Future<Output = ()> = &block;
3341
| ^^^^^^ expected `()`, found `u8`
3442
|
35-
= note: required for the cast from `&{async block@$DIR/async-block-control-flow-static-semantics.rs:23:17: 23:22}` to `&dyn Future<Output = ()>`
43+
= note: required for the cast from `&{async block@$DIR/async-block-control-flow-static-semantics.rs:14:17: 14:22}` to `&dyn Future<Output = ()>`
3644

3745
error[E0308]: mismatched types
3846
--> $DIR/async-block-control-flow-static-semantics.rs:12:43
@@ -42,14 +50,6 @@ LL | fn return_targets_async_block_not_fn() -> u8 {
4250
| |
4351
| implicitly returns `()` as its body has no tail or `return` expression
4452

45-
error[E0271]: expected `{async block@$DIR/async-block-control-flow-static-semantics.rs:14:17: 14:22}` to be a future that resolves to `()`, but it resolves to `u8`
46-
--> $DIR/async-block-control-flow-static-semantics.rs:17:39
47-
|
48-
LL | let _: &dyn Future<Output = ()> = &block;
49-
| ^^^^^^ expected `()`, found `u8`
50-
|
51-
= note: required for the cast from `&{async block@$DIR/async-block-control-flow-static-semantics.rs:14:17: 14:22}` to `&dyn Future<Output = ()>`
52-
5353
error[E0308]: mismatched types
5454
--> $DIR/async-block-control-flow-static-semantics.rs:49:44
5555
|

tests/ui/cross/cross-borrow-trait.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ LL | let _y: &dyn Trait = x;
88
|
99
= note: expected reference `&dyn Trait`
1010
found struct `Box<dyn Trait>`
11+
help: consider borrowing here
12+
|
13+
LL | let _y: &dyn Trait = &x;
14+
| +
1115

1216
error: aborting due to 1 previous error
1317

0 commit comments

Comments
 (0)