Skip to content

Commit b15e2c1

Browse files
committed
Auto merge of rust-lang#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
2 parents 46244f3 + 8c7e836 commit b15e2c1

File tree

21 files changed

+233
-136
lines changed

21 files changed

+233
-136
lines changed

compiler/rustc_codegen_ssa/src/base.rs

+51-13
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use rustc_session::Session;
3838
use rustc_span::symbol::sym;
3939
use rustc_span::Symbol;
4040
use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType};
41-
use rustc_target::abi::{Align, VariantIdx};
41+
use rustc_target::abi::{Align, Size, VariantIdx};
4242

4343
use std::collections::BTreeSet;
4444
use std::convert::TryFrom;
@@ -150,7 +150,12 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
150150
(&ty::Array(_, len), &ty::Slice(_)) => {
151151
cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all()))
152152
}
153-
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
153+
(
154+
&ty::Dynamic(ref data_a, _, src_dyn_kind),
155+
&ty::Dynamic(ref data_b, _, target_dyn_kind),
156+
) => {
157+
assert_eq!(src_dyn_kind, target_dyn_kind);
158+
154159
let old_info =
155160
old_info.expect("unsized_info: missing old info for trait upcasting coercion");
156161
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>>(
166171
if let Some(entry_idx) = vptr_entry_idx {
167172
let ptr_ty = cx.type_i8p();
168173
let ptr_align = cx.tcx().data_layout.pointer_align.abi;
169-
let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
170-
cx.layout_of(cx.tcx().mk_mut_ptr(target)),
171-
1,
172-
true,
173-
);
174+
let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind);
174175
let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty));
175176
let gep = bx.inbounds_gep(
176177
ptr_ty,
@@ -186,18 +187,32 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
186187
old_info
187188
}
188189
}
189-
(_, &ty::Dynamic(ref data, ..)) => {
190-
let vtable_ptr_ty = cx.scalar_pair_element_backend_type(
191-
cx.layout_of(cx.tcx().mk_mut_ptr(target)),
192-
1,
193-
true,
194-
);
190+
(_, &ty::Dynamic(ref data, _, target_dyn_kind)) => {
191+
let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind);
195192
cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty)
196193
}
197194
_ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target),
198195
}
199196
}
200197

198+
// Returns the vtable pointer type of a `dyn` or `dyn*` type
199+
fn vtable_ptr_ty<'tcx, Cx: CodegenMethods<'tcx>>(
200+
cx: &Cx,
201+
target: Ty<'tcx>,
202+
kind: ty::DynKind,
203+
) -> <Cx as BackendTypes>::Type {
204+
cx.scalar_pair_element_backend_type(
205+
cx.layout_of(match kind {
206+
// vtable is the second field of `*mut dyn Trait`
207+
ty::Dyn => cx.tcx().mk_mut_ptr(target),
208+
// vtable is the second field of `dyn* Trait`
209+
ty::DynStar => target,
210+
}),
211+
1,
212+
true,
213+
)
214+
}
215+
201216
/// Coerces `src` to `dst_ty`. `src_ty` must be a pointer.
202217
pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
203218
bx: &mut Bx,
@@ -247,6 +262,29 @@ pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
247262
}
248263
}
249264

265+
/// Coerces `src` to `dst_ty` which is guaranteed to be a `dyn*` type.
266+
pub fn cast_to_dyn_star<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
267+
bx: &mut Bx,
268+
src: Bx::Value,
269+
src_ty_and_layout: TyAndLayout<'tcx>,
270+
dst_ty: Ty<'tcx>,
271+
old_info: Option<Bx::Value>,
272+
) -> (Bx::Value, Bx::Value) {
273+
debug!("cast_to_dyn_star: {:?} => {:?}", src_ty_and_layout.ty, dst_ty);
274+
assert!(
275+
matches!(dst_ty.kind(), ty::Dynamic(_, _, ty::DynStar)),
276+
"destination type must be a dyn*"
277+
);
278+
// FIXME(dyn-star): this is probably not the best way to check if this is
279+
// a pointer, and really we should ensure that the value is a suitable
280+
// pointer earlier in the compilation process.
281+
let src = match src_ty_and_layout.pointee_info_at(bx.cx(), Size::ZERO) {
282+
Some(_) => bx.ptrtoint(src, bx.cx().type_isize()),
283+
None => bx.bitcast(src, bx.type_isize()),
284+
};
285+
(src, unsized_info(bx, src_ty_and_layout.ty, dst_ty, old_info))
286+
}
287+
250288
/// Coerces `src`, which is a reference to a value of type `src_ty`,
251289
/// to a value of type `dst_ty`, and stores the result in `dst`.
252290
pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(

compiler/rustc_codegen_ssa/src/mir/rvalue.rs

+6-21
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use super::{FunctionCx, LocalRef};
44

55
use crate::base;
66
use crate::common::{self, IntPredicate};
7-
use crate::meth::get_vtable;
87
use crate::traits::*;
98
use crate::MemFlags;
109

@@ -14,7 +13,6 @@ use rustc_middle::ty::cast::{CastTy, IntTy};
1413
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
1514
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
1615
use rustc_span::source_map::{Span, DUMMY_SP};
17-
use rustc_target::abi::Size;
1816

1917
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
2018
#[instrument(level = "trace", skip(self, bx))]
@@ -274,27 +272,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
274272
}
275273
}
276274
mir::CastKind::DynStar => {
277-
let data = match operand.val {
275+
let (lldata, llextra) = match operand.val {
278276
OperandValue::Ref(_, _, _) => todo!(),
279-
OperandValue::Immediate(v) => v,
280-
OperandValue::Pair(_, _) => todo!(),
281-
};
282-
let trait_ref =
283-
if let ty::Dynamic(data, _, ty::DynStar) = cast.ty.kind() {
284-
data.principal()
285-
} else {
286-
bug!("Only valid to do a DynStar cast into a DynStar type")
287-
};
288-
let vtable = get_vtable(bx.cx(), source.ty(self.mir, bx.tcx()), trait_ref);
289-
let vtable = bx.pointercast(vtable, bx.cx().type_ptr_to(bx.cx().type_isize()));
290-
// FIXME(dyn-star): this is probably not the best way to check if this is
291-
// a pointer, and really we should ensure that the value is a suitable
292-
// pointer earlier in the compilation process.
293-
let data = match operand.layout.pointee_info_at(bx.cx(), Size::ZERO) {
294-
Some(_) => bx.ptrtoint(data, bx.cx().type_isize()),
295-
None => data,
277+
OperandValue::Immediate(v) => (v, None),
278+
OperandValue::Pair(v, l) => (v, Some(l)),
296279
};
297-
OperandValue::Pair(data, vtable)
280+
let (lldata, llextra) =
281+
base::cast_to_dyn_star(&mut bx, lldata, operand.layout, cast.ty, llextra);
282+
OperandValue::Pair(lldata, llextra)
298283
}
299284
mir::CastKind::Pointer(
300285
PointerCast::MutToConstPointer | PointerCast::ArrayToPointer,

compiler/rustc_hir_analysis/src/check/cast.rs

+3-76
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,12 @@ use crate::type_error_struct;
3535
use hir::def_id::LOCAL_CRATE;
3636
use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed};
3737
use rustc_hir as hir;
38-
use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode};
3938
use rustc_middle::mir::Mutability;
4039
use rustc_middle::ty::adjustment::AllowTwoPhase;
4140
use rustc_middle::ty::cast::{CastKind, CastTy};
4241
use rustc_middle::ty::error::TypeError;
4342
use rustc_middle::ty::subst::SubstsRef;
44-
use rustc_middle::ty::{self, Binder, Ty, TypeAndMut, TypeVisitable, VariantDef};
43+
use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable, VariantDef};
4544
use rustc_session::lint;
4645
use rustc_session::Session;
4746
use rustc_span::symbol::sym;
@@ -204,76 +203,8 @@ fn make_invalid_casting_error<'a, 'tcx>(
204203
)
205204
}
206205

207-
pub enum CastCheckResult<'tcx> {
208-
Ok,
209-
Deferred(CastCheck<'tcx>),
210-
Err(ErrorGuaranteed),
211-
}
212-
213-
pub fn check_cast<'tcx>(
214-
fcx: &FnCtxt<'_, 'tcx>,
215-
expr: &'tcx hir::Expr<'tcx>,
216-
expr_ty: Ty<'tcx>,
217-
cast_ty: Ty<'tcx>,
218-
cast_span: Span,
219-
span: Span,
220-
) -> CastCheckResult<'tcx> {
221-
if cast_ty.is_dyn_star() {
222-
check_dyn_star_cast(fcx, expr, expr_ty, cast_ty)
223-
} else {
224-
match CastCheck::new(fcx, expr, expr_ty, cast_ty, cast_span, span) {
225-
Ok(check) => CastCheckResult::Deferred(check),
226-
Err(e) => CastCheckResult::Err(e),
227-
}
228-
}
229-
}
230-
231-
fn check_dyn_star_cast<'tcx>(
232-
fcx: &FnCtxt<'_, 'tcx>,
233-
expr: &'tcx hir::Expr<'tcx>,
234-
expr_ty: Ty<'tcx>,
235-
cast_ty: Ty<'tcx>,
236-
) -> CastCheckResult<'tcx> {
237-
// Find the bounds in the dyn*. For eaxmple, if we have
238-
//
239-
// let x = 22_usize as dyn* (Clone + Debug + 'static)
240-
//
241-
// this would return `existential_predicates = [?Self: Clone, ?Self: Debug]` and `region = 'static`.
242-
let (existential_predicates, region) = match cast_ty.kind() {
243-
ty::Dynamic(predicates, region, ty::DynStar) => (predicates, region),
244-
_ => panic!("Invalid dyn* cast_ty"),
245-
};
246-
247-
let cause = ObligationCause::new(
248-
expr.span,
249-
fcx.body_id,
250-
// FIXME(dyn-star): Use a better obligation cause code
251-
ObligationCauseCode::MiscObligation,
252-
);
253-
254-
// For each existential predicate (e.g., `?Self: Clone`) substitute
255-
// the type of the expression (e.g., `usize` in our example above)
256-
// and then require that the resulting predicate (e.g., `usize: Clone`)
257-
// holds (it does).
258-
for existential_predicate in existential_predicates.iter() {
259-
let predicate = existential_predicate.with_self_ty(fcx.tcx, expr_ty);
260-
fcx.register_predicate(Obligation::new(cause.clone(), fcx.param_env, predicate));
261-
}
262-
263-
// Enforce the region bound `'static` (e.g., `usize: 'static`, in our example).
264-
fcx.register_predicate(Obligation::new(
265-
cause,
266-
fcx.param_env,
267-
fcx.tcx.mk_predicate(Binder::dummy(ty::PredicateKind::TypeOutlives(
268-
ty::OutlivesPredicate(expr_ty, *region),
269-
))),
270-
));
271-
272-
CastCheckResult::Ok
273-
}
274-
275206
impl<'a, 'tcx> CastCheck<'tcx> {
276-
fn new(
207+
pub fn new(
277208
fcx: &FnCtxt<'a, 'tcx>,
278209
expr: &'tcx hir::Expr<'tcx>,
279210
expr_ty: Ty<'tcx>,
@@ -934,11 +865,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
934865

935866
(Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast),
936867

937-
// FIXME(dyn-star): this needs more conditions...
938-
(_, DynStar) => Ok(CastKind::DynStarCast),
939-
940-
// FIXME(dyn-star): do we want to allow dyn* upcasting or other casts?
941-
(DynStar, _) => Err(CastError::IllegalCast),
868+
(_, DynStar) | (DynStar, _) => bug!("should be handled by `try_coerce`"),
942869
}
943870
}
944871

compiler/rustc_hir_analysis/src/check/coercion.rs

+60
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
216216
ty::Ref(r_b, _, mutbl_b) => {
217217
return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b);
218218
}
219+
ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star => {
220+
return self.coerce_dyn_star(a, b, predicates, region);
221+
}
219222
_ => {}
220223
}
221224

@@ -745,6 +748,63 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
745748
Ok(coercion)
746749
}
747750

