From 76386bd65e650b5289b142daa310a4b98230c3db Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 14 Sep 2022 23:20:03 +0000 Subject: [PATCH 1/4] Make dyn* cast into a coercion --- compiler/rustc_hir_analysis/src/check/cast.rs | 63 ++----------------- .../rustc_hir_analysis/src/check/coercion.rs | 50 +++++++++++++++ .../src/expr_use_visitor.rs | 4 +- .../src/mem_categorization.rs | 3 +- compiler/rustc_middle/src/mir/mod.rs | 4 +- compiler/rustc_middle/src/ty/adjustment.rs | 3 + compiler/rustc_mir_build/src/thir/cx/expr.rs | 1 + src/test/ui/dyn-star/const.rs | 2 +- src/test/ui/dyn-star/drop.rs | 2 +- src/test/ui/dyn-star/error.rs | 2 +- src/test/ui/dyn-star/error.stderr | 2 +- src/test/ui/dyn-star/make-dyn-star.rs | 2 +- src/test/ui/dyn-star/method.rs | 3 +- 13 files changed, 73 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/cast.rs b/compiler/rustc_hir_analysis/src/check/cast.rs index 01badc133c918..7587283014069 100644 --- a/compiler/rustc_hir_analysis/src/check/cast.rs +++ b/compiler/rustc_hir_analysis/src/check/cast.rs @@ -35,13 +35,12 @@ use crate::type_error_struct; use hir::def_id::LOCAL_CRATE; use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; 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::Session; use rustc_span::symbol::sym; @@ -218,58 +217,10 @@ pub fn check_cast<'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)); + match CastCheck::new(fcx, expr, expr_ty, cast_ty, cast_span, span) { + Ok(check) => CastCheckResult::Deferred(check), + Err(e) => CastCheckResult::Err(e), } - - // 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> { @@ -934,11 +885,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), - // FIXME(dyn-star): this needs more conditions... - (_, DynStar) => Ok(CastKind::DynStarCast), - - // FIXME(dyn-star): do we want to allow dyn* upcasting or other casts? - (DynStar, _) => Err(CastError::IllegalCast), + (_, DynStar) | (DynStar, _) => bug!("should be handled by `try_coerce`"), } } diff --git a/compiler/rustc_hir_analysis/src/check/coercion.rs b/compiler/rustc_hir_analysis/src/check/coercion.rs index cf87fe3c510a4..44e6438239774 100644 --- a/compiler/rustc_hir_analysis/src/check/coercion.rs +++ b/compiler/rustc_hir_analysis/src/check/coercion.rs @@ -216,6 +216,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::Ref(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,53 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Ok(coercion) } + fn coerce_dyn_star( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + predicates: &'tcx ty::List>>, + 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 { + bug!("dyn* trait upcasting is not supported"); + } + } + + 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 `'static` (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( &self, a: Ty<'tcx>, diff --git a/compiler/rustc_hir_analysis/src/expr_use_visitor.rs b/compiler/rustc_hir_analysis/src/expr_use_visitor.rs index cbc3769901d72..039c653e5bc64 100644 --- a/compiler/rustc_hir_analysis/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_analysis/src/expr_use_visitor.rs @@ -583,7 +583,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { for adjustment in adjustments { debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment); 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 // the input and stores it into the resulting rvalue. self.delegate_consume(&place_with_id, place_with_id.hir_id); diff --git a/compiler/rustc_hir_analysis/src/mem_categorization.rs b/compiler/rustc_hir_analysis/src/mem_categorization.rs index b62c5b5e07783..362f1c3430041 100644 --- a/compiler/rustc_hir_analysis/src/mem_categorization.rs +++ b/compiler/rustc_hir_analysis/src/mem_categorization.rs @@ -292,7 +292,8 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) - | adjustment::Adjust::Borrow(_) => { + | adjustment::Adjust::Borrow(_) + | adjustment::Adjust::DynStar => { // Result is an rvalue. Ok(self.cat_rvalue(expr.hir_id, expr.span, target)) } diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index d4258151ff3f3..c022ea9e5b470 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1824,7 +1824,6 @@ impl<'tcx> Rvalue<'tcx> { // While the model is undecided, we should be conservative. See // Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => false, - Rvalue::Cast(CastKind::DynStar, _, _) => false, Rvalue::Use(_) | Rvalue::CopyForDeref(_) @@ -1841,7 +1840,8 @@ impl<'tcx> Rvalue<'tcx> { | CastKind::FnPtrToPtr | CastKind::PtrToPtr | CastKind::Pointer(_) - | CastKind::PointerFromExposedAddress, + | CastKind::PointerFromExposedAddress + | CastKind::DynStar, _, _, ) diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index b809f176760d6..4682ac96b5297 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -101,6 +101,9 @@ pub enum Adjust<'tcx> { Borrow(AutoBorrow<'tcx>), Pointer(PointerCast), + + /// Cast into a dyn* object. + DynStar, } /// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)` diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index bcf2ed6817210..c7a7c3e3fa8ee 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -159,6 +159,7 @@ impl<'tcx> Cx<'tcx> { Adjust::Borrow(AutoBorrow::RawPtr(mutability)) => { 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 } diff --git a/src/test/ui/dyn-star/const.rs b/src/test/ui/dyn-star/const.rs index e49caf649f892..67e3ab7ab35f1 100644 --- a/src/test/ui/dyn-star/const.rs +++ b/src/test/ui/dyn-star/const.rs @@ -6,7 +6,7 @@ use std::fmt::Debug; fn make_dyn_star() { let i = 42usize; - let dyn_i: dyn* Debug = i as dyn* Debug; + let dyn_i: dyn* Debug = i; } fn main() { diff --git a/src/test/ui/dyn-star/drop.rs b/src/test/ui/dyn-star/drop.rs index 46b232f3dd399..1478498c0a9f3 100644 --- a/src/test/ui/dyn-star/drop.rs +++ b/src/test/ui/dyn-star/drop.rs @@ -15,7 +15,7 @@ impl Drop for Foo { } fn make_dyn_star(i: Foo) { - let _dyn_i: dyn* Debug = i as dyn* Debug; + let _dyn_i: dyn* Debug = i; } fn main() { diff --git a/src/test/ui/dyn-star/error.rs b/src/test/ui/dyn-star/error.rs index 33eff80a5fe70..d8261387efa9e 100644 --- a/src/test/ui/dyn-star/error.rs +++ b/src/test/ui/dyn-star/error.rs @@ -7,7 +7,7 @@ trait Foo {} fn make_dyn_star() { 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() {} diff --git a/src/test/ui/dyn-star/error.stderr b/src/test/ui/dyn-star/error.stderr index d612ccc630ea8..ae54b9ca707d0 100644 --- a/src/test/ui/dyn-star/error.stderr +++ b/src/test/ui/dyn-star/error.stderr @@ -1,7 +1,7 @@ error[E0277]: the trait bound `{integer}: Foo` is not satisfied --> $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}` error: aborting due to previous error diff --git a/src/test/ui/dyn-star/make-dyn-star.rs b/src/test/ui/dyn-star/make-dyn-star.rs index 708ffa25d6fee..4f9393abb30e2 100644 --- a/src/test/ui/dyn-star/make-dyn-star.rs +++ b/src/test/ui/dyn-star/make-dyn-star.rs @@ -5,7 +5,7 @@ use std::fmt::Debug; fn make_dyn_star(i: usize) { - let _dyn_i: dyn* Debug = i as dyn* Debug; + let _dyn_i: dyn* Debug = i; } fn main() { diff --git a/src/test/ui/dyn-star/method.rs b/src/test/ui/dyn-star/method.rs index d04958ca2aac9..5a77640f0d932 100644 --- a/src/test/ui/dyn-star/method.rs +++ b/src/test/ui/dyn-star/method.rs @@ -1,4 +1,5 @@ // run-pass + #![feature(dyn_star)] #![allow(incomplete_features)] @@ -17,7 +18,7 @@ fn invoke_dyn_star(i: dyn* Foo) -> 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) } From feb4244f54c4ecf828ba8e0ae3d8c23d80b64646 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 14 Sep 2022 23:28:14 +0000 Subject: [PATCH 2/4] Allow dyn* upcasting --- compiler/rustc_codegen_ssa/src/base.rs | 61 +++++++++++++++---- compiler/rustc_codegen_ssa/src/mir/rvalue.rs | 27 ++------ .../rustc_hir_analysis/src/check/coercion.rs | 12 +++- src/test/ui/dyn-star/upcast.rs | 33 ++++++++++ 4 files changed, 97 insertions(+), 36 deletions(-) create mode 100644 src/test/ui/dyn-star/upcast.rs diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index cb5436fd61a16..70e1fe62e4724 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -38,7 +38,7 @@ use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::Symbol; use rustc_span::{DebuggerVisualizerFile, DebuggerVisualizerType}; -use rustc_target::abi::{Align, VariantIdx}; +use rustc_target::abi::{Align, Size, VariantIdx}; use std::collections::BTreeSet; use std::convert::TryFrom; @@ -150,7 +150,12 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( (&ty::Array(_, len), &ty::Slice(_)) => { 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 = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); 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 { let ptr_ty = cx.type_i8p(); let ptr_align = cx.tcx().data_layout.pointer_align.abi; - let vtable_ptr_ty = cx.scalar_pair_element_backend_type( - cx.layout_of(cx.tcx().mk_mut_ptr(target)), - 1, - true, - ); + let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind); let llvtable = bx.pointercast(old_info, bx.type_ptr_to(ptr_ty)); let gep = bx.inbounds_gep( ptr_ty, @@ -186,18 +187,32 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( old_info } } - (_, &ty::Dynamic(ref data, ..)) => { - let vtable_ptr_ty = cx.scalar_pair_element_backend_type( - cx.layout_of(cx.tcx().mk_mut_ptr(target)), - 1, - true, - ); + (_, &ty::Dynamic(ref data, _, target_dyn_kind)) => { + let vtable_ptr_ty = vtable_ptr_ty(cx, target, target_dyn_kind); cx.const_ptrcast(meth::get_vtable(cx, source, data.principal()), vtable_ptr_ty) } _ => 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, +) -> ::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. pub fn unsize_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, @@ -247,6 +262,26 @@ 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) { + debug!("unsize_ptr: {:?} => {:?}", src_ty_and_layout.ty, dst_ty); + assert!(matches!(dst_ty.kind(), ty::Dynamic(_, _, ty::DynStar))); + // 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`, /// to a value of type `dst_ty`, and stores the result in `dst`. pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 4ed99df1e8169..4aab31fbfe7da 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -4,7 +4,6 @@ use super::{FunctionCx, LocalRef}; use crate::base; use crate::common::{self, IntPredicate}; -use crate::meth::get_vtable; use crate::traits::*; 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::{self, adjustment::PointerCast, Instance, Ty, TyCtxt}; use rustc_span::source_map::{Span, DUMMY_SP}; -use rustc_target::abi::Size; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, 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 => { - let data = match operand.val { + let (lldata, llextra) = match operand.val { OperandValue::Ref(_, _, _) => todo!(), - OperandValue::Immediate(v) => v, - OperandValue::Pair(_, _) => todo!(), - }; - let trait_ref = - if let ty::Dynamic(data, _, ty::DynStar) = cast.ty.kind() { - data.principal() - } 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::Immediate(v) => (v, None), + OperandValue::Pair(v, l) => (v, Some(l)), }; - OperandValue::Pair(data, vtable) + let (lldata, llextra) = + base::cast_to_dyn_star(&mut bx, lldata, operand.layout, cast.ty, llextra); + OperandValue::Pair(lldata, llextra) } mir::CastKind::Pointer( PointerCast::MutToConstPointer | PointerCast::ArrayToPointer, diff --git a/compiler/rustc_hir_analysis/src/check/coercion.rs b/compiler/rustc_hir_analysis/src/check/coercion.rs index 44e6438239774..a2bb249ba6e28 100644 --- a/compiler/rustc_hir_analysis/src/check/coercion.rs +++ b/compiler/rustc_hir_analysis/src/check/coercion.rs @@ -764,8 +764,16 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { { if a_data.principal_def_id() == b_data.principal_def_id() { return self.unify_and(a, b, |_| vec![]); - } else { - bug!("dyn* trait upcasting is not supported"); + } 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(); } } diff --git a/src/test/ui/dyn-star/upcast.rs b/src/test/ui/dyn-star/upcast.rs new file mode 100644 index 0000000000000..cee76ada7df3b --- /dev/null +++ b/src/test/ui/dyn-star/upcast.rs @@ -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(); +} From 0cb217d29b6cd9a67fc493a12a5754ed9d21671a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 14 Sep 2022 23:42:25 +0000 Subject: [PATCH 3/4] Remove CastCheckResult since it's unused --- compiler/rustc_hir_analysis/src/check/cast.rs | 22 +------------- compiler/rustc_hir_analysis/src/check/expr.rs | 9 +++--- .../clippy_lints/src/transmute/utils.rs | 29 +++++++++++-------- 3 files changed, 22 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/cast.rs b/compiler/rustc_hir_analysis/src/check/cast.rs index 7587283014069..51abdd2e059d7 100644 --- a/compiler/rustc_hir_analysis/src/check/cast.rs +++ b/compiler/rustc_hir_analysis/src/check/cast.rs @@ -203,28 +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> { - match CastCheck::new(fcx, expr, expr_ty, cast_ty, cast_span, span) { - Ok(check) => CastCheckResult::Deferred(check), - Err(e) => CastCheckResult::Err(e), - } -} - impl<'a, 'tcx> CastCheck<'tcx> { - fn new( + pub fn new( fcx: &FnCtxt<'a, 'tcx>, expr: &'tcx hir::Expr<'tcx>, expr_ty: Ty<'tcx>, diff --git a/compiler/rustc_hir_analysis/src/check/expr.rs b/compiler/rustc_hir_analysis/src/check/expr.rs index 34c257845971e..71c6da862c94b 100644 --- a/compiler/rustc_hir_analysis/src/check/expr.rs +++ b/compiler/rustc_hir_analysis/src/check/expr.rs @@ -3,7 +3,7 @@ //! See `mod.rs` for more context on type checking in general. use crate::astconv::AstConv as _; -use crate::check::cast::{self, CastCheckResult}; +use crate::check::cast; use crate::check::coercion::CoerceMany; use crate::check::fatally_break_rust; use crate::check::method::SelfSource; @@ -1270,9 +1270,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // Defer other checks until we're done type checking. 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) { - CastCheckResult::Ok => t_cast, - CastCheckResult::Deferred(cast_check) => { + match cast::CastCheck::new(self, e, t_expr, t_cast, t.span, expr.span) { + Ok(cast_check) => { debug!( "check_expr_cast: deferring cast from {:?} to {:?}: {:?}", t_cast, t_expr, cast_check, @@ -1280,7 +1279,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { deferred_cast_checks.push(cast_check); t_cast } - CastCheckResult::Err(ErrorGuaranteed { .. }) => self.tcx.ty_error(), + Err(_) => self.tcx.ty_error(), } } } diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs index b567d92230bb1..102f7541c8ce1 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs @@ -1,15 +1,16 @@ use rustc_hir::Expr; -use rustc_hir_analysis::check::{ - cast::{self, CastCheckResult}, - FnCtxt, Inherited, -}; +use rustc_hir_analysis::check::{cast, FnCtxt, Inherited}; use rustc_lint::LateContext; use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; // check if the component types of the transmuted collection and the result have different ABI, // 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) && 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)) @@ -32,7 +33,9 @@ pub(super) fn can_be_expressed_as_pointer_cast<'tcx>( from_ty: Ty<'tcx>, to_ty: Ty<'tcx>, ) -> bool { - use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast}; + use CastKind::{ + AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast, + }; matches!( check_cast(cx, e, from_ty, to_ty), 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 /// to pointers, this may cause additional errors to be emitted and/or ICE error /// 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 { +fn check_cast<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx Expr<'_>, + from_ty: Ty<'tcx>, + to_ty: Ty<'tcx>, +) -> Option { let hir_id = e.hir_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); // If we already have errors, we can't be sure we can pointer cast. - assert!( - !fn_ctxt.errors_reported_since_creation(), - "Newly created FnCtxt contained errors" - ); + assert!(!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, // We won't show any error to the user, so we don't care what the span is here. DUMMY_SP, DUMMY_SP, From 8c7e836abb62331ebb8350c7724c95241ef95a51 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 14 Oct 2022 04:55:07 +0000 Subject: [PATCH 4/4] Address nits, add test for implicit dyn-star coercion without feature gate --- compiler/rustc_codegen_ssa/src/base.rs | 7 +++++-- .../rustc_hir_analysis/src/check/coercion.rs | 4 +++- .../ui/dyn-star/auxiliary/dyn-star-foreign.rs | 10 ++++++++++ src/test/ui/dyn-star/make-dyn-star.rs | 5 +++++ src/test/ui/dyn-star/no-implicit-dyn-star.rs | 8 ++++++++ .../ui/dyn-star/no-implicit-dyn-star.stderr | 19 +++++++++++++++++++ 6 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/dyn-star/auxiliary/dyn-star-foreign.rs create mode 100644 src/test/ui/dyn-star/no-implicit-dyn-star.rs create mode 100644 src/test/ui/dyn-star/no-implicit-dyn-star.stderr diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 70e1fe62e4724..0ca6408aab910 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -270,8 +270,11 @@ pub fn cast_to_dyn_star<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( dst_ty: Ty<'tcx>, old_info: Option, ) -> (Bx::Value, Bx::Value) { - debug!("unsize_ptr: {:?} => {:?}", src_ty_and_layout.ty, dst_ty); - assert!(matches!(dst_ty.kind(), ty::Dynamic(_, _, ty::DynStar))); + 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. diff --git a/compiler/rustc_hir_analysis/src/check/coercion.rs b/compiler/rustc_hir_analysis/src/check/coercion.rs index a2bb249ba6e28..faa6c6d9356f4 100644 --- a/compiler/rustc_hir_analysis/src/check/coercion.rs +++ b/compiler/rustc_hir_analysis/src/check/coercion.rs @@ -777,6 +777,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { } } + // Check the obligations of the cast -- for example, when casting + // `usize` to `dyn* Clone + 'static`: let obligations = predicates .iter() .map(|predicate| { @@ -787,7 +789,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let predicate = predicate.with_self_ty(self.tcx, a); Obligation::new(self.cause.clone(), self.param_env, predicate) }) - // Enforce the region bound `'static` (e.g., `usize: 'static`, in our example). + // Enforce the region bound (e.g., `usize: 'static`, in our example). .chain([Obligation::new( self.cause.clone(), self.param_env, diff --git a/src/test/ui/dyn-star/auxiliary/dyn-star-foreign.rs b/src/test/ui/dyn-star/auxiliary/dyn-star-foreign.rs new file mode 100644 index 0000000000000..7673c79367833 --- /dev/null +++ b/src/test/ui/dyn-star/auxiliary/dyn-star-foreign.rs @@ -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); +} diff --git a/src/test/ui/dyn-star/make-dyn-star.rs b/src/test/ui/dyn-star/make-dyn-star.rs index 4f9393abb30e2..e5255a64ba119 100644 --- a/src/test/ui/dyn-star/make-dyn-star.rs +++ b/src/test/ui/dyn-star/make-dyn-star.rs @@ -8,6 +8,11 @@ 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; +} + fn main() { make_dyn_star(42); + make_dyn_star_explicit(42); } diff --git a/src/test/ui/dyn-star/no-implicit-dyn-star.rs b/src/test/ui/dyn-star/no-implicit-dyn-star.rs new file mode 100644 index 0000000000000..d9470e2841770 --- /dev/null +++ b/src/test/ui/dyn-star/no-implicit-dyn-star.rs @@ -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 +} diff --git a/src/test/ui/dyn-star/no-implicit-dyn-star.stderr b/src/test/ui/dyn-star/no-implicit-dyn-star.stderr new file mode 100644 index 0000000000000..e7c5918629bdc --- /dev/null +++ b/src/test/ui/dyn-star/no-implicit-dyn-star.stderr @@ -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`.