Skip to content

Commit 5883d54

Browse files
committed
Auto merge of rust-lang#138542 - compiler-errors:coerce-unsize-later, r=<try>
Move coercion hack from `coerce_unsized` to `check_cast` r? `@ghost`
2 parents 4d30011 + 1a7c923 commit 5883d54

File tree

6 files changed

+66
-116
lines changed

6 files changed

+66
-116
lines changed

compiler/rustc_hir_typeck/src/cast.rs

+34-23
Original file line numberDiff line numberDiff line change
@@ -705,33 +705,44 @@ impl<'a, 'tcx> CastCheck<'tcx> {
705705
self.report_cast_to_unsized_type(fcx);
706706
} else if self.expr_ty.references_error() || self.cast_ty.references_error() {
707707
// No sense in giving duplicate error messages
708+
} else if self.expr_ty.is_raw_ptr() && self.cast_ty.is_raw_ptr() {
709+
// HACK: We check `may_coerce` first to ensure that the unsizing is
710+
// worth committing to. The weird thing is that `coerce_unsized` is
711+
// somewhat unreliable; it commits to coercions that are doomed to
712+
// fail like `*const W<dyn Trait> -> *const dyn Trait` even though
713+
// the pointee is unsized. Here, we want to fall back to a ptr-ptr
714+
// cast, so we first check `may_coerce` which also checks that all
715+
// of the nested obligations hold first, *then* only commit to the
716+
// coercion cast if definitely holds.
717+
if fcx.may_coerce(self.expr_ty, self.cast_ty) {
718+
self.try_coercion_cast(fcx).expect("`may_coerce` should imply types can coerce");
719+
// When casting a raw pointer to another raw pointer, we cannot convert the cast into
720+
// a coercion because the pointee types might only differ in regions, which HIR typeck
721+
// cannot distinguish. This would cause us to erroneously discard a cast which will
722+
// lead to a borrowck error like #113257.
723+
// We still did a coercion above to unify inference variables for `ptr as _` casts.
724+
// This does cause us to miss some trivial casts in the trivial cast lint.
725+
} else {
726+
match self.do_check(fcx) {
727+
Ok(k) => {
728+
debug!(" -> {:?}", k);
729+
}
730+
Err(e) => self.report_cast_error(fcx, e),
731+
}
732+
}
708733
} else {
709734
match self.try_coercion_cast(fcx) {
710735
Ok(()) => {
711-
if self.expr_ty.is_raw_ptr() && self.cast_ty.is_raw_ptr() {
712-
// When casting a raw pointer to another raw pointer, we cannot convert the cast into
713-
// a coercion because the pointee types might only differ in regions, which HIR typeck
714-
// cannot distinguish. This would cause us to erroneously discard a cast which will
715-
// lead to a borrowck error like #113257.
716-
// We still did a coercion above to unify inference variables for `ptr as _` casts.
717-
// This does cause us to miss some trivial casts in the trivial cast lint.
718-
debug!(" -> PointerCast");
719-
} else {
720-
self.trivial_cast_lint(fcx);
721-
debug!(" -> CoercionCast");
722-
fcx.typeck_results
723-
.borrow_mut()
724-
.set_coercion_cast(self.expr.hir_id.local_id);
725-
}
726-
}
727-
Err(_) => {
728-
match self.do_check(fcx) {
729-
Ok(k) => {
730-
debug!(" -> {:?}", k);
731-
}
732-
Err(e) => self.report_cast_error(fcx, e),
733-
};
736+
self.trivial_cast_lint(fcx);
737+
debug!(" -> CoercionCast");
738+
fcx.typeck_results.borrow_mut().set_coercion_cast(self.expr.hir_id.local_id);
734739
}
740+
Err(_) => match self.do_check(fcx) {
741+
Ok(k) => {
742+
debug!(" -> {:?}", k);
743+
}
744+
Err(e) => self.report_cast_error(fcx, e),
745+
},
735746
};
736747
}
737748
}

compiler/rustc_hir_typeck/src/coercion.rs

+8-63
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ use rustc_attr_parsing::InlineAttr;
4242
use rustc_errors::codes::*;
4343
use rustc_errors::{Applicability, Diag, struct_span_code_err};
4444
use rustc_hir::def_id::{DefId, LocalDefId};
45-
use rustc_hir::{self as hir, LangItem};
45+
use rustc_hir::{self as hir};
4646
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
4747
use rustc_infer::infer::relate::RelateResult;
4848
use rustc_infer::infer::{Coercion, DefineOpaqueTypes, InferOk, InferResult};
@@ -55,7 +55,7 @@ use rustc_middle::ty::adjustment::{
5555
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion,
5656
};
5757
use rustc_middle::ty::error::TypeError;
58-
use rustc_middle::ty::{self, AliasTy, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
58+
use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
5959
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Span};
6060
use rustc_trait_selection::infer::InferCtxtExt as _;
6161
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
@@ -599,57 +599,6 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
599599
ty::TraitRef::new(self.tcx, coerce_unsized_did, [coerce_source, coerce_target]),
600600
);
601601

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

727676
// Dyn-compatibility violations or miscellaneous.
728-
Err(err) => {
729-
let guar = self.err_ctxt().report_selection_error(
730-
obligation.clone(),
731-
&obligation,
732-
&err,
733-
);
734-
self.fcx.set_tainted_by_errors(guar);
735-
// Treat this like an obligation and follow through
736-
// with the unsizing - the lack of a coercion should
737-
// be silent, as it causes a type mismatch later.
677+
Err(_) => {
678+
// Previously we reported an error here; instead of doing that,
679+
// we just register the failing obligation which will be reported
680+
// in the outer selection loop. This prevents reporting errors
681+
// in places like `may_coerce` calls.
682+
coercion.obligations.push(obligation);
738683
}
739684

740685
Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()),