751+
fn coerce_dyn_star(
752+
&self,
753+
a: Ty<'tcx>,
754+
b: Ty<'tcx>,
755+
predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
756+
b_region: ty::Region<'tcx>,
757+
) -> CoerceResult<'tcx> {
758+
if !self.tcx.features().dyn_star {
759+
return Err(TypeError::Mismatch);
760+
}
761+
762+
if let ty::Dynamic(a_data, _, _) = a.kind()
763+
&& let ty::Dynamic(b_data, _, _) = b.kind()
764+
{
765+
if a_data.principal_def_id() == b_data.principal_def_id() {
766+
return self.unify_and(a, b, |_| vec![]);
767+
} else if !self.tcx().features().trait_upcasting {
768+
let mut err = feature_err(
769+
&self.tcx.sess.parse_sess,
770+
sym::trait_upcasting,
771+
self.cause.span,
772+
&format!(
773+
"cannot cast `{a}` to `{b}`, trait upcasting coercion is experimental"
774+
),
775+
);
776+
err.emit();
777+
}
778+
}
779+
780+
// Check the obligations of the cast -- for example, when casting
781+
// `usize` to `dyn* Clone + 'static`:
782+
let obligations = predicates
783+
.iter()
784+
.map(|predicate| {
785+
// For each existential predicate (e.g., `?Self: Clone`) substitute
786+
// the type of the expression (e.g., `usize` in our example above)
787+
// and then require that the resulting predicate (e.g., `usize: Clone`)
788+
// holds (it does).
789+
let predicate = predicate.with_self_ty(self.tcx, a);
790+
Obligation::new(self.cause.clone(), self.param_env, predicate)
791+
})
792+
// Enforce the region bound (e.g., `usize: 'static`, in our example).
793+
.chain([Obligation::new(
794+
self.cause.clone(),
795+
self.param_env,
796+
self.tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::TypeOutlives(
797+
ty::OutlivesPredicate(a, b_region),
798+
))),
799+
)])
800+
.collect();
801+
802+
Ok(InferOk {
803+
value: (vec![Adjustment { kind: Adjust::DynStar, target: b }], b),
804+
obligations,
805+
})
806+
}
807+
748808
fn coerce_from_safe_fn<F, G>(
749809
&self,
750810
a: Ty<'tcx>,

compiler/rustc_hir_analysis/src/check/expr.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! See `mod.rs` for more context on type checking in general.
44
55
use crate::astconv::AstConv as _;
6-
use crate::check::cast::{self, CastCheckResult};
6+
use crate::check::cast;
77
use crate::check::coercion::CoerceMany;
88
use crate::check::fatally_break_rust;
99
use crate::check::method::SelfSource;
@@ -1270,17 +1270,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12701270
} else {
12711271
// Defer other checks until we're done type checking.
12721272
let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
1273-
match cast::check_cast(self, e, t_expr, t_cast, t.span, expr.span) {
1274-
CastCheckResult::Ok => t_cast,
1275-
CastCheckResult::Deferred(cast_check) => {
1273+
match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) {
1274+
Ok(cast_check) => {
12761275
debug!(
12771276
"check_expr_cast: deferring cast from {:?} to {:?}: {:?}",
12781277
t_cast, t_expr, cast_check,
12791278
);
12801279
deferred_cast_checks.push(cast_check);
12811280
t_cast
12821281
}
1283-
CastCheckResult::Err(ErrorGuaranteed { .. }) => self.tcx.ty_error(),
1282+
Err(_) => self.tcx.ty_error(),
12841283
}
12851284
}
12861285
}

compiler/rustc_hir_analysis/src/expr_use_visitor.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
583583
for adjustment in adjustments {
584584
debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment);
585585
match adjustment.kind {
586-
adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => {
586+
adjustment::Adjust::NeverToAny
587+
| adjustment::Adjust::Pointer(_)
588+
| adjustment::Adjust::DynStar => {
587589
// Creating a closure/fn-pointer or unsizing consumes
588590
// the input and stores it into the resulting rvalue.
589591
self.delegate_consume(&place_with_id, place_with_id.hir_id);

compiler/rustc_hir_analysis/src/mem_categorization.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,8 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
292292

293293
adjustment::Adjust::NeverToAny
294294
| adjustment::Adjust::Pointer(_)
295-
| adjustment::Adjust::Borrow(_) => {
295+
| adjustment::Adjust::Borrow(_)
296+
| adjustment::Adjust::DynStar => {
296297
// Result is an rvalue.
297298
Ok(self.cat_rvalue(expr.hir_id, expr.span, target))
298299
}

0 commit comments

Comments
 (0)