Auto merge of #101832 - compiler-errors:dyn-star-plus, r=eholk
Make `dyn*` casts into a coercion, allow `dyn*` upcasting I know that `dyn*` is likely not going to be a feature exposed to surface Rust, but this makes it slightly more ergonomic to write tests for these types anyways. ... and this was just fun to implement anyways. 1. Make `dyn*` into a coercion instead of a cast 2. Enable `dyn*` upcasting since we basically get it for free 3. Simplify some of the cast checking code since we're using the coercion path now r? `@eholk` but feel free to reassign cc `@nikomatsakis` and `@tmandry` who might care about making `dyn*` casts into a coercion
This commit is contained in:
commit
b15e2c129e
21 changed files with 233 additions and 136 deletions
|
@ -38,7 +38,7 @@ use rustc_session::Session;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::Symbol;
|
use rustc_span::Symbol;
|
||||||
use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType};
|
use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType};
|
||||||
use rustc_target::abi::{Align, VariantIdx};
|
use rustc_target::abi::{Align, Size, VariantIdx};
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -150,7 +150,12 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||||
(&ty::Array(_, len), &ty::Slice(_)) => {
|
(&ty::Array(_, len), &ty::Slice(_)) => {
|
||||||
cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all()))
|
cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all()))
|
||||||
}
|
}
|
||||||
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
|
(
|
||||||
|
&ty::Dynamic(ref data_a, _, src_dyn_kind),
|
||||||
|
&ty::Dynamic(ref data_b, _, target_dyn_kind),
|
||||||
|
) => {
|
||||||
|
assert_eq!(src_dyn_kind, target_dyn_kind);
|
||||||
|
|
||||||
let old_info =
|
let old_info =
|
||||||
old_info.expect("unsized_info: missing old info for trait upcasting coercion");
|
old_info.expect("unsized_info: missing old info for trait upcasting coercion");
|
||||||
if data_a.principal_def_id() == data_b.principal_def_id() {
|
if data_a.principal_def_id() == data_b.principal_def_id() {
|
||||||
|
@ -166,11 +171,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||||
if let Some(entry_idx) = vptr_entry_idx {
|
if let Some(entry_idx) = vptr_entry_idx {
|
||||||
let ptr_ty = cx.type_i8p();
|
let ptr_ty = cx.type_i8p();
|
||||||
let ptr_align = cx.tcx().data_layout.pointer_align.abi;
|
let ptr_align = cx.tcx().data_layout.pointer_align.abi;
|
||||||
let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
|
let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind);
|
||||||
cx.layout_of(cx.tcx().mk_mut_ptr(target)),
|
|
||||||
1,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty));
|
let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty));
|
||||||
let gep = bx.inbounds_gep(
|
let gep = bx.inbounds_gep(
|
||||||
ptr_ty,
|
ptr_ty,
|
||||||
|
@ -186,18 +187,32 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||||
old_info
|
old_info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(_, &ty::Dynamic(ref data, ..)) => {
|
(_, &ty::Dynamic(ref data, _, target_dyn_kind)) => {
|
||||||
let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
|
let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind);
|
||||||
cx.layout_of(cx.tcx().mk_mut_ptr(target)),
|
|
||||||
1,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty)
|
cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty)
|
||||||
}
|
}
|
||||||
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
|
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the vtable pointer type of a `dyn` or `dyn*` type
|
||||||
|
fn vtable_ptr_ty<'tcx, Cx: CodegenMethods<'tcx>>(
|
||||||
|
cx: &Cx,
|
||||||
|
target: Ty<'tcx>,
|
||||||
|
kind: ty::DynKind,
|
||||||
|
) -> <Cx as BackendTypes>::Type {
|
||||||
|
cx.scalar_pair_element_backend_type(
|
||||||
|
cx.layout_of(match kind {
|
||||||
|
// vtable is the second field of `*mut dyn Trait`
|
||||||
|
ty::Dyn => cx.tcx().mk_mut_ptr(target),
|
||||||
|
// vtable is the second field of `dyn* Trait`
|
||||||
|
ty::DynStar => target,
|
||||||
|
}),
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Coerces `src` to `dst_ty`. `src_ty` must be a pointer.
|
/// Coerces `src` to `dst_ty`. `src_ty` must be a pointer.
|
||||||
pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||||
bx: &mut Bx,
|
bx: &mut Bx,
|
||||||
|
@ -247,6 +262,29 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Coerces `src` to `dst_ty` which is guaranteed to be a `dyn*` type.
|
||||||
|
pub fn cast_to_dyn_star<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||||
|
bx: &mut Bx,
|
||||||
|
src: Bx::Value,
|
||||||
|
src_ty_and_layout: TyAndLayout<'tcx>,
|
||||||
|
dst_ty: Ty<'tcx>,
|
||||||
|
old_info: Option<Bx::Value>,
|
||||||
|
) -> (Bx::Value, Bx::Value) {
|
||||||
|
debug!("cast_to_dyn_star: {:?} => {:?}", src_ty_and_layout.ty, dst_ty);
|
||||||
|
assert!(
|
||||||
|
matches!(dst_ty.kind(), ty::Dynamic(_, _, ty::DynStar)),
|
||||||
|
"destination type must be a dyn*"
|
||||||
|
);
|
||||||
|
// FIXME(dyn-star): this is probably not the best way to check if this is
|
||||||
|
// a pointer, and really we should ensure that the value is a suitable
|
||||||
|
// pointer earlier in the compilation process.
|
||||||
|
let src = match src_ty_and_layout.pointee_info_at(bx.cx(), Size::ZERO) {
|
||||||
|
Some(_) => bx.ptrtoint(src, bx.cx().type_isize()),
|
||||||
|
None => bx.bitcast(src, bx.type_isize()),
|
||||||
|
};
|
||||||
|
(src, unsized_info(bx, src_ty_and_layout.ty, dst_ty, old_info))
|
||||||
|
}
|
||||||
|
|
||||||
/// Coerces `src`, which is a reference to a value of type `src_ty`,
|
/// Coerces `src`, which is a reference to a value of type `src_ty`,
|
||||||
/// to a value of type `dst_ty`, and stores the result in `dst`.
|
/// to a value of type `dst_ty`, and stores the result in `dst`.
|
||||||
pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||||
|
|
|
@ -4,7 +4,6 @@ use super::{FunctionCx, LocalRef};
|
||||||
|
|
||||||
use crate::base;
|
use crate::base;
|
||||||
use crate::common::{self, IntPredicate};
|
use crate::common::{self, IntPredicate};
|
||||||
use crate::meth::get_vtable;
|
|
||||||
use crate::traits::*;
|
use crate::traits::*;
|
||||||
use crate::MemFlags;
|
use crate::MemFlags;
|
||||||
|
|
||||||
|
@ -14,7 +13,6 @@ use rustc_middle::ty::cast::{CastTy, IntTy};
|
||||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||||
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
|
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
|
||||||
use rustc_span::source_map::{Span, DUMMY_SP};
|
use rustc_span::source_map::{Span, DUMMY_SP};
|
||||||
use rustc_target::abi::Size;
|
|
||||||
|
|
||||||
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
#[instrument(level = "trace", skip(self, bx))]
|
#[instrument(level = "trace", skip(self, bx))]
|
||||||
|
@ -274,27 +272,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mir::CastKind::DynStar => {
|
mir::CastKind::DynStar => {
|
||||||
let data = match operand.val {
|
let (lldata, llextra) = match operand.val {
|
||||||
OperandValue::Ref(_, _, _) => todo!(),
|
OperandValue::Ref(_, _, _) => todo!(),
|
||||||
OperandValue::Immediate(v) => v,
|
OperandValue::Immediate(v) => (v, None),
|
||||||
OperandValue::Pair(_, _) => todo!(),
|
OperandValue::Pair(v, l) => (v, Some(l)),
|
||||||
};
|
};
|
||||||
let trait_ref =
|
let (lldata, llextra) =
|
||||||
if let ty::Dynamic(data, _, ty::DynStar) = cast.ty.kind() {
|
base::cast_to_dyn_star(&mut bx, lldata, operand.layout, cast.ty, llextra);
|
||||||
data.principal()
|
OperandValue::Pair(lldata, llextra)
|
||||||
} else {
|
|
||||||
bug!("Only valid to do a DynStar cast into a DynStar type")
|
|
||||||
};
|
|
||||||
let vtable = get_vtable(bx.cx(), source.ty(self.mir, bx.tcx()), trait_ref);
|
|
||||||
let vtable = bx.pointercast(vtable, bx.cx().type_ptr_to(bx.cx().type_isize()));
|
|
||||||
// FIXME(dyn-star): this is probably not the best way to check if this is
|
|
||||||
// a pointer, and really we should ensure that the value is a suitable
|
|
||||||
// pointer earlier in the compilation process.
|
|
||||||
let data = match operand.layout.pointee_info_at(bx.cx(), Size::ZERO) {
|
|
||||||
Some(_) => bx.ptrtoint(data, bx.cx().type_isize()),
|
|
||||||
None => data,
|
|
||||||
};
|
|
||||||
OperandValue::Pair(data, vtable)
|
|
||||||
}
|
}
|
||||||
mir::CastKind::Pointer(
|
mir::CastKind::Pointer(
|
||||||
PointerCast::MutToConstPointer | PointerCast::ArrayToPointer,
|
PointerCast::MutToConstPointer | PointerCast::ArrayToPointer,
|
||||||
|
|
|
@ -35,13 +35,12 @@ use crate::type_error_struct;
|
||||||
use hir::def_id::LOCAL_CRATE;
|
use hir::def_id::LOCAL_CRATE;
|
||||||
use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed};
|
use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode};
|
|
||||||
use rustc_middle::mir::Mutability;
|
use rustc_middle::mir::Mutability;
|
||||||
use rustc_middle::ty::adjustment::AllowTwoPhase;
|
use rustc_middle::ty::adjustment::AllowTwoPhase;
|
||||||
use rustc_middle::ty::cast::{CastKind, CastTy};
|
use rustc_middle::ty::cast::{CastKind, CastTy};
|
||||||
use rustc_middle::ty::error::TypeError;
|
use rustc_middle::ty::error::TypeError;
|
||||||
use rustc_middle::ty::subst::SubstsRef;
|
use rustc_middle::ty::subst::SubstsRef;
|
||||||
use rustc_middle::ty::{self, Binder, Ty, TypeAndMut, TypeVisitable, VariantDef};
|
use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable, VariantDef};
|
||||||
use rustc_session::lint;
|
use rustc_session::lint;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
@ -204,76 +203,8 @@ fn make_invalid_casting_error<'a, 'tcx>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum CastCheckResult<'tcx> {
|
|
||||||
Ok,
|
|
||||||
Deferred(CastCheck<'tcx>),
|
|
||||||
Err(ErrorGuaranteed),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_cast<'tcx>(
|
|
||||||
fcx: &FnCtxt<'_, 'tcx>,
|
|
||||||
expr: &'tcx hir::Expr<'tcx>,
|
|
||||||
expr_ty: Ty<'tcx>,
|
|
||||||
cast_ty: Ty<'tcx>,
|
|
||||||
cast_span: Span,
|
|
||||||
span: Span,
|
|
||||||
) -> CastCheckResult<'tcx> {
|
|
||||||
if cast_ty.is_dyn_star() {
|
|
||||||
check_dyn_star_cast(fcx, expr, expr_ty, cast_ty)
|
|
||||||
} else {
|
|
||||||
match CastCheck::new(fcx, expr, expr_ty, cast_ty, cast_span, span) {
|
|
||||||
Ok(check) => CastCheckResult::Deferred(check),
|
|
||||||
Err(e) => CastCheckResult::Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_dyn_star_cast<'tcx>(
|
|
||||||
fcx: &FnCtxt<'_, 'tcx>,
|
|
||||||
expr: &'tcx hir::Expr<'tcx>,
|
|
||||||
expr_ty: Ty<'tcx>,
|
|
||||||
cast_ty: Ty<'tcx>,
|
|
||||||
) -> CastCheckResult<'tcx> {
|
|
||||||
// Find the bounds in the dyn*. For eaxmple, if we have
|
|
||||||
//
|
|
||||||
// let x = 22_usize as dyn* (Clone + Debug + 'static)
|
|
||||||
//
|
|
||||||
// this would return `existential_predicates = [?Self: Clone, ?Self: Debug]` and `region = 'static`.
|
|
||||||
let (existential_predicates, region) = match cast_ty.kind() {
|
|
||||||
ty::Dynamic(predicates, region, ty::DynStar) => (predicates, region),
|
|
||||||
_ => panic!("Invalid dyn* cast_ty"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let cause = ObligationCause::new(
|
|
||||||
expr.span,
|
|
||||||
fcx.body_id,
|
|
||||||
// FIXME(dyn-star): Use a better obligation cause code
|
|
||||||
ObligationCauseCode::MiscObligation,
|
|
||||||
);
|
|
||||||
|
|
||||||
// For each existential predicate (e.g., `?Self: Clone`) substitute
|
|
||||||
// the type of the expression (e.g., `usize` in our example above)
|
|
||||||
// and then require that the resulting predicate (e.g., `usize: Clone`)
|
|
||||||
// holds (it does).
|
|
||||||
for existential_predicate in existential_predicates.iter() {
|
|
||||||
let predicate = existential_predicate.with_self_ty(fcx.tcx, expr_ty);
|
|
||||||
fcx.register_predicate(Obligation::new(cause.clone(), fcx.param_env, predicate));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enforce the region bound `'static` (e.g., `usize: 'static`, in our example).
|
|
||||||
fcx.register_predicate(Obligation::new(
|
|
||||||
cause,
|
|
||||||
fcx.param_env,
|
|
||||||
fcx.tcx.mk_predicate(Binder::dummy(ty::PredicateKind::TypeOutlives(
|
|
||||||
ty::OutlivesPredicate(expr_ty, *region),
|
|
||||||
))),
|
|
||||||
));
|
|
||||||
|
|
||||||
CastCheckResult::Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'tcx> CastCheck<'tcx> {
|
impl<'a, 'tcx> CastCheck<'tcx> {
|
||||||
fn new(
|
pub fn new(
|
||||||
fcx: &FnCtxt<'a, 'tcx>,
|
fcx: &FnCtxt<'a, 'tcx>,
|
||||||
expr: &'tcx hir::Expr<'tcx>,
|
expr: &'tcx hir::Expr<'tcx>,
|
||||||
expr_ty: Ty<'tcx>,
|
expr_ty: Ty<'tcx>,
|
||||||
|
@ -934,11 +865,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
|
||||||
|
|
||||||
(Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast),
|
(Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast),
|
||||||
|
|
||||||
// FIXME(dyn-star): this needs more conditions...
|
(_, DynStar) | (DynStar, _) => bug!("should be handled by `try_coerce`"),
|
||||||
(_, DynStar) => Ok(CastKind::DynStarCast),
|
|
||||||
|
|
||||||
// FIXME(dyn-star): do we want to allow dyn* upcasting or other casts?
|
|
||||||
(DynStar, _) => Err(CastError::IllegalCast),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -216,6 +216,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
||||||
ty::Ref(r_b, _, mutbl_b) => {
|
ty::Ref(r_b, _, mutbl_b) => {
|
||||||
return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b);
|
return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b);
|
||||||
}
|
}
|
||||||
|
ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star => {
|
||||||
|
return self.coerce_dyn_star(a, b, predicates, region);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -745,6 +748,63 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
||||||
Ok(coercion)
|
Ok(coercion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn coerce_dyn_star(
|
||||||
|
&self,
|
||||||
|
a: Ty<'tcx>,
|
||||||
|
b: Ty<'tcx>,
|
||||||
|
predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
|
||||||
|
b_region: ty::Region<'tcx>,
|
||||||
|
) -> CoerceResult<'tcx> {
|
||||||
|
if !self.tcx.features().dyn_star {
|
||||||
|
return Err(TypeError::Mismatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let ty::Dynamic(a_data, _, _) = a.kind()
|
||||||
|
&& let ty::Dynamic(b_data, _, _) = b.kind()
|
||||||
|
{
|
||||||
|
if a_data.principal_def_id() == b_data.principal_def_id() {
|
||||||
|
return self.unify_and(a, b, |_| vec![]);
|
||||||
|
} else if !self.tcx().features().trait_upcasting {
|
||||||
|
let mut err = feature_err(
|
||||||
|
&self.tcx.sess.parse_sess,
|
||||||
|
sym::trait_upcasting,
|
||||||
|
self.cause.span,
|
||||||
|
&format!(
|
||||||
|
"cannot cast `{a}` to `{b}`, trait upcasting coercion is experimental"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
err.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the obligations of the cast -- for example, when casting
|
||||||
|
// `usize` to `dyn* Clone + 'static`:
|
||||||
|
let obligations = predicates
|
||||||
|
.iter()
|
||||||
|
.map(|predicate| {
|
||||||
|
// For each existential predicate (e.g., `?Self: Clone`) substitute
|
||||||
|
// the type of the expression (e.g., `usize` in our example above)
|
||||||
|
// and then require that the resulting predicate (e.g., `usize: Clone`)
|
||||||
|
// holds (it does).
|
||||||
|
let predicate = predicate.with_self_ty(self.tcx, a);
|
||||||
|
Obligation::new(self.cause.clone(), self.param_env, predicate)
|
||||||
|
})
|
||||||
|
// Enforce the region bound (e.g., `usize: 'static`, in our example).
|
||||||
|
.chain([Obligation::new(
|
||||||
|
self.cause.clone(),
|
||||||
|
self.param_env,
|
||||||
|
self.tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::TypeOutlives(
|
||||||
|
ty::OutlivesPredicate(a, b_region),
|
||||||
|
))),
|
||||||
|
)])
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(InferOk {
|
||||||
|
value: (vec![Adjustment { kind: Adjust::DynStar, target: b }], b),
|
||||||
|
obligations,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn coerce_from_safe_fn<F, G>(
|
fn coerce_from_safe_fn<F, G>(
|
||||||
&self,
|
&self,
|
||||||
a: Ty<'tcx>,
|
a: Ty<'tcx>,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
//! See `mod.rs` for more context on type checking in general.
|
//! See `mod.rs` for more context on type checking in general.
|
||||||
|
|
||||||
use crate::astconv::AstConv as _;
|
use crate::astconv::AstConv as _;
|
||||||
use crate::check::cast::{self, CastCheckResult};
|
use crate::check::cast;
|
||||||
use crate::check::coercion::CoerceMany;
|
use crate::check::coercion::CoerceMany;
|
||||||
use crate::check::fatally_break_rust;
|
use crate::check::fatally_break_rust;
|
||||||
use crate::check::method::SelfSource;
|
use crate::check::method::SelfSource;
|
||||||
|
@ -1270,9 +1270,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
} else {
|
} else {
|
||||||
// Defer other checks until we're done type checking.
|
// Defer other checks until we're done type checking.
|
||||||
let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
|
let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
|
||||||
match cast::check_cast(self, e, t_expr, t_cast, t.span, expr.span) {
|
match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) {
|
||||||
CastCheckResult::Ok => t_cast,
|
Ok(cast_check) => {
|
||||||
CastCheckResult::Deferred(cast_check) => {
|
|
||||||
debug!(
|
debug!(
|
||||||
"check_expr_cast: deferring cast from {:?} to {:?}: {:?}",
|
"check_expr_cast: deferring cast from {:?} to {:?}: {:?}",
|
||||||
t_cast, t_expr, cast_check,
|
t_cast, t_expr, cast_check,
|
||||||
|
@ -1280,7 +1279,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
deferred_cast_checks.push(cast_check);
|
deferred_cast_checks.push(cast_check);
|
||||||
t_cast
|
t_cast
|
||||||
}
|
}
|
||||||
CastCheckResult::Err(ErrorGuaranteed { .. }) => self.tcx.ty_error(),
|
Err(_) => self.tcx.ty_error(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -583,7 +583,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||||
for adjustment in adjustments {
|
for adjustment in adjustments {
|
||||||
debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment);
|
debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment);
|
||||||
match adjustment.kind {
|
match adjustment.kind {
|
||||||
adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => {
|
adjustment::Adjust::NeverToAny
|
||||||
|
| adjustment::Adjust::Pointer(_)
|
||||||
|
| adjustment::Adjust::DynStar => {
|
||||||
// Creating a closure/fn-pointer or unsizing consumes
|
// Creating a closure/fn-pointer or unsizing consumes
|
||||||
// the input and stores it into the resulting rvalue.
|
// the input and stores it into the resulting rvalue.
|
||||||
self.delegate_consume(&place_with_id, place_with_id.hir_id);
|
self.delegate_consume(&place_with_id, place_with_id.hir_id);
|
||||||
|
|
|
@ -292,7 +292,8 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
||||||
|
|
||||||
adjustment::Adjust::NeverToAny
|
adjustment::Adjust::NeverToAny
|
||||||
| adjustment::Adjust::Pointer(_)
|
| adjustment::Adjust::Pointer(_)
|
||||||
| adjustment::Adjust::Borrow(_) => {
|
| adjustment::Adjust::Borrow(_)
|
||||||
|
| adjustment::Adjust::DynStar => {
|
||||||
// Result is an rvalue.
|
// Result is an rvalue.
|
||||||
Ok(self.cat_rvalue(expr.hir_id, expr.span, target))
|
Ok(self.cat_rvalue(expr.hir_id, expr.span, target))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1824,7 +1824,6 @@ impl<'tcx> Rvalue<'tcx> {
|
||||||
// While the model is undecided, we should be conservative. See
|
// While the model is undecided, we should be conservative. See
|
||||||
// <https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html>
|
// <https://www.ralfj.de/blog/2022/04/11/provenance-exposed.html>
|
||||||
Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false,
|
Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false,
|
||||||
Rvalue::Cast(CastKind::DynStar, _, _) => false,
|
|
||||||
|
|
||||||
Rvalue::Use(_)
|
Rvalue::Use(_)
|
||||||
| Rvalue::CopyForDeref(_)
|
| Rvalue::CopyForDeref(_)
|
||||||
|
@ -1841,7 +1840,8 @@ impl<'tcx> Rvalue<'tcx> {
|
||||||
| CastKind::FnPtrToPtr
|
| CastKind::FnPtrToPtr
|
||||||
| CastKind::PtrToPtr
|
| CastKind::PtrToPtr
|
||||||
| CastKind::Pointer(_)
|
| CastKind::Pointer(_)
|
||||||
| CastKind::PointerFromExposedAddress,
|
| CastKind::PointerFromExposedAddress
|
||||||
|
| CastKind::DynStar,
|
||||||
_,
|
_,
|
||||||
_,
|
_,
|
||||||
)
|
)
|
||||||
|
|
|
@ -101,6 +101,9 @@ pub enum Adjust<'tcx> {
|
||||||
Borrow(AutoBorrow<'tcx>),
|
Borrow(AutoBorrow<'tcx>),
|
||||||
|
|
||||||
Pointer(PointerCast),
|
Pointer(PointerCast),
|
||||||
|
|
||||||
|
/// Cast into a dyn* object.
|
||||||
|
DynStar,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)`
|
/// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)`
|
||||||
|
|
|
@ -159,6 +159,7 @@ impl<'tcx> Cx<'tcx> {
|
||||||
Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => {
|
Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => {
|
||||||
ExprKind::AddressOf { mutability, arg: self.thir.exprs.push(expr) }
|
ExprKind::AddressOf { mutability, arg: self.thir.exprs.push(expr) }
|
||||||
}
|
}
|
||||||
|
Adjust::DynStar => ExprKind::Cast { source: self.thir.exprs.push(expr) },
|
||||||
};
|
};
|
||||||
|
|
||||||
Expr { temp_lifetime, ty: adjustment.target, span, kind }
|
Expr { temp_lifetime, ty: adjustment.target, span, kind }
|
||||||
|
|
10
src/test/ui/dyn-star/auxiliary/dyn-star-foreign.rs
Normal file
10
src/test/ui/dyn-star/auxiliary/dyn-star-foreign.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#![feature(dyn_star)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
pub fn require_dyn_star_display(_: dyn* Display) {}
|
||||||
|
|
||||||
|
fn works_locally() {
|
||||||
|
require_dyn_star_display(1usize);
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ use std::fmt::Debug;
|
||||||
|
|
||||||
fn make_dyn_star() {
|
fn make_dyn_star() {
|
||||||
let i = 42usize;
|
let i = 42usize;
|
||||||
let dyn_i: dyn* Debug = i as dyn* Debug;
|
let dyn_i: dyn* Debug = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -15,7 +15,7 @@ impl Drop for Foo {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_dyn_star(i: Foo) {
|
fn make_dyn_star(i: Foo) {
|
||||||
let _dyn_i: dyn* Debug = i as dyn* Debug;
|
let _dyn_i: dyn* Debug = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -7,7 +7,7 @@ trait Foo {}
|
||||||
|
|
||||||
fn make_dyn_star() {
|
fn make_dyn_star() {
|
||||||
let i = 42;
|
let i = 42;
|
||||||
let dyn_i: dyn* Foo = i as dyn* Foo; //~ ERROR trait bound `{integer}: Foo` is not satisfied
|
let dyn_i: dyn* Foo = i; //~ ERROR trait bound `{integer}: Foo` is not satisfied
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
error[E0277]: the trait bound `{integer}: Foo` is not satisfied
|
error[E0277]: the trait bound `{integer}: Foo` is not satisfied
|
||||||
--> $DIR/error.rs:10:27
|
--> $DIR/error.rs:10:27
|
||||||
|
|
|
|
||||||
LL | let dyn_i: dyn* Foo = i as dyn* Foo;
|
LL | let dyn_i: dyn* Foo = i;
|
||||||
| ^ the trait `Foo` is not implemented for `{integer}`
|
| ^ the trait `Foo` is not implemented for `{integer}`
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
|
@ -5,9 +5,14 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
fn make_dyn_star(i: usize) {
|
fn make_dyn_star(i: usize) {
|
||||||
|
let _dyn_i: dyn* Debug = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_dyn_star_explicit(i: usize) {
|
||||||
let _dyn_i: dyn* Debug = i as dyn* Debug;
|
let _dyn_i: dyn* Debug = i as dyn* Debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
make_dyn_star(42);
|
make_dyn_star(42);
|
||||||
|
make_dyn_star_explicit(42);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// run-pass
|
// run-pass
|
||||||
|
|
||||||
#![feature(dyn_star)]
|
#![feature(dyn_star)]
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
@ -17,7 +18,7 @@ fn invoke_dyn_star(i: dyn* Foo) -> usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_and_invoke_dyn_star(i: usize) -> usize {
|
fn make_and_invoke_dyn_star(i: usize) -> usize {
|
||||||
let dyn_i: dyn* Foo = i as dyn* Foo;
|
let dyn_i: dyn* Foo = i;
|
||||||
invoke_dyn_star(dyn_i)
|
invoke_dyn_star(dyn_i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
8
src/test/ui/dyn-star/no-implicit-dyn-star.rs
Normal file
8
src/test/ui/dyn-star/no-implicit-dyn-star.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// aux-build:dyn-star-foreign.rs
|
||||||
|
|
||||||
|
extern crate dyn_star_foreign;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
dyn_star_foreign::require_dyn_star_display(1usize);
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
}
|
19
src/test/ui/dyn-star/no-implicit-dyn-star.stderr
Normal file
19
src/test/ui/dyn-star/no-implicit-dyn-star.stderr
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/no-implicit-dyn-star.rs:6:48
|
||||||
|
|
|
||||||
|
LL | dyn_star_foreign::require_dyn_star_display(1usize);
|
||||||
|
| ------------------------------------------ ^^^^^^ expected trait object `dyn std::fmt::Display`, found `usize`
|
||||||
|
| |
|
||||||
|
| arguments to this function are incorrect
|
||||||
|
|
|
||||||
|
= note: expected trait object `(dyn* std::fmt::Display + 'static)`
|
||||||
|
found type `usize`
|
||||||
|
note: function defined here
|
||||||
|
--> $DIR/auxiliary/dyn-star-foreign.rs:6:8
|
||||||
|
|
|
||||||
|
LL | pub fn require_dyn_star_display(_: dyn* Display) {}
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
33
src/test/ui/dyn-star/upcast.rs
Normal file
33
src/test/ui/dyn-star/upcast.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// run-pass
|
||||||
|
|
||||||
|
#![feature(dyn_star, trait_upcasting)]
|
||||||
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
trait Foo: Bar {
|
||||||
|
fn hello(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Bar {
|
||||||
|
fn world(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct W(usize);
|
||||||
|
|
||||||
|
impl Foo for W {
|
||||||
|
fn hello(&self) {
|
||||||
|
println!("hello!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bar for W {
|
||||||
|
fn world(&self) {
|
||||||
|
println!("world!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let w: dyn* Foo = W(0);
|
||||||
|
w.hello();
|
||||||
|
let w: dyn* Bar = w;
|
||||||
|
w.world();
|
||||||
|
}
|
|
@ -1,15 +1,16 @@
|
||||||
use rustc_hir::Expr;
|
use rustc_hir::Expr;
|
||||||
use rustc_hir_analysis::check::{
|
use rustc_hir_analysis::check::{cast, FnCtxt, Inherited};
|
||||||
cast::{self, CastCheckResult},
|
|
||||||
FnCtxt, Inherited,
|
|
||||||
};
|
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty::{cast::CastKind, Ty};
|
use rustc_middle::ty::{cast::CastKind, Ty};
|
||||||
use rustc_span::DUMMY_SP;
|
use rustc_span::DUMMY_SP;
|
||||||
|
|
||||||
// check if the component types of the transmuted collection and the result have different ABI,
|
// check if the component types of the transmuted collection and the result have different ABI,
|
||||||
// size or alignment
|
// size or alignment
|
||||||
pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
|
pub(super) fn is_layout_incompatible<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
from: Ty<'tcx>,
|
||||||
|
to: Ty<'tcx>,
|
||||||
|
) -> bool {
|
||||||
if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from)
|
if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from)
|
||||||
&& let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to)
|
&& let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to)
|
||||||
&& let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from))
|
&& let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from))
|
||||||
|
@ -32,7 +33,9 @@ pub(super) fn can_be_expressed_as_pointer_cast<'tcx>(
|
||||||
from_ty: Ty<'tcx>,
|
from_ty: Ty<'tcx>,
|
||||||
to_ty: Ty<'tcx>,
|
to_ty: Ty<'tcx>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
|
use CastKind::{
|
||||||
|
AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast,
|
||||||
|
};
|
||||||
matches!(
|
matches!(
|
||||||
check_cast(cx, e, from_ty, to_ty),
|
check_cast(cx, e, from_ty, to_ty),
|
||||||
Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast)
|
Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast)
|
||||||
|
@ -43,7 +46,12 @@ pub(super) fn can_be_expressed_as_pointer_cast<'tcx>(
|
||||||
/// the cast. In certain cases, including some invalid casts from array references
|
/// the cast. In certain cases, including some invalid casts from array references
|
||||||
/// to pointers, this may cause additional errors to be emitted and/or ICE error
|
/// to pointers, this may cause additional errors to be emitted and/or ICE error
|
||||||
/// messages. This function will panic if that occurs.
|
/// messages. This function will panic if that occurs.
|
||||||
fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option<CastKind> {
|
fn check_cast<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
e: &'tcx Expr<'_>,
|
||||||
|
from_ty: Ty<'tcx>,
|
||||||
|
to_ty: Ty<'tcx>,
|
||||||
|
) -> Option<CastKind> {
|
||||||
let hir_id = e.hir_id;
|
let hir_id = e.hir_id;
|
||||||
let local_def_id = hir_id.owner.def_id;
|
let local_def_id = hir_id.owner.def_id;
|
||||||
|
|
||||||
|
@ -51,12 +59,9 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>
|
||||||
let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id);
|
let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, hir_id);
|
||||||
|
|
||||||
// If we already have errors, we can't be sure we can pointer cast.
|
// If we already have errors, we can't be sure we can pointer cast.
|
||||||
assert!(
|
assert!(!fn_ctxt.errors_reported_since_creation(), "Newly created FnCtxt contained errors");
|
||||||
!fn_ctxt.errors_reported_since_creation(),
|
|
||||||
"Newly created FnCtxt contained errors"
|
|
||||||
);
|
|
||||||
|
|
||||||
if let CastCheckResult::Deferred(check) = cast::check_cast(
|
if let Ok(check) = cast::CastCheck::new(
|
||||||
&fn_ctxt, e, from_ty, to_ty,
|
&fn_ctxt, e, from_ty, to_ty,
|
||||||
// We won't show any error to the user, so we don't care what the span is here.
|
// We won't show any error to the user, so we don't care what the span is here.
|
||||||
DUMMY_SP, DUMMY_SP,
|
DUMMY_SP, DUMMY_SP,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue