Skip to content

Commit b28e7ac

Browse files
author
Lukas Markeffsky
committed
typeck ptr-to-ptr casts via traits
1 parent d8415b1 commit b28e7ac

File tree

5 files changed

+43
-35
lines changed

5 files changed

+43
-35
lines changed

compiler/rustc_hir_typeck/src/cast.rs

+32-24
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ use super::FnCtxt;
3232

3333
use crate::errors;
3434
use crate::type_error_struct;
35-
use hir::ExprKind;
35+
use hir::{ExprKind, LangItem};
3636
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
3737
use rustc_hir as hir;
38-
use rustc_macros::{TypeFoldable, TypeVisitable};
38+
use rustc_infer::traits::Obligation;
3939
use rustc_middle::mir::Mutability;
4040
use rustc_middle::ty::adjustment::AllowTwoPhase;
4141
use rustc_middle::ty::cast::{CastKind, CastTy};
@@ -45,7 +45,8 @@ use rustc_session::lint;
4545
use rustc_span::def_id::{DefId, LOCAL_CRATE};
4646
use rustc_span::symbol::sym;
4747
use rustc_span::Span;
48-
use rustc_trait_selection::infer::InferCtxtExt;
48+
use rustc_trait_selection::infer::InferCtxtExt as _;
49+
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
4950