tests/ui/issues/issue-22034.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ fn main() {
22
let ptr: *mut () = core::ptr::null_mut();
33
let _: &mut dyn Fn() = unsafe {
44
&mut *(ptr as *mut dyn Fn())
5-
//~^ ERROR expected a `Fn()` closure, found `()`
5+
//~^ ERROR cannot cast thin pointer `*mut ()` to wide pointer `*mut dyn Fn()`
66
};
77
}

tests/ui/issues/issue-22034.stderr

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
error[E0277]: expected a `Fn()` closure, found `()`
2-
--> $DIR/issue-22034.rs:4:16
1+
error[E0607]: cannot cast thin pointer `*mut ()` to wide pointer `*mut dyn Fn()`
2+
--> $DIR/issue-22034.rs:4:15
33
|
44
LL | &mut *(ptr as *mut dyn Fn())
5-
| ^^^ expected an `Fn()` closure, found `()`
6-
|
7-
= help: the trait `Fn()` is not implemented for `()`
8-
= note: wrap the `()` in a closure with no arguments: `|| { /* code */ }`
9-
= note: required for the cast from `*mut ()` to `*mut dyn Fn()`
5+
| ^^^^^^^^^^^^^^^^^^^^^^
106

117
error: aborting due to 1 previous error
128

13-
For more information about this error, try `rustc --explain E0277`.
9+
For more information about this error, try `rustc --explain E0607`.

tests/ui/mismatched_types/cast-rfc0401.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ fn main()
5050

5151
let _ = 42usize as *const [u8]; //~ ERROR cannot cast `usize` to a pointer that is wide
5252
let _ = v as *const [u8]; //~ ERROR cannot cast
53-
let _ = fat_v as *const dyn Foo; //~ ERROR the size for values of type
53+
let _ = fat_v as *const dyn Foo; //~ ERROR casting `*const [u8]` as `*const dyn Foo` is invalid
5454
let _ = foo as *const str; //~ ERROR is invalid
5555
let _ = foo as *mut str; //~ ERROR is invalid
5656
let _ = main as *mut str; //~ ERROR is invalid
@@ -59,7 +59,7 @@ fn main()
5959
let _ = fat_sv as usize; //~ ERROR is invalid
6060

6161
let a : *const str = "hello";
62-
let _ = a as *const dyn Foo; //~ ERROR the size for values of type
62+
let _ = a as *const dyn Foo; //~ ERROR casting `*const str` as `*const dyn Foo` is invalid
6363

6464
// check no error cascade
6565
let _ = main.f as *const u32; //~ ERROR no field

tests/ui/mismatched_types/cast-rfc0401.stderr

+17-19
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,14 @@ error[E0607]: cannot cast thin pointer `*const u8` to wide pointer `*const [u8]`
165165
LL | let _ = v as *const [u8];
166166
| ^^^^^^^^^^^^^^^^
167167

168+
error[E0606]: casting `*const [u8]` as `*const dyn Foo` is invalid
169+
--> $DIR/cast-rfc0401.rs:53:13
170+
|
171+
LL | let _ = fat_v as *const dyn Foo;
172+
| ^^^^^^^^^^^^^^^^^^^^^^^
173+
|
174+
= note: the pointers have different metadata
175+
168176
error[E0606]: casting `&dyn Foo` as `*const str` is invalid
169177
--> $DIR/cast-rfc0401.rs:54:13
170178
|
@@ -203,6 +211,14 @@ LL | let _ = fat_sv as usize;
203211
|
204212
= help: cast through a thin pointer first
205213

214+
error[E0606]: casting `*const str` as `*const dyn Foo` is invalid
215+
--> $DIR/cast-rfc0401.rs:62:13
216+
|
217+
LL | let _ = a as *const dyn Foo;
218+
| ^^^^^^^^^^^^^^^^^^^
219+
|
220+
= note: the pointers have different metadata
221+
206222
error[E0606]: casting `*const dyn Foo` as `*const [u16]` is invalid
207223
--> $DIR/cast-rfc0401.rs:68:13
208224
|
@@ -219,24 +235,6 @@ LL | let _ = cf as *const dyn Bar;
219235
|
220236
= note: the trait objects may have different vtables
221237

222-
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
223-
--> $DIR/cast-rfc0401.rs:53:13
224-
|
225-
LL | let _ = fat_v as *const dyn Foo;
226-
| ^^^^^ doesn't have a size known at compile-time
227-
|
228-
= help: the trait `Sized` is not implemented for `[u8]`
229-
= note: required for the cast from `*const [u8]` to `*const dyn Foo`
230-
231-
error[E0277]: the size for values of type `str` cannot be known at compilation time
232-
--> $DIR/cast-rfc0401.rs:62:13
233-
|
234-
LL | let _ = a as *const dyn Foo;
235-
| ^ doesn't have a size known at compile-time
236-
|
237-
= help: the trait `Sized` is not implemented for `str`
238-
= note: required for the cast from `*const str` to `*const dyn Foo`
239-
240238
error[E0606]: casting `&{float}` as `f32` is invalid
241239
--> $DIR/cast-rfc0401.rs:71:30
242240
|
@@ -250,5 +248,5 @@ LL | vec![0.0].iter().map(|s| *s as f32).collect::<Vec<f32>>();
250248

251249
error: aborting due to 34 previous errors
252250

253-
Some errors have detailed explanations: E0054, E0277, E0604, E0605, E0606, E0607, E0609.
251+
Some errors have detailed explanations: E0054, E0604, E0605, E0606, E0607, E0609.
254252
For more information about an error, try `rustc --explain E0054`.

0 commit comments

Comments
 (0)