5051
/// Reifies a cast check to be checked once we have full type information for
5152
/// a function context.
@@ -67,7 +68,7 @@ pub struct CastCheck<'tcx> {
6768
/// The kind of pointer and associated metadata (thin, length or vtable) - we
6869
/// only allow casts between fat pointers if their metadata have the same
6970
/// kind.
70-
#[derive(Debug, Copy, Clone, PartialEq, Eq, TypeVisitable, TypeFoldable)]
71+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
7172
enum PointerKind<'tcx> {
7273
/// No metadata attached, ie pointer to sized type or foreign type
7374
Thin,
@@ -787,33 +788,40 @@ impl<'a, 'tcx> CastCheck<'tcx> {
787788
) -> Result<(), CastError> {
788789
debug!(?expr, ?cast, "check_ptr_ptr_cast");
789790

790-
let expr_kind = fcx.pointer_kind(expr, self.span)?;
791-
let cast_kind = fcx.pointer_kind(cast, self.span)?;
791+
let meta_did = fcx.tcx.require_lang_item(LangItem::Metadata, Some(self.span));
792+
let expr_meta = Ty::new_projection(fcx.tcx, meta_did, [expr]);
793+
let cast_meta = Ty::new_projection(fcx.tcx, meta_did, [cast]);
794+
let expr_meta = fcx.normalize(self.span, expr_meta);
795+
let cast_meta = fcx.normalize(self.span, cast_meta);
792796

793-
let Some(cast_kind) = cast_kind else {
794-
// We can't cast if target pointer kind is unknown
795-
return Err(CastError::UnknownCastPtrKind);
796-
};
797+
let pred = ty::TraitRef::from_lang_item(
798+
fcx.tcx,
799+
LangItem::MetadataCast,
800+
self.span,
801+
[expr_meta, cast_meta],
802+
);
797803

798-
// Cast to thin pointer is OK
799-
if cast_kind == PointerKind::Thin {
804+
let obligation = Obligation::new(fcx.tcx, fcx.misc(self.span), fcx.param_env, pred);
805+
if fcx.predicate_must_hold_modulo_regions(&obligation) {
800806
return Ok(());
801807
}
802808

803-
let Some(expr_kind) = expr_kind else {
804-
// We can't cast to fat pointer if source pointer kind is unknown
805-
return Err(CastError::UnknownExprPtrKind);
806-
};
807-
808-
// thin -> fat? report invalid cast (don't complain about vtable kinds)
809-
if expr_kind == PointerKind::Thin {
810-
return Err(CastError::SizedUnsizedCast);
811-
}
809+
expr_meta.error_reported()?;
810+
cast_meta.error_reported()?;
812811

813-
// vtable kinds must match
814-
if fcx.tcx.erase_regions(cast_kind) == fcx.tcx.erase_regions(expr_kind) {
815-
Ok(())
812+
if cast_meta == fcx.tcx.types.unit {
813+
span_bug!(self.span, "cast to thin pointer, but predicate didn't hold: {pred}");
814+
} else if cast_meta.has_infer_types() {
815+
// We can't cast if target pointer kind is unknown
816+
Err(CastError::UnknownCastPtrKind)
817+
} else if expr_meta.has_infer_types() {
818+
// We can't cast to fat pointer if source pointer kind is unknown
819+
Err(CastError::UnknownExprPtrKind)
820+
} else if expr_meta == fcx.tcx.types.unit {
821+
// thin -> fat? report invalid cast (don't complain about metadata kinds)
822+
Err(CastError::SizedUnsizedCast)
816823
} else {
824+
// metadata kinds must match
817825
Err(CastError::DifferingKinds)
818826
}
819827
}

tests/ui/traits/metadata-cast-fail-lifetimes.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ fn cast<T: ?Sized, U: ?Sized>(ptr: *mut T) -> *mut U
66
where
77
T: PointerCast<U>,
88
{
9-
todo!()
9+
ptr as _
1010
}
1111

1212
fn check<T: ?Sized, U: ?Sized>()

tests/ui/traits/metadata-cast-fail-traits.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
use std::ptr::{DynMetadata, MetadataCast, PointerCast};
44

5-
fn cast<T: ?Sized, U: ?Sized>(_: *mut T) -> *mut U
5+
fn cast<T: ?Sized, U: ?Sized>(ptr: *mut T) -> *mut U
66
where
77
T: PointerCast<U>,
88
{
9-
todo!()
9+
ptr as _
1010
}
1111

1212
fn check<T: ?Sized, U: ?Sized>()

tests/ui/traits/metadata-cast-fail-traits.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ LL | let _: *mut [u8] = cast(thin);
130130
note: required by a bound in `cast`
131131
--> $DIR/metadata-cast-fail-traits.rs:7:8
132132
|
133-
LL | fn cast<T: ?Sized, U: ?Sized>(_: *mut T) -> *mut U
133+
LL | fn cast<T: ?Sized, U: ?Sized>(ptr: *mut T) -> *mut U
134134
| ---- required by a bound in this function
135135
LL | where
136136
LL | T: PointerCast<U>,
@@ -148,7 +148,7 @@ LL | let _: *mut dyn Trait = cast(thin);
148148
note: required by a bound in `cast`
149149
--> $DIR/metadata-cast-fail-traits.rs:7:8
150150
|
151-
LL | fn cast<T: ?Sized, U: ?Sized>(_: *mut T) -> *mut U
151+
LL | fn cast<T: ?Sized, U: ?Sized>(ptr: *mut T) -> *mut U
152152
| ---- required by a bound in this function
153153
LL | where
154154
LL | T: PointerCast<U>,
@@ -166,7 +166,7 @@ LL | let _: *mut [u8] = cast(trait_object);
166166
note: required by a bound in `cast`
167167
--> $DIR/metadata-cast-fail-traits.rs:7:8
168168
|
169-
LL | fn cast<T: ?Sized, U: ?Sized>(_: *mut T) -> *mut U
169+
LL | fn cast<T: ?Sized, U: ?Sized>(ptr: *mut T) -> *mut U
170170
| ---- required by a bound in this function
171171
LL | where
172172
LL | T: PointerCast<U>,
@@ -184,7 +184,7 @@ LL | let _: *mut dyn Trait = cast(slice);
184184
note: required by a bound in `cast`
185185
--> $DIR/metadata-cast-fail-traits.rs:7:8
186186
|
187-
LL | fn cast<T: ?Sized, U: ?Sized>(_: *mut T) -> *mut U
187+
LL | fn cast<T: ?Sized, U: ?Sized>(ptr: *mut T) -> *mut U
188188
| ---- required by a bound in this function
189189
LL | where
190190
LL | T: PointerCast<U>,
@@ -202,7 +202,7 @@ LL | let _: *mut dyn Trait2 = cast(trait_object);
202202
note: required by a bound in `cast`
203203
--> $DIR/metadata-cast-fail-traits.rs:7:8
204204
|
205-
LL | fn cast<T: ?Sized, U: ?Sized>(_: *mut T) -> *mut U
205+
LL | fn cast<T: ?Sized, U: ?Sized>(ptr: *mut T) -> *mut U
206206
| ---- required by a bound in this function
207207
LL | where
208208
LL | T: PointerCast<U>,
@@ -220,7 +220,7 @@ LL | let _: *mut dyn Send = cast(trait_object);
220220
note: required by a bound in `cast`
221221
--> $DIR/metadata-cast-fail-traits.rs:7:8
222222
|
223-
LL | fn cast<T: ?Sized, U: ?Sized>(_: *mut T) -> *mut U
223+
LL | fn cast<T: ?Sized, U: ?Sized>(ptr: *mut T) -> *mut U
224224
| ---- required by a bound in this function
225225
LL | where
226226
LL | T: PointerCast<U>,

tests/ui/traits/metadata-cast-pass.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33

44
use std::ptr::{self, DynMetadata, MetadataCast, Pointee, PointerCast, Thin};
55

6-
fn cast<T: ?Sized, U: ?Sized>(_: *mut T) -> *mut U
6+
fn cast<T: ?Sized, U: ?Sized>(ptr: *mut T) -> *mut U
77
where
88
T: PointerCast<U>,
99
{
10-
todo!()
10+
ptr as _
1111
}
1212

1313
fn check<T: ?Sized, U: ?Sized>()

0 commit comments

Comments
 (0)