From 51e051a986b763db1dfe233b5a30b1a643af090d Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Fri, 22 Nov 2019 17:03:44 +0000 Subject: [PATCH 01/14] A few drive-by cosmetic changes. --- src/librustc_mir/interpret/traits.rs | 2 +- src/librustc_trait_selection/traits/util.rs | 12 ++++++------ src/librustc_typeck/astconv.rs | 10 ++++++---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 7edd787c986bd..4adac56023df9 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -108,7 +108,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { idx: u64, ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> { let ptr_size = self.pointer_size(); - // Skip over the 'drop_ptr', 'size', and 'align' fields. + // Skip over the `drop_ptr`, `size`, and `align` fields. let vtable_slot = vtable.ptr_offset(ptr_size * idx.checked_add(3).unwrap(), self)?; let vtable_slot = self .memory diff --git a/src/librustc_trait_selection/traits/util.rs b/src/librustc_trait_selection/traits/util.rs index eff73cf4a2d8a..49137cd065225 100644 --- a/src/librustc_trait_selection/traits/util.rs +++ b/src/librustc_trait_selection/traits/util.rs @@ -1,15 +1,15 @@ -use rustc_errors::DiagnosticBuilder; -use rustc_span::Span; -use smallvec::smallvec; -use smallvec::SmallVec; +use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; use rustc_data_structures::fx::FxHashSet; +use rustc_errors::DiagnosticBuilder; use rustc_hir::def_id::DefId; +pub use rustc_infer::traits::util::*; use rustc_middle::ty::subst::{GenericArg, Subst, SubstsRef}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; +use rustc_span::Span; -use super::{Normalized, Obligation, ObligationCause, PredicateObligation, SelectionContext}; -pub use rustc_infer::traits::util::*; +use smallvec::smallvec; +use smallvec::SmallVec; /////////////////////////////////////////////////////////////////////////// // `TraitAliasExpander` iterator diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 39fcd07564516..faa40a98cec6c 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -3,11 +3,10 @@ //! The main routine here is `ast_ty_to_ty()`; each use is parameterized by an //! instance of `AstConv`. -// ignore-tidy-filelength - use crate::collect::PlaceholderHirTyCollector; use crate::middle::resolve_lifetime as rl; use crate::require_c_abi_if_c_variadic; + use rustc_ast::ast; use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -64,8 +63,11 @@ pub trait AstConv<'tcx> { fn get_type_parameter_bounds(&self, span: Span, def_id: DefId) -> ty::GenericPredicates<'tcx>; /// Returns the lifetime to use when a lifetime is omitted (and not elided). - fn re_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) - -> Option>; + fn re_infer( + &self, + param: Option<&ty::GenericParamDef>, + span: Span, + ) -> Option>; /// Returns the type to use when a type is omitted. fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx>; From b276f7b47976ffc788d5ecd955c467004aeb7ca4 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Sat, 12 Oct 2019 08:57:46 -0400 Subject: [PATCH 02/14] Added `-Zno-verify-llvm-ir` compiler option. --- src/librustc_session/session.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs index 93a1315c6b59f..727e750829e8f 100644 --- a/src/librustc_session/session.rs +++ b/src/librustc_session/session.rs @@ -474,7 +474,9 @@ impl Session { self.opts.debugging_opts.asm_comments } pub fn verify_llvm_ir(&self) -> bool { - self.opts.debugging_opts.verify_llvm_ir || cfg!(always_verify_llvm_ir) + (self.opts.debugging_opts.verify_llvm_ir + || cfg!(always_verify_llvm_ir)) + && !self.opts.debugging_opts.no_verify_llvm_ir } pub fn borrowck_stats(&self) -> bool { self.opts.debugging_opts.borrowck_stats From 1a721a3239d7ba4cc6cadb076e3295fb527665bc Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Fri, 17 May 2019 21:51:53 +0100 Subject: [PATCH 03/14] Cleaned up code for writing MIR for vtable. --- src/librustc_mir/interpret/traits.rs | 43 ++++++++++++++-------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 4adac56023df9..30fe81b03f6cb 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -1,10 +1,10 @@ -use std::convert::TryFrom; +use super::{FnVal, InterpCx, Machine, Memory, MemoryKind}; use rustc_middle::mir::interpret::{InterpResult, Pointer, PointerArithmetic, Scalar}; use rustc_middle::ty::{self, Instance, Ty, TypeFoldable}; use rustc_target::abi::{Align, HasDataLayout, LayoutOf, Size}; -use super::{FnVal, InterpCx, Machine, MemoryKind}; +use std::convert::TryFrom; impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// Creates a dynamic vtable for the given type and vtable origin. This is used only for @@ -62,34 +62,35 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ); let tcx = &*self.tcx; + // Keep track of current position within vtable and write to there, offsetting position by + // one pointer size each time. + let mut cur_ptr = vtable; + let mut write_ptr = |memory: &mut Memory<'mir, 'tcx, M>, val| -> InterpResult<'_> { + let res = memory + .get_raw_mut(cur_ptr.alloc_id)? + .write_ptr_sized(tcx, cur_ptr, val)?; + cur_ptr = cur_ptr.offset(ptr_size, tcx)?; + Ok(res) + }; + let drop = Instance::resolve_drop_in_place(*tcx, ty); let drop = self.memory.create_fn_alloc(FnVal::Instance(drop)); // No need to do any alignment checks on the memory accesses below, because we know the - // allocation is correctly aligned as we created it above. Also we're only offsetting by - // multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`. - let vtable_alloc = self.memory.get_raw_mut(vtable.alloc_id)?; - vtable_alloc.write_ptr_sized(tcx, vtable, drop.into())?; - - let size_ptr = vtable.offset(ptr_size, tcx)?; - vtable_alloc.write_ptr_sized(tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?; - let align_ptr = vtable.offset(ptr_size * 2, tcx)?; - vtable_alloc.write_ptr_sized(tcx, align_ptr, Scalar::from_uint(align, ptr_size).into())?; + // allocation is correctly aligned, as we created it above. Also. we're only offsetting + // by multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`. + write_ptr(&mut self.memory, Scalar::Ptr(drop).into())?; + write_ptr(&mut self.memory, Scalar::from_uint(size, ptr_size).into())?; + write_ptr(&mut self.memory, Scalar::from_uint(align, ptr_size).into())?; - for (i, method) in methods.iter().enumerate() { + for method in methods.iter() { if let Some((def_id, substs)) = *method { - // resolve for vtable: insert shims where needed + // Resolve for vtable; insert shims where needed. let instance = ty::Instance::resolve_for_vtable(*tcx, self.param_env, def_id, substs) .ok_or_else(|| err_inval!(TooGeneric))?; let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); - // We cannot use `vtable_allic` as we are creating fn ptrs in this loop. - let method_ptr = vtable.offset(ptr_size * (3 + i as u64), tcx)?; - self.memory.get_raw_mut(vtable.alloc_id)?.write_ptr_sized( - tcx, - method_ptr, - fn_ptr.into(), - )?; + write_ptr(&mut self.memory, Scalar::Ptr(fn_ptr).into())?; } } @@ -179,7 +180,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if size >= self.tcx.data_layout().obj_size_bound() { throw_ub_format!( "invalid vtable: \ - size is bigger than largest supported object" + size is bigger than largest supported object" ); } Ok((Size::from_bytes(size), Align::from_bytes(align).unwrap())) From 21f2e9816fcc8d7f9e7e97494a499b85cb569d4d Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Sat, 7 Sep 2019 01:57:12 +0100 Subject: [PATCH 04/14] Implemented trait upcasting. --- src/librustc_codegen_ssa/base.rs | 53 ++++++++- src/librustc_codegen_ssa/meth.rs | 41 ++++--- src/librustc_middle/query/mod.rs | 5 +- src/librustc_middle/traits/mod.rs | 2 +- src/librustc_mir/interpret/traits.rs | 41 ++++--- src/librustc_mir/monomorphize/collector.rs | 3 +- src/librustc_trait_selection/traits/mod.rs | 16 +-- src/librustc_trait_selection/traits/select.rs | 106 ++++++++++-------- 8 files changed, 176 insertions(+), 91 deletions(-) diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs index c5b95905ea012..da194c5da006c 100644 --- a/src/librustc_codegen_ssa/base.rs +++ b/src/librustc_codegen_ssa/base.rs @@ -20,7 +20,7 @@ use crate::back::write::{ use crate::common::{IntPredicate, RealPredicate, TypeKind}; use crate::meth; use crate::mir; -use crate::mir::operand::OperandValue; +use crate::mir::operand::{OperandRef, OperandValue}; use crate::mir::place::PlaceRef; use crate::traits::*; use crate::{CachedModuleCodegen, CrateInfo, MemFlags, ModuleCodegen, ModuleKind}; @@ -221,6 +221,57 @@ pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } +/// Coerces `op` to `dst.ty`. `op` may be a fat pointer. +pub fn coerce_ptr_unsized<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + op: OperandRef<'tcx, Bx::Value>, + dst: TyLayout<'tcx>, +) -> OperandValue { + assert!(bx.cx().is_backend_scalar_pair(dst)); + let src = op.layout; + let (base, info) = match op.val { + OperandValue::Pair(base, info) => { + // Fat-ptr to fat-ptr unsize (e.g., `&'a fmt::Debug + Send` => `&'a fmt::Debug`). + // We always need to `pointercast` the base to ensure the types match up. + // In some cases, e.g., `&'a Bar` => `&'a Foo` where `Bar: Foo`, we also need to + // modify the info (i.e., the pointer to the vtable), by offsetting into the + // existing vtable. See the `get_vtable` fn in `meth.rs` for more details of vtable + // layout. + + let base = bx.pointercast( + base, + bx.cx().scalar_pair_element_backend_type(dst, 0, true), + ); + let info = match (&src.ty.kind, &dst.ty.kind) { + (&ty::Ref(_, a, _), + &ty::Ref(_, b, _)) | + (&ty::Ref(_, a, _), + &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | + (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), + &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { + unsized_info(bx, a, b, Some(info)) + } + (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { + assert_eq!(def_a, def_b); + unsized_info(bx, src.ty, dst.ty, Some(info)) + } + _ => bug!("coerce_ptr_unsized: called on bad types"), + }; + (base, info) + } + OperandValue::Immediate(base) => { + unsize_thin_ptr(bx, base, src.ty, dst.ty) + } + OperandValue::Ref(..) => { + bug!( + "coerce_ptr_unsized: unexpected by-ref operand {:?}", + op + ); + } + }; + OperandValue::Pair(base, 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/src/librustc_codegen_ssa/meth.rs b/src/librustc_codegen_ssa/meth.rs index 199dd8c7df42f..9cf4153c7b76e 100644 --- a/src/librustc_codegen_ssa/meth.rs +++ b/src/librustc_codegen_ssa/meth.rs @@ -85,17 +85,21 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( (&[]).iter() }; - let methods = methods.cloned().map(|opt_mth| { - opt_mth.map_or(nullptr, |(def_id, substs)| { - cx.get_fn_addr( - ty::Instance::resolve_for_vtable( - cx.tcx(), - ty::ParamEnv::reveal_all(), - def_id, - substs, + // Resolve instances for each method in the vtable. + // Preserve nested (per-supertrait) structure of list of methods. + let methods = methods.cloned().map(|trait_methods| { + trait_methods.into_iter().map(|opt_meth| { + opt_meth.map_or(nullptr, |(def_id, substs)| { + cx.get_fn_addr( + ty::Instance::resolve_for_vtable( + cx.tcx(), + ty::ParamEnv::reveal_all(), + def_id, + substs, + ) + .unwrap(), ) - .unwrap(), - ) + }) }) }); @@ -104,15 +108,20 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( // If you touch this code, be sure to also make the corresponding changes to // `get_vtable` in `rust_mir/interpret/traits.rs`. // ///////////////////////////////////////////////////////////////////////////////////////////// - let components: Vec<_> = [ + let metadata = [ cx.get_fn_addr(Instance::resolve_drop_in_place(cx.tcx(), ty)), cx.const_usize(layout.size.bytes()), cx.const_usize(layout.align.abi.bytes()), - ] - .iter() - .cloned() - .chain(methods) - .collect(); + ]; + let mut components: Vec<_> = methods + .flat_map(|trait_methods| { + // Insert copy of metadata before methods for the current (super)trait. + metadata.iter().cloned().chain(trait_methods) + }) + .collect(); + if components.is_empty() { + components.extend(&metadata); + } let vtable_const = cx.const_struct(&components, false); let align = cx.data_layout().pointer_align.abi; diff --git a/src/librustc_middle/query/mod.rs b/src/librustc_middle/query/mod.rs index b0c442381484c..d9903ca7de957 100644 --- a/src/librustc_middle/query/mod.rs +++ b/src/librustc_middle/query/mod.rs @@ -657,8 +657,9 @@ rustc_queries! { } Other { - query vtable_methods(key: ty::PolyTraitRef<'tcx>) - -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] { + query vtable_methods( + key: ty::PolyTraitRef<'tcx> + ) -> &'tcx [&'tcx [Option<(DefId, SubstsRef<'tcx>)>]] { desc { |tcx| "finding all methods for trait {}", tcx.def_path_str(key.def_id()) } } } diff --git a/src/librustc_middle/traits/mod.rs b/src/librustc_middle/traits/mod.rs index 3a05d577bfa7e..7d8c3f823ba3f 100644 --- a/src/librustc_middle/traits/mod.rs +++ b/src/librustc_middle/traits/mod.rs @@ -681,6 +681,6 @@ pub enum MethodViolationCode { /// e.g., `fn foo()` Generic, - /// the method's receiver (`self` argument) can't be dispatched on + /// The method's receiver (`self` argument) cannot be dispatched on. UndispatchableReceiver, } diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 30fe81b03f6cb..744792ccc0c55 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -56,7 +56,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // `get_vtable` in `rust_codegen_llvm/meth.rs`. // ///////////////////////////////////////////////////////////////////////////////////////// let vtable = self.memory.allocate( - ptr_size * u64::try_from(methods.len()).unwrap().checked_add(3).unwrap(), + // Compute size of vtable, including 3 entries per supertrait for (drop, size, align) + // metadata. + ptr_size * ( + methods + .iter() + .map(|l| u64::try_from(methods.len()).unwrap().checked_add(3).unwrap()) + .sum() + ), ptr_align, MemoryKind::Vtable, ); @@ -76,21 +83,23 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let drop = Instance::resolve_drop_in_place(*tcx, ty); let drop = self.memory.create_fn_alloc(FnVal::Instance(drop)); - // No need to do any alignment checks on the memory accesses below, because we know the - // allocation is correctly aligned, as we created it above. Also. we're only offsetting - // by multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`. - write_ptr(&mut self.memory, Scalar::Ptr(drop).into())?; - write_ptr(&mut self.memory, Scalar::from_uint(size, ptr_size).into())?; - write_ptr(&mut self.memory, Scalar::from_uint(align, ptr_size).into())?; - - for method in methods.iter() { - if let Some((def_id, substs)) = *method { - // Resolve for vtable; insert shims where needed. - let instance = - ty::Instance::resolve_for_vtable(*tcx, self.param_env, def_id, substs) - .ok_or_else(|| err_inval!(TooGeneric))?; - let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); - write_ptr(&mut self.memory, Scalar::Ptr(fn_ptr).into())?; + for trait_methods in methods.iter() { + // No need to do any alignment checks on the memory accesses below, because we know the + // allocation is correctly aligned, as we created it above. Also. we're only offsetting + // by multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`. + write_ptr(&mut self.memory, Scalar::Ptr(drop).into())?; + write_ptr(&mut self.memory, Scalar::from_uint(size, ptr_size).into())?; + write_ptr(&mut self.memory, Scalar::from_uint(align, ptr_size).into())?; + + for method in trait_methods.iter() { + if let Some((def_id, substs)) = *method { + // Resolve for vtable; insert shims where needed. + let instance = + ty::Instance::resolve_for_vtable(*tcx, self.param_env, def_id, substs) + .ok_or_else(|| err_inval!(TooGeneric))?; + let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); + write_ptr(&mut self.memory, Scalar::Ptr(fn_ptr).into())?; + } } } diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index dfcd2c3c93600..6232f7f02cb81 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -886,10 +886,9 @@ fn create_mono_items_for_vtable_methods<'tcx>( let poly_trait_ref = principal.with_self_ty(tcx, impl_ty); assert!(!poly_trait_ref.has_escaping_bound_vars()); - // Walk all methods of the trait, including those of its supertraits + // Walk all methods of the trait, including those of its supertraits. let methods = tcx.vtable_methods(poly_trait_ref); let methods = methods - .iter() .cloned() .filter_map(|method| method) .map(|(def_id, substs)| { diff --git a/src/librustc_trait_selection/traits/mod.rs b/src/librustc_trait_selection/traits/mod.rs index c5dbe81629550..6bda3495a79e3 100644 --- a/src/librustc_trait_selection/traits/mod.rs +++ b/src/librustc_trait_selection/traits/mod.rs @@ -462,24 +462,24 @@ fn substitute_normalize_and_test_predicates<'tcx>( result } -/// Given a trait `trait_ref`, iterates the vtable entries -/// that come from `trait_ref`, including its supertraits. +/// Given a trait `trait_ref`, iterates the vtable entries that come from `trait_ref`, including its +/// supertraits, and returns them per-trait. #[inline] // FIXME(#35870): avoid closures being unexported due to `impl Trait`. fn vtable_methods<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>, -) -> &'tcx [Option<(DefId, SubstsRef<'tcx>)>] { +) -> &'tcx [&'tcx [Option<(DefId, SubstsRef<'tcx>)>]] { debug!("vtable_methods({:?})", trait_ref); - tcx.arena.alloc_from_iter(supertraits(tcx, trait_ref).flat_map(move |trait_ref| { + tcx.arena.alloc_from_iter(supertraits(tcx, trait_ref).map(move |trait_ref| { let trait_methods = tcx .associated_items(trait_ref.def_id()) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Fn); - // Now list each method's DefId and InternalSubsts (for within its trait). - // If the method can never be called from this object, produce None. - trait_methods.map(move |trait_method| { + // Now, list each method's `DefId` and `InternalSubsts` (for within its trait). + // If the method can never be called from this object, produce `None`. + &*tcx.arena.alloc_from_iter(trait_methods.map(move |trait_method| { debug!("vtable_methods: trait_method={:?}", trait_method); let def_id = trait_method.def_id; @@ -516,7 +516,7 @@ fn vtable_methods<'tcx>( } Some((def_id, substs)) - }) + })) })) } diff --git a/src/librustc_trait_selection/traits/select.rs b/src/librustc_trait_selection/traits/select.rs index dfbb07424487d..f55a278626d53 100644 --- a/src/librustc_trait_selection/traits/select.rs +++ b/src/librustc_trait_selection/traits/select.rs @@ -1855,24 +1855,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("assemble_candidates_for_unsizing(source={:?}, target={:?})", source, target); let may_apply = match (&source.kind, &target.kind) { - // Trait+Kx+'a -> Trait+Ky+'b (upcasts). + // `dyn TraitA + Kx + 'a` -> `dyn TraitB + Ky + 'b` (upcasts) (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => { - // Upcasts permit two things: + // Upcasts permit three things: // - // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` - // 2. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b` + // 1. Moving from a subtrait to a supertrait, e.g., `Foo` to `Bar` where `Foo: Bar`. + // 2. Dropping auto traits, e.g., `Foo + Send` to `Foo`. + // 3. Tightening the region bound, e.g., `Foo + 'a` to `Foo + 'b` if `'a: 'b`. // - // Note that neither of these changes requires any - // change at runtime. Eventually this will be - // generalized. - // - // We always upcast when we can because of reason - // #2 (region bounds). - data_a.principal_def_id() == data_b.principal_def_id() - && data_b - .auto_traits() - // All of a's auto traits need to be in b's auto traits. - .all(|b| data_a.auto_traits().any(|a| a == b)) + // We always upcast when we can because of reason 3 (region bounds). + + // All of a's auto traits need to be in b's auto traits. + data_b.auto_traits() + .all(|b| data_a.auto_traits().any(|a| a == b)) } // `T` -> `Trait` @@ -2710,7 +2705,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // we pass over, we sum up the set of number of vtable // entries, so that we can compute the offset for the selected // trait. - vtable_base = nonmatching.map(|t| super::util::count_own_vtable_entries(tcx, t)).sum(); + vtable_base = nonmatching + // Skip 3 entries in vtable per supertrait for `(drop, size, align)` metadata. + .map(|trait_ref| + u64::try_from(tcx.count_own_vtable_entries(trait_ref)).unwrap().checked_add(3).unwrap() + ) + .sum(); } VtableObjectData { upcast_trait_ref: upcast_trait_ref.unwrap(), vtable_base, nested } @@ -2925,10 +2925,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { debug!("confirm_builtin_unsize_candidate(source={:?}, target={:?})", source, target); + let cause = ObligationCause::new( + obligation.cause.span, + obligation.cause.body_id, + ObjectCastObligation(target), + ); + + let predicate_to_obligation = |predicate| { + Obligation::with_depth( + cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + predicate, + ) + }; + let mut nested = vec![]; match (&source.kind, &target.kind) { - // Trait+Kx+'a -> Trait+Ky+'b (upcasts). - (&ty::Dynamic(ref data_a, r_a), &ty::Dynamic(ref data_b, r_b)) => { + // `dyn TraitA + Kx + 'a` -> `dyn TraitB + Ky + 'b` (upcasts) + (&ty::Dynamic(ref data_a, region_a), &ty::Dynamic(ref data_b, region_b)) => { // See `assemble_candidates_for_unsizing` for more info. let existential_predicates = data_a.map_bound(|data_a| { let iter = data_a @@ -2939,8 +2954,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { .chain(data_b.auto_traits().map(ty::ExistentialPredicate::AutoTrait)); tcx.mk_existential_predicates(iter) }); - let source_trait = tcx.mk_dynamic(existential_predicates, r_b); + let source_ty = tcx.mk_dynamic(existential_predicates, region_b); + /* // Require that the traits involved in this upcast are **equal**; // only the **lifetime bound** is changed. // @@ -2960,21 +2976,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let InferOk { obligations, .. } = self .infcx .at(&obligation.cause, obligation.param_env) - .eq(target, source_trait) // FIXME -- see below + .eq(target, source_ty) // FIXME -- see above .map_err(|_| Unimplemented)?; nested.extend(obligations); + */ - // Register one obligation for 'a: 'b. - let cause = ObligationCause::new( - obligation.cause.span, - obligation.cause.body_id, - ObjectCastObligation(target), + // Register obligations for `dyn TraitA1 [TraitA2...]: TraitB1 [TraitB2...]`. + nested.extend( + data_b.iter() + // HACK(alexreg | nikomatsakis): we handle auto traits specially here + // because of cases like like `dyn Foo + Send + 'a` -> + // `dyn Foo + Send + 'b`, which requires proving the obligation + // `dyn Foo + Send: Send`. This is unfortunately ambiguous under the + // current trait solver model: it holds both because `Send` is a supertrait + // of `Foo + Send` and because there's an automatic impl of `Send` for the + // trait object. + .filter(|predicate| { + match predicate.skip_binder() { + ty::ExistentialPredicate::AutoTrait(did) => + !data_a.auto_traits().any(|did_a| did_a == *did), + _ => true, + } + }) + .map(|predicate| + predicate_to_obligation(predicate.with_self_ty(tcx, source_ty)) + ), ); - let outlives = ty::OutlivesPredicate(r_a, r_b); - nested.push(Obligation::with_depth( - cause, - obligation.recursion_depth + 1, - obligation.param_env, + + // Register an obligation for `'a: 'b`. + let outlives = ty::OutlivesPredicate(region_a, region_b); + nested.push(predicate_to_obligation( ty::Binder::bind(outlives).to_predicate(), )); } @@ -2986,21 +3017,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { return Err(TraitNotObjectSafe(did)); } - let cause = ObligationCause::new( - obligation.cause.span, - obligation.cause.body_id, - ObjectCastObligation(target), - ); - - let predicate_to_obligation = |predicate| { - Obligation::with_depth( - cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - predicate, - ) - }; - // Create obligations: // - Casting `T` to `Trait` // - For all the various builtin bounds attached to the object cast. (In other From af3d69c3c567268e7af5adf6f3bfe3facc010bd8 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Thu, 31 Oct 2019 02:23:32 +0000 Subject: [PATCH 05/14] Implemented codegen. --- src/librustc_codegen_ssa/base.rs | 104 +++++++++++++++++-------- src/librustc_codegen_ssa/mir/rvalue.rs | 30 +------ src/librustc_middle/ty/sty.rs | 8 ++ 3 files changed, 79 insertions(+), 63 deletions(-) diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs index da194c5da006c..657a0ed0a56dd 100644 --- a/src/librustc_codegen_ssa/base.rs +++ b/src/librustc_codegen_ssa/base.rs @@ -144,29 +144,54 @@ pub fn compare_simd_types<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( /// /// The `old_info` argument is a bit odd. It is intended for use in an upcast, /// where the new vtable for an object will be derived from the old one. -pub fn unsized_info<'tcx, Cx: CodegenMethods<'tcx>>( - cx: &Cx, +pub fn unsized_info<'tcx, 'a, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, source: Ty<'tcx>, target: Ty<'tcx>, - old_info: Option, -) -> Cx::Value { + old_info: Option, +) -> Bx::Value { + let tcx = bx.tcx(); let (source, target) = - cx.tcx().struct_lockstep_tails_erasing_lifetimes(source, target, cx.param_env()); + tcx.struct_lockstep_tails_erasing_lifetimes(source, target, bx.param_env()); match (&source.kind, &target.kind) { (&ty::Array(_, len), &ty::Slice(_)) => { - cx.const_usize(len.eval_usize(cx.tcx(), ty::ParamEnv::reveal_all())) + bx.cx().const_usize(len.eval_usize(tcx, ty::ParamEnv::reveal_all())) } - (&ty::Dynamic(..), &ty::Dynamic(..)) => { - // For now, upcasts are limited to changes in marker - // traits, and hence never actually require an actual - // change to the vtable. - old_info.expect("unsized_info: missing old info for trait upcast") + (&ty::Dynamic(..), &ty::Dynamic(ref target_data, ..)) => { + // Trait upcast + + let source_ptr = old_info.expect("unsized_info: missing old info for trait upcast"); + let target_trait_ref = target_data.principal() + .expect("target trait object of upcast does not have principal"); + + let trait_ref = target_trait_ref.with_self_ty(tcx, source); + let vtable = tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)); + let offset = match vtable { + Vtable::VtableObject(ref data) => data.vtable_base, + // HACK(alexreg): dubious solution to ICE in `manual-self-impl-for-unsafe-obj` test. + Vtable::VtableImpl(_) => 0, + _ => bug!("unsized_info: unexpected vtable kind {:?}", vtable), + }; + + let vtable_layout = bx.cx().layout_of(tcx.mk_mut_ptr(source)); + let source_ptr = bx.pointercast( + source_ptr, + bx.cx().scalar_pair_element_backend_type(vtable_layout, 1, true), + ); + + let target_ptr = bx.struct_gep(source_ptr, offset as u64); + + let vtable_layout = bx.cx().layout_of(tcx.mk_mut_ptr(target)); + bx.pointercast( + target_ptr, + bx.cx().scalar_pair_element_backend_type(vtable_layout, 1, true), + ) } (_, &ty::Dynamic(ref data, ..)) => { - let vtable_ptr = cx.layout_of(cx.tcx().mk_mut_ptr(target)).field(cx, FAT_PTR_EXTRA); - cx.const_ptrcast( - meth::get_vtable(cx, source, data.principal()), - cx.backend_type(vtable_ptr), + let vtable_layout = bx.cx().layout_of(tcx.mk_mut_ptr(target)); + bx.pointercast( + meth::get_vtable(bx.cx(), source, data.principal()), + bx.cx().scalar_pair_element_backend_type(vtable_layout, 1, true), ) } _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", source, target), @@ -186,7 +211,7 @@ pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( | (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { assert!(bx.cx().type_is_sized(a)); let ptr_ty = bx.cx().type_ptr_to(bx.cx().backend_type(bx.cx().layout_of(b))); - (bx.pointercast(src, ptr_ty), unsized_info(bx.cx(), a, b, None)) + (bx.pointercast(src, ptr_ty), unsized_info(bx, a, b, None)) } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); @@ -253,11 +278,35 @@ pub fn coerce_ptr_unsized<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); - unsized_info(bx, src.ty, dst.ty, Some(info)) + + let mut result = None; + for i in 0..src.fields.count() { + let src_f = src.field(bx.cx(), i); + assert_eq!(src.fields.offset(i).bytes(), 0); + assert_eq!(dst.fields.offset(i).bytes(), 0); + if src_f.is_zst() { + continue; + } + assert_eq!(src.size, src_f.size); + + let dst_f = dst.field(bx.cx(), i); + assert_eq!(result, None); + result = if src_f == dst_f { + Some(info) + } else { + let f = op.extract_field(bx, i); + return coerce_ptr_unsized(bx, f, dst_f); + }; + } + result.unwrap() } _ => bug!("coerce_ptr_unsized: called on bad types"), }; - (base, info) + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + // FIXME(eddyb) move these out of this `match` arm, so they're always + // applied, uniformly, no matter the source/destination types. + (bx.bitcast(base, bx.cx().scalar_pair_element_backend_type(dst, 0, true)), + bx.bitcast(info, bx.cx().scalar_pair_element_backend_type(dst, 1, true))) } OperandValue::Immediate(base) => { unsize_thin_ptr(bx, base, src.ty, dst.ty) @@ -283,23 +332,10 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let dst_ty = dst.layout.ty; match (&src_ty.kind, &dst_ty.kind) { (&ty::Ref(..), &ty::Ref(..) | &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => { - let (base, info) = match bx.load_operand(src).val { - OperandValue::Pair(base, info) => { - // fat-ptr to fat-ptr unsize preserves the vtable - // i.e., &'a fmt::Debug+Send => &'a fmt::Debug - // So we need to pointercast the base to ensure - // the types match up. - // FIXME(eddyb) use `scalar_pair_element_backend_type` here, - // like `unsize_thin_ptr` does. - let thin_ptr = dst.layout.field(bx.cx(), FAT_PTR_ADDR); - (bx.pointercast(base, bx.cx().backend_type(thin_ptr)), info) - } - OperandValue::Immediate(base) => unsize_thin_ptr(bx, base, src_ty, dst_ty), - OperandValue::Ref(..) => bug!(), - }; - OperandValue::Pair(base, info).store(bx, dst); + let src_op = bx.load_operand(src); + let dst_op = coerce_ptr_unsized(bx, src_op, dst.layout); + dst_op.store(bx, dst);s } - (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); diff --git a/src/librustc_codegen_ssa/mir/rvalue.rs b/src/librustc_codegen_ssa/mir/rvalue.rs index 19a0138d9cb6d..12a7cc71dce73 100644 --- a/src/librustc_codegen_ssa/mir/rvalue.rs +++ b/src/librustc_codegen_ssa/mir/rvalue.rs @@ -224,35 +224,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { operand.val } mir::CastKind::Pointer(PointerCast::Unsize) => { - assert!(bx.cx().is_backend_scalar_pair(cast)); - match operand.val { - OperandValue::Pair(lldata, llextra) => { - // unsize from a fat pointer -- this is a - // "trait-object-to-supertrait" coercion, for - // example, `&'a fmt::Debug + Send => &'a fmt::Debug`. - - // HACK(eddyb) have to bitcast pointers - // until LLVM removes pointee types. - let lldata = bx.pointercast( - lldata, - bx.cx().scalar_pair_element_backend_type(cast, 0, true), - ); - OperandValue::Pair(lldata, llextra) - } - OperandValue::Immediate(lldata) => { - // "standard" unsize - let (lldata, llextra) = base::unsize_thin_ptr( - &mut bx, - lldata, - operand.layout.ty, - cast.ty, - ); - OperandValue::Pair(lldata, llextra) - } - OperandValue::Ref(..) => { - bug!("by-ref operand {:?} in `codegen_rvalue_operand`", operand); - } - } + base::coerce_ptr_unsized(&mut bx, operand, cast) } mir::CastKind::Pointer(PointerCast::MutToConstPointer) | mir::CastKind::Misc diff --git a/src/librustc_middle/ty/sty.rs b/src/librustc_middle/ty/sty.rs index ac5477edcc3c0..cdba49e2f8f11 100644 --- a/src/librustc_middle/ty/sty.rs +++ b/src/librustc_middle/ty/sty.rs @@ -1881,6 +1881,14 @@ impl<'tcx> TyS<'tcx> { } } + /// Panics if called on any type other than `PhantomData`. + pub fn phantom_ty(&self) -> Ty<'tcx> { + match self.kind { + Adt(def, substs) if def.is_phantom_data() => substs.type_at(0), + _ => bug!("`phantom_ty` is called on non-phantom-data type {:?}", self), + } + } + /// A scalar type is one that denotes an atomic datum, with no sub-components. /// (A RawPtr is scalar because it represents a non-managed pointer, so its /// contents are abstract to rustc.) From 92d567620f055b67e957948acf70294f49450486 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Tue, 5 Nov 2019 02:36:22 +0000 Subject: [PATCH 06/14] Added diagnostic to suggest enabling `trait_upcasting` feature. --- .../infer/error_reporting/mod.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index a8d6c01785ff9..5f9f00482d747 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -1589,6 +1589,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } if let Some(exp_found) = exp_found { self.suggest_as_ref_where_appropriate(span, &exp_found, diag); + + if let &TypeError::Traits(ref exp_found_traits) = terr { + self.note_enable_trait_upcasting_where_appropriate( + &exp_found_traits, diag + ); + } } // In some (most?) cases cause.body_id points to actual body, but in some cases @@ -1668,6 +1674,22 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + /// When encountering a coercion to a supertrait object is attempted, + /// note that this is permitted with the "trait upcasting" feature enabled. + fn note_enable_trait_upcasting_where_appropriate( + &self, + exp_found: &ty::error::ExpectedFound, + diag: &mut DiagnosticBuilder<'tcx>, + ) { + use rustc::ty::{Binder, TraitRef}; + + let trait_ref = Binder::bind(TraitRef::identity(self.tcx, exp_found.found)); + let supertraits = crate::traits::supertraits(self.tcx, trait_ref); + if supertraits.into_iter().any(|trait_ref| trait_ref.def_id() == exp_found.expected) { + diag.note("enable the `trait_upcasting` feature to permit upcasting of trait objects"); + } + } + pub fn report_and_explain_type_error( &self, trace: TypeTrace<'tcx>, From 2ee0694cb65a1ecf69893da577a7c1fe0417d8fd Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Thu, 31 Oct 2019 02:52:05 +0000 Subject: [PATCH 07/14] Added feature gate. --- src/librustc_feature/active.rs | 12 ++- .../infer/error_reporting/mod.rs | 2 +- src/librustc_span/symbol.rs | 1 + src/librustc_trait_selection/traits/select.rs | 91 +++++++++---------- .../feature-gate-trait_upcasting.rs | 16 ++++ .../feature-gate-trait_upcasting.stderr | 13 +++ 6 files changed, 84 insertions(+), 51 deletions(-) create mode 100644 src/test/ui/feature-gates/feature-gate-trait_upcasting.rs create mode 100644 src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs index a1dd7a5ca5225..52ae14c38bf17 100644 --- a/src/librustc_feature/active.rs +++ b/src/librustc_feature/active.rs @@ -497,7 +497,7 @@ declare_features! ( /// Allows the use of raw-dylibs (RFC 2627). (active, raw_dylib, "1.40.0", Some(58713), None), - /// Allows `#[track_caller]` to be used which provides + /// Allows `#[track_caller]` to be used, which provides /// accurate caller location reporting during panic (RFC 2091). (active, track_caller, "1.40.0", Some(47809), None), @@ -524,7 +524,7 @@ declare_features! ( /// Allows the use of `if` and `match` in constants. (active, const_if_match, "1.41.0", Some(49146), None), - /// Allows the use of `#[cfg(sanitize = "option")]`; set when -Zsanitizer is used. + /// Allows the use of `#[cfg(sanitize = "option")]`; set when `-Zsanitizer` is used. (active, cfg_sanitize, "1.41.0", Some(39699), None), /// Allows using `..X`, `..=X`, `...X`, and `X..` as a pattern. @@ -553,10 +553,10 @@ declare_features! ( /// Allows the use of `no_sanitize` attribute. (active, no_sanitize, "1.42.0", Some(39699), None), - // Allows limiting the evaluation steps of const expressions + /// Allows limiting the evaluation steps of const expressions. (active, const_eval_limit, "1.43.0", Some(67217), None), - /// Allow negative trait implementations. + /// Allows negative trait implementations. (active, negative_impls, "1.44.0", Some(68318), None), /// Allows the use of `#[target_feature]` on safe functions. @@ -565,6 +565,10 @@ declare_features! ( /// Allow conditional compilation depending on rust version (active, cfg_version, "1.45.0", Some(64796), None), + /// Allows upcasting trait objects via supertraits. + /// Trait upcasting is casting, e.g., `dyn Foo -> dyn Bar` where `Foo: Bar`. + (active, trait_upcasting, "1.45.0", Some(65991), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index 5f9f00482d747..dd72b37360ce2 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -1686,7 +1686,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let trait_ref = Binder::bind(TraitRef::identity(self.tcx, exp_found.found)); let supertraits = crate::traits::supertraits(self.tcx, trait_ref); if supertraits.into_iter().any(|trait_ref| trait_ref.def_id() == exp_found.expected) { - diag.note("enable the `trait_upcasting` feature to permit upcasting of trait objects"); + diag.note("add `#![feature(trait_upcasting)]` to the crate attributes to enable"); } } diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs index a61647bfd655f..424dd7580b697 100644 --- a/src/librustc_span/symbol.rs +++ b/src/librustc_span/symbol.rs @@ -751,6 +751,7 @@ symbols! { transparent, transparent_enums, transparent_unions, + trait_upcasting, trivial_bounds, Try, try_blocks, diff --git a/src/librustc_trait_selection/traits/select.rs b/src/librustc_trait_selection/traits/select.rs index f55a278626d53..8886006c55758 100644 --- a/src/librustc_trait_selection/traits/select.rs +++ b/src/librustc_trait_selection/traits/select.rs @@ -2956,52 +2956,51 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }); let source_ty = tcx.mk_dynamic(existential_predicates, region_b); - /* - // Require that the traits involved in this upcast are **equal**; - // only the **lifetime bound** is changed. - // - // FIXME: This condition is arguably too strong -- it would - // suffice for the source trait to be a *subtype* of the target - // trait. In particular, changing from something like - // `for<'a, 'b> Foo<'a, 'b>` to `for<'a> Foo<'a, 'a>` should be - // permitted. And, indeed, in the in commit - // 904a0bde93f0348f69914ee90b1f8b6e4e0d7cbc, this - // condition was loosened. However, when the leak check was - // added back, using subtype here actually guides the coercion - // code in such a way that it accepts `old-lub-glb-object.rs`. - // This is probably a good thing, but I've modified this to `.eq` - // because I want to continue rejecting that test (as we have - // done for quite some time) before we are firmly comfortable - // with what our behavior should be there. -nikomatsakis - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .eq(target, source_ty) // FIXME -- see above - .map_err(|_| Unimplemented)?; - nested.extend(obligations); - */ - - // Register obligations for `dyn TraitA1 [TraitA2...]: TraitB1 [TraitB2...]`. - nested.extend( - data_b.iter() - // HACK(alexreg | nikomatsakis): we handle auto traits specially here - // because of cases like like `dyn Foo + Send + 'a` -> - // `dyn Foo + Send + 'b`, which requires proving the obligation - // `dyn Foo + Send: Send`. This is unfortunately ambiguous under the - // current trait solver model: it holds both because `Send` is a supertrait - // of `Foo + Send` and because there's an automatic impl of `Send` for the - // trait object. - .filter(|predicate| { - match predicate.skip_binder() { - ty::ExistentialPredicate::AutoTrait(did) => - !data_a.auto_traits().any(|did_a| did_a == *did), - _ => true, - } - }) - .map(|predicate| - predicate_to_obligation(predicate.with_self_ty(tcx, source_ty)) - ), - ); + if tcx.features().trait_upcasting { + // Register obligations for `dyn TraitA1 [TraitA2...]: TraitB1 [TraitB2...]`. + nested.extend( + data_b.iter() + // HACK(alexreg | nikomatsakis): we handle auto traits specially here + // because of cases like like `dyn Foo + Send + 'a` -> + // `dyn Foo + Send + 'b`, which requires proving the obligation + // `dyn Foo + Send: Send`. This is unfortunately ambiguous under the + // current trait solver model: it holds both because `Send` is a + // supertrait of `Foo + Send` and because there's an automatic impl of + // `Send` for the trait object. + .filter(|predicate| { + match predicate.skip_binder() { + ty::ExistentialPredicate::AutoTrait(did) => + !data_a.auto_traits().any(|did_a| did_a == *did), + _ => true, + } + }) + .map(|predicate| + predicate_to_obligation(predicate.with_self_ty(tcx, source_ty)) + ), + ); + } else { + // Require that the traits involved in this upcast are **equal**; + // only the **lifetime bound** is changed. + // + // FIXME: This condition is arguably too strong -- it would + // suffice for the source trait to be a *subtype* of the target + // trait. In particular, changing from something like + // `for<'a, 'b> Foo<'a, 'b>` to `for<'a> Foo<'a, 'a>` should be + // permitted. And, indeed, in the in commit + // 904a0bde93f0348f69914ee90b1f8b6e4e0d7cbc, this + // condition was loosened. However, when the leak check was + // added back, using subtype here actually guides the coercion + // code in such a way that it accepts `old-lub-glb-object.rs`. + // This is probably a good thing, but I've modified this to `.eq` + // because I want to continue rejecting that test (as we have + // done for quite some time) before we are firmly comfortable + // with what our behavior should be there. -nikomatsakis + let InferOk { obligations, .. } = self.infcx + .at(&obligation.cause, obligation.param_env) + .eq(target, source_ty) // FIXME: see above. + .map_err(|_| Unimplemented)?; + nested.extend(obligations); + } // Register an obligation for `'a: 'b`. let outlives = ty::OutlivesPredicate(region_a, region_b); diff --git a/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs b/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs new file mode 100644 index 0000000000000..f0facfd97fb7a --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs @@ -0,0 +1,16 @@ +trait Foo {} + +trait Bar: Foo {} + +impl Foo for () {} + +impl Bar for () {} + +fn main() { + let bar: &dyn Bar = &(); + let foo: &dyn Foo = bar; + //~^ ERROR mismatched types [E0308] + //~| NOTE expected type `&dyn Foo` + //~| NOTE expected trait `Foo`, found trait `Bar` + //~| NOTE add `#![feature(trait_upcasting)]` to the crate attributes to enable +} diff --git a/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr b/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr new file mode 100644 index 0000000000000..452913f163ae2 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr @@ -0,0 +1,13 @@ +error[E0308]: mismatched types + --> $DIR/feature-gate-trait_upcasting.rs:11:25 + | +LL | let foo: &dyn Foo = bar; + | ^^^ expected trait `Foo`, found trait `Bar` + | + = note: add `#![feature(trait_upcasting)]` to the crate attributes to enable + = note: expected type `&dyn Foo` + found type `&dyn Bar` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From e21df4b5ff6768f114597112ce52fc9a23402765 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Tue, 12 Nov 2019 02:45:45 +0000 Subject: [PATCH 08/14] Handle upcasting to auto traits properly. --- src/librustc_codegen_ssa/base.rs | 36 ++++++++++--------- src/librustc_trait_selection/traits/select.rs | 32 ++++++++++++----- 2 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs index 657a0ed0a56dd..394440272a149 100644 --- a/src/librustc_codegen_ssa/base.rs +++ b/src/librustc_codegen_ssa/base.rs @@ -161,25 +161,27 @@ pub fn unsized_info<'tcx, 'a, Bx: BuilderMethods<'a, 'tcx>>( // Trait upcast let source_ptr = old_info.expect("unsized_info: missing old info for trait upcast"); - let target_trait_ref = target_data.principal() - .expect("target trait object of upcast does not have principal"); - - let trait_ref = target_trait_ref.with_self_ty(tcx, source); - let vtable = tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)); - let offset = match vtable { - Vtable::VtableObject(ref data) => data.vtable_base, - // HACK(alexreg): dubious solution to ICE in `manual-self-impl-for-unsafe-obj` test. - Vtable::VtableImpl(_) => 0, - _ => bug!("unsized_info: unexpected vtable kind {:?}", vtable), - }; + let target_ptr = if let Some(target_trait_ref) = target_data.principal() { + let trait_ref = target_trait_ref.with_self_ty(tcx, source); + let vtable = tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)); + let offset = match vtable { + Vtable::VtableObject(ref data) => data.vtable_base, + // HACK(alexreg): slightly dubious solution to ICE in + // `manual-self-impl-for-unsafe-obj` test. + Vtable::VtableImpl(_) => 0, + _ => bug!("unsized_info: unexpected vtable kind {:?}", vtable), + }; - let vtable_layout = bx.cx().layout_of(tcx.mk_mut_ptr(source)); - let source_ptr = bx.pointercast( - source_ptr, - bx.cx().scalar_pair_element_backend_type(vtable_layout, 1, true), - ); + let vtable_layout = bx.cx().layout_of(tcx.mk_mut_ptr(source)); + let source_ptr = bx.pointercast( + source_ptr, + bx.cx().scalar_pair_element_backend_type(vtable_layout, 1, true), + ); - let target_ptr = bx.struct_gep(source_ptr, offset as u64); + bx.struct_gep(source_ptr, offset as u64) + } else { + source_ptr + }; let vtable_layout = bx.cx().layout_of(tcx.mk_mut_ptr(target)); bx.pointercast( diff --git a/src/librustc_trait_selection/traits/select.rs b/src/librustc_trait_selection/traits/select.rs index 8886006c55758..104fbd6977f14 100644 --- a/src/librustc_trait_selection/traits/select.rs +++ b/src/librustc_trait_selection/traits/select.rs @@ -1865,9 +1865,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // // We always upcast when we can because of reason 3 (region bounds). - // All of a's auto traits need to be in b's auto traits. - data_b.auto_traits() - .all(|b| data_a.auto_traits().any(|a| a == b)) + if self.tcx().features().trait_upcasting { + true + } else { + // Just allow upcast kinds 2 and 3 from above. + data_a.principal_def_id() == data_b.principal_def_id() && + data_b.auto_traits() + // All of a's auto traits need to be in b's auto traits. + .all(|b| data_a.auto_traits().any(|a| a == b)) + } } // `T` -> `Trait` @@ -2949,12 +2955,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let iter = data_a .principal() .map(ty::ExistentialPredicate::Trait) - .into_iter() - .chain(data_a.projection_bounds().map(ty::ExistentialPredicate::Projection)) - .chain(data_b.auto_traits().map(ty::ExistentialPredicate::AutoTrait)); + .into_iter().chain( + data_a + .projection_bounds() + .map(|x| ty::ExistentialPredicate::Projection(x)), + ) + .chain( + data_b + .auto_traits() + .map(ty::ExistentialPredicate::AutoTrait), + ); tcx.mk_existential_predicates(iter) }); - let source_ty = tcx.mk_dynamic(existential_predicates, region_b); + let source_with_target_auto_traits = + tcx.mk_dynamic(existential_predicates, region_b); if tcx.features().trait_upcasting { // Register obligations for `dyn TraitA1 [TraitA2...]: TraitB1 [TraitB2...]`. @@ -2975,7 +2989,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } }) .map(|predicate| - predicate_to_obligation(predicate.with_self_ty(tcx, source_ty)) + predicate_to_obligation(predicate.with_self_ty(tcx, source)) ), ); } else { @@ -2997,7 +3011,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // with what our behavior should be there. -nikomatsakis let InferOk { obligations, .. } = self.infcx .at(&obligation.cause, obligation.param_env) - .eq(target, source_ty) // FIXME: see above. + .eq(target, source_with_target_auto_traits) // FIXME: see above. .map_err(|_| Unimplemented)?; nested.extend(obligations); } From a9305a16b43f593419707e254ee2306ed4805f3d Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Thu, 31 Oct 2019 03:53:51 +0000 Subject: [PATCH 09/14] Added page to Unstable Book. --- .../src/language-features/trait-upcasting.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/doc/unstable-book/src/language-features/trait-upcasting.md diff --git a/src/doc/unstable-book/src/language-features/trait-upcasting.md b/src/doc/unstable-book/src/language-features/trait-upcasting.md new file mode 100644 index 0000000000000..b7aafe58c3108 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/trait-upcasting.md @@ -0,0 +1,26 @@ +# `trait_upcasting` + +The tracking issue for this feature is: [#31436] + +[#65991]: https://github.com/rust-lang/rust/issues/65991 + +------------------------ + +The `trait_upcasting` feature adds support for trait upcasting. This allows a +trait object of type `dyn Foo` to be cast to a trait object of type `dyn Bar` +so long as `Foo: Bar`. + +```rust,edition2018 +#![feature(trait_upcasting)] + +trait Foo {} + +trait Bar: Foo {} + +impl Foo for i32 {} + +impl Bar for T {} + +let foo: &dyn Foo = &123; +let bar: &dyn Bar = foo; +``` From b18378980ed3f359e0851e5a8c93b5353bddfaa3 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Tue, 12 Nov 2019 02:52:15 +0000 Subject: [PATCH 10/14] Made adjustments suggested by reviews. --- src/librustc_codegen_ssa/base.rs | 5 ++++ src/librustc_middle/ty/sty.rs | 8 ------- src/librustc_trait_selection/traits/select.rs | 23 ++++++++++++++++++- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs index 394440272a149..592c70ed5baa8 100644 --- a/src/librustc_codegen_ssa/base.rs +++ b/src/librustc_codegen_ssa/base.rs @@ -161,7 +161,10 @@ pub fn unsized_info<'tcx, 'a, Bx: BuilderMethods<'a, 'tcx>>( // Trait upcast let source_ptr = old_info.expect("unsized_info: missing old info for trait upcast"); + // Only bother offsetting into the parent vtable if we are upcasting to a + // non-auto trait. let target_ptr = if let Some(target_trait_ref) = target_data.principal() { + // Find the offset of the supertrait's vtable within the subtrait (parent) vtable. let trait_ref = target_trait_ref.with_self_ty(tcx, source); let vtable = tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)); let offset = match vtable { @@ -172,12 +175,14 @@ pub fn unsized_info<'tcx, 'a, Bx: BuilderMethods<'a, 'tcx>>( _ => bug!("unsized_info: unexpected vtable kind {:?}", vtable), }; + // Ensure the pointer to the parent vtable has the right LLVM type. let vtable_layout = bx.cx().layout_of(tcx.mk_mut_ptr(source)); let source_ptr = bx.pointercast( source_ptr, bx.cx().scalar_pair_element_backend_type(vtable_layout, 1, true), ); + // Perform the offset within the parent vtable. bx.struct_gep(source_ptr, offset as u64) } else { source_ptr diff --git a/src/librustc_middle/ty/sty.rs b/src/librustc_middle/ty/sty.rs index cdba49e2f8f11..ac5477edcc3c0 100644 --- a/src/librustc_middle/ty/sty.rs +++ b/src/librustc_middle/ty/sty.rs @@ -1881,14 +1881,6 @@ impl<'tcx> TyS<'tcx> { } } - /// Panics if called on any type other than `PhantomData`. - pub fn phantom_ty(&self) -> Ty<'tcx> { - match self.kind { - Adt(def, substs) if def.is_phantom_data() => substs.type_at(0), - _ => bug!("`phantom_ty` is called on non-phantom-data type {:?}", self), - } - } - /// A scalar type is one that denotes an atomic datum, with no sub-components. /// (A RawPtr is scalar because it represents a non-managed pointer, so its /// contents are abstract to rustc.) diff --git a/src/librustc_trait_selection/traits/select.rs b/src/librustc_trait_selection/traits/select.rs index 104fbd6977f14..e859ece359eb6 100644 --- a/src/librustc_trait_selection/traits/select.rs +++ b/src/librustc_trait_selection/traits/select.rs @@ -2971,7 +2971,28 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { tcx.mk_dynamic(existential_predicates, region_b); if tcx.features().trait_upcasting { - // Register obligations for `dyn TraitA1 [TraitA2...]: TraitB1 [TraitB2...]`. + // Given that we are upcasting `dyn (TraitA1 + ... + TraitAn)` to + // `dyn (TraitB1 + ... + TraitBn)`, register proof obligations like + // + // dyn (TraitA1 + ... + TraitAn): TraitB1 + // ... + // dyn (TraitA1 + ... + TraitAn): TraitBn + // + // So, if for example we are upcasting `dyn (Foo + Send)` to `dyn (Bar + Send)`, + // then we would check that `dyn (Foo + Send): Bar` and `dyn (Foo + Send): Send` + // -- or at least we would, were it not for the slight pre-filter hack. + // + // The pre-filter hack removes cases where `TraitBi` is an auto-trait like + // `Send`, so long as we see that auto-trait in the A type. In our example, this + // would skip the dyn (Foo + Send): Send obligation. There are two reasons for + // this. The first is efficiency -- this case trivially holds. The second is + // because we would otherwise encounter ambiguity errors during bootstrap, owing + // to the `rustc_datastructures::sync::Send` trait (which is not the same as the + // standard `Send` trait). This trait has a `impl Send for T` impl, + // and thus we get an error when trying to choose between this impl versus and + // the automatic impl that we prove from `dyn Traits`. This ambiguity is silly + // (it doesn't matter which one we choose), but rather than resolve that in the + // general case (which is subtle), we can screen it out here easily enough. nested.extend( data_b.iter() // HACK(alexreg | nikomatsakis): we handle auto traits specially here From 29de923a4ab9de7fa636c9699704db15f537a50a Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Sat, 23 Nov 2019 02:27:14 +0000 Subject: [PATCH 11/14] Account for diamond inheritance in vtable layout. --- src/librustc_codegen_ssa/base.rs | 9 +- .../infer/error_reporting/mod.rs | 4 +- src/librustc_infer/traits/util.rs | 38 +++++++- src/librustc_mir/interpret/traits.rs | 2 +- src/librustc_mir/monomorphize/collector.rs | 2 + src/librustc_session/session.rs | 4 +- src/librustc_trait_selection/traits/mod.rs | 90 ++++++++++--------- src/librustc_trait_selection/traits/select.rs | 27 +++--- 8 files changed, 105 insertions(+), 71 deletions(-) diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs index 592c70ed5baa8..b3051ae07fb01 100644 --- a/src/librustc_codegen_ssa/base.rs +++ b/src/librustc_codegen_ssa/base.rs @@ -38,8 +38,8 @@ use rustc_middle::middle::cstore::EncodedMetadata; use rustc_middle::middle::cstore::{self, LinkagePreference}; use rustc_middle::middle::lang_items; use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; +use rustc_middle::traits::Vtable; use rustc_middle::ty::layout::{self, HasTyCtxt, TyAndLayout}; -use rustc_middle::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::cgu_reuse_tracker::CguReuse; @@ -166,7 +166,8 @@ pub fn unsized_info<'tcx, 'a, Bx: BuilderMethods<'a, 'tcx>>( let target_ptr = if let Some(target_trait_ref) = target_data.principal() { // Find the offset of the supertrait's vtable within the subtrait (parent) vtable. let trait_ref = target_trait_ref.with_self_ty(tcx, source); - let vtable = tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)); + let vtable = tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)) + .unwrap_or_else(|| bug!("unsized_info: no vtable found")); let offset = match vtable { Vtable::VtableObject(ref data) => data.vtable_base, // HACK(alexreg): slightly dubious solution to ICE in @@ -257,7 +258,7 @@ pub fn unsize_thin_ptr<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( pub fn coerce_ptr_unsized<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, op: OperandRef<'tcx, Bx::Value>, - dst: TyLayout<'tcx>, + dst: TyAndLayout<'tcx>, ) -> OperandValue { assert!(bx.cx().is_backend_scalar_pair(dst)); let src = op.layout; @@ -341,7 +342,7 @@ pub fn coerce_unsized_into<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( (&ty::Ref(..), &ty::Ref(..) | &ty::RawPtr(..)) | (&ty::RawPtr(..), &ty::RawPtr(..)) => { let src_op = bx.load_operand(src); let dst_op = coerce_ptr_unsized(bx, src_op, dst.layout); - dst_op.store(bx, dst);s + dst_op.store(bx, dst); } (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index dd72b37360ce2..9fcd4a1f9d0cd 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -1681,10 +1681,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { exp_found: &ty::error::ExpectedFound, diag: &mut DiagnosticBuilder<'tcx>, ) { - use rustc::ty::{Binder, TraitRef}; + use ty::{Binder, TraitRef}; let trait_ref = Binder::bind(TraitRef::identity(self.tcx, exp_found.found)); - let supertraits = crate::traits::supertraits(self.tcx, trait_ref); + let supertraits = crate::traits::util::supertraits(self.tcx, trait_ref); if supertraits.into_iter().any(|trait_ref| trait_ref.def_id() == exp_found.expected) { diag.note("add `#![feature(trait_upcasting)]` to the crate attributes to enable"); } diff --git a/src/librustc_infer/traits/util.rs b/src/librustc_infer/traits/util.rs index 55bea57f3e243..d234a8751375d 100644 --- a/src/librustc_infer/traits/util.rs +++ b/src/librustc_infer/traits/util.rs @@ -91,6 +91,8 @@ impl>> Extend for PredicateSet<'tcx> { pub struct Elaborator<'tcx> { stack: Vec>, visited: PredicateSet<'tcx>, + /// `true` to allow the same predicate to appear more than once within the sequence. + allow_repetitions: bool, } pub fn elaborate_trait_ref<'tcx>( @@ -108,6 +110,17 @@ pub fn elaborate_trait_refs<'tcx>( elaborate_predicates(tcx, predicates) } +pub fn elaborate_trait_ref_with_repetitions<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> Elaborator<'tcx> { + Elaborator { + stack: vec![predicate_obligation(trait_ref.without_const().to_predicate(), None)], + visited: PredicateSet::new(tcx), + allow_repetitions: true, + } +} + pub fn elaborate_predicates<'tcx>( tcx: TyCtxt<'tcx>, predicates: impl Iterator>, @@ -123,7 +136,11 @@ pub fn elaborate_obligations<'tcx>( ) -> Elaborator<'tcx> { let mut visited = PredicateSet::new(tcx); obligations.retain(|obligation| visited.insert(&obligation.predicate)); - Elaborator { stack: obligations, visited } + Elaborator { + stack: obligations, + visited, + allow_repetitions: false, + } } fn predicate_obligation<'tcx>( @@ -134,7 +151,12 @@ fn predicate_obligation<'tcx>( if let Some(span) = span { cause.span = span; } - Obligation { cause, param_env: ty::ParamEnv::empty(), recursion_depth: 0, predicate } + Obligation { + cause, + param_env: ty::ParamEnv::empty(), + recursion_depth: 0, + predicate, + } } impl Elaborator<'tcx> { @@ -144,6 +166,7 @@ impl Elaborator<'tcx> { fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) { let tcx = self.visited.tcx; + let allow_repetitions = self.allow_repetitions; match obligation.predicate { ty::Predicate::Trait(ref data, _) => { // Get predicates declared on the trait. @@ -162,7 +185,7 @@ impl Elaborator<'tcx> { // cases. One common case is when people define // `trait Sized: Sized { }` rather than `trait Sized { }`. let visited = &mut self.visited; - let obligations = obligations.filter(|o| visited.insert(&o.predicate)); + let obligations = obligations.filter(|o| allow_repetitions || visited.insert(&o.predicate)); self.stack.extend(obligations); } @@ -245,7 +268,7 @@ impl Elaborator<'tcx> { None } }) - .filter(|p| visited.insert(p)) + .filter(|p| allow_repetitions || visited.insert(p)) .map(|p| predicate_obligation(p, None)), ); } @@ -284,6 +307,13 @@ pub fn supertraits<'tcx>( elaborate_trait_ref(tcx, trait_ref).filter_to_traits() } +pub fn supertraits_with_repetitions<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> Supertraits<'tcx> { + elaborate_trait_ref_with_repetitions(tcx, trait_ref).filter_to_traits() +} + pub fn transitive_bounds<'tcx>( tcx: TyCtxt<'tcx>, bounds: impl Iterator>, diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index 744792ccc0c55..a36603a0f65ea 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -61,7 +61,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ptr_size * ( methods .iter() - .map(|l| u64::try_from(methods.len()).unwrap().checked_add(3).unwrap()) + .map(|l| u64::try_from(l.len()).unwrap().checked_add(3).unwrap()) .sum() ), ptr_align, diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 6232f7f02cb81..ee72f57e206d1 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -889,6 +889,8 @@ fn create_mono_items_for_vtable_methods<'tcx>( // Walk all methods of the trait, including those of its supertraits. let methods = tcx.vtable_methods(poly_trait_ref); let methods = methods + .into_iter() + .flat_map(|s| *s) .cloned() .filter_map(|method| method) .map(|(def_id, substs)| { diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs index 727e750829e8f..93a1315c6b59f 100644 --- a/src/librustc_session/session.rs +++ b/src/librustc_session/session.rs @@ -474,9 +474,7 @@ impl Session { self.opts.debugging_opts.asm_comments } pub fn verify_llvm_ir(&self) -> bool { - (self.opts.debugging_opts.verify_llvm_ir - || cfg!(always_verify_llvm_ir)) - && !self.opts.debugging_opts.no_verify_llvm_ir + self.opts.debugging_opts.verify_llvm_ir || cfg!(always_verify_llvm_ir) } pub fn borrowck_stats(&self) -> bool { self.opts.debugging_opts.borrowck_stats diff --git a/src/librustc_trait_selection/traits/mod.rs b/src/librustc_trait_selection/traits/mod.rs index 6bda3495a79e3..2f86bcfecf33f 100644 --- a/src/librustc_trait_selection/traits/mod.rs +++ b/src/librustc_trait_selection/traits/mod.rs @@ -66,7 +66,8 @@ pub use self::util::{ get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices, }; pub use self::util::{ - supertrait_def_ids, supertraits, transitive_bounds, SupertraitDefIds, Supertraits, + supertraits, supertraits_with_repetitions, supertrait_def_ids, transitive_bounds, Supertraits, + SupertraitDefIds, }; pub use rustc_infer::traits::*; @@ -471,51 +472,52 @@ fn vtable_methods<'tcx>( ) -> &'tcx [&'tcx [Option<(DefId, SubstsRef<'tcx>)>]] { debug!("vtable_methods({:?})", trait_ref); - tcx.arena.alloc_from_iter(supertraits(tcx, trait_ref).map(move |trait_ref| { - let trait_methods = tcx - .associated_items(trait_ref.def_id()) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Fn); - - // Now, list each method's `DefId` and `InternalSubsts` (for within its trait). - // If the method can never be called from this object, produce `None`. - &*tcx.arena.alloc_from_iter(trait_methods.map(move |trait_method| { - debug!("vtable_methods: trait_method={:?}", trait_method); - let def_id = trait_method.def_id; - - // Some methods cannot be called on an object; skip those. - if !is_vtable_safe_method(tcx, trait_ref.def_id(), &trait_method) { - debug!("vtable_methods: not vtable safe"); - return None; - } + tcx.arena.alloc_from_iter(supertraits_with_repetitions(tcx, trait_ref) + .map(move |trait_ref| { + let trait_methods = tcx + .associated_items(trait_ref.def_id()) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Fn); + + // Now, list each method's `DefId` and `InternalSubsts` (for within its trait). + // If the method can never be called from this object, produce `None`. + &*tcx.arena.alloc_from_iter(trait_methods.map(move |trait_method| { + debug!("vtable_methods: trait_method={:?}", trait_method); + let def_id = trait_method.def_id; + + // Some methods cannot be called on an object; skip those. + if !is_vtable_safe_method(tcx, trait_ref.def_id(), &trait_method) { + debug!("vtable_methods: not vtable safe"); + return None; + } - // The method may have some early-bound lifetimes; add regions for those. - let substs = trait_ref.map_bound(|trait_ref| { - InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { - trait_ref.substs[param.index as usize] - } - }) - }); - - // The trait type may have higher-ranked lifetimes in it; - // erase them if they appear, so that we get the type - // at some particular call site. - let substs = - tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &substs); - - // It's possible that the method relies on where-clauses that - // do not hold for this particular set of type parameters. - // Note that this method could then never be called, so we - // do not want to try and codegen it, in that case (see #23435). - let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); - if !normalize_and_test_predicates(tcx, predicates.predicates) { - debug!("vtable_methods: predicates do not hold"); - return None; - } + // The method may have some early-bound lifetimes; add regions for those. + let substs = trait_ref.map_bound(|trait_ref| { + InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { + GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + trait_ref.substs[param.index as usize] + } + }) + }); + + // The trait type may have higher-ranked lifetimes in it; + // erase them if they appear, so that we get the type + // at some particular call site. + let substs = + tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &substs); + + // It's possible that the method relies on where-clauses that + // do not hold for this particular set of type parameters. + // Note that this method could then never be called, so we + // do not want to try and codegen it, in that case (see #23435). + let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); + if !normalize_and_test_predicates(tcx, predicates.predicates) { + debug!("vtable_methods: predicates do not hold"); + return None; + } - Some((def_id, substs)) + Some((def_id, substs)) })) })) } diff --git a/src/librustc_trait_selection/traits/select.rs b/src/librustc_trait_selection/traits/select.rs index e859ece359eb6..6480d239023ef 100644 --- a/src/librustc_trait_selection/traits/select.rs +++ b/src/librustc_trait_selection/traits/select.rs @@ -2696,16 +2696,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // where we can unify, because otherwise select would have // reported an ambiguity. (When we do find a match, also // record it for later.) - let nonmatching = util::supertraits(tcx, poly_trait_ref).take_while(|&t| { - match self.infcx.commit_if_ok(|_| self.match_poly_trait_ref(obligation, t)) { - Ok(obligations) => { - upcast_trait_ref = Some(t); - nested.extend(obligations); - false - } - Err(_) => true, - } - }); + let nonmatching = util::supertraits_with_repetitions(tcx, poly_trait_ref) + .take_while( + |&trait_ref| match self.infcx.commit_if_ok( + |_| self.match_poly_trait_ref(obligation, trait_ref) + ) { + Ok(obligations) => { + upcast_trait_ref = Some(trait_ref); + nested.extend(obligations); + false + } + Err(_) => true, + }, + ); // Additionally, for each of the non-matching predicates that // we pass over, we sum up the set of number of vtable @@ -2713,9 +2716,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // trait. vtable_base = nonmatching // Skip 3 entries in vtable per supertrait for `(drop, size, align)` metadata. - .map(|trait_ref| - u64::try_from(tcx.count_own_vtable_entries(trait_ref)).unwrap().checked_add(3).unwrap() - ) + .map(|trait_ref| util::count_own_vtable_entries(tcx, trait_ref).checked_add(3).unwrap()) .sum(); } From 9549fbd6255fb9f7e4ce1a8859fae7335465e014 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Tue, 5 Nov 2019 02:18:39 +0000 Subject: [PATCH 12/14] Added tests. --- .../infer/error_reporting/mod.rs | 4 +- .../feature-gate-c_variadic.rs | 0 .../feature-gate-c_variadic.stderr | 0 .../feature-gate-static-nobundle-2.rs | 0 .../feature-gate-static-nobundle-2.stderr | 0 .../feature-gate-trait_upcasting.rs | 5 +- .../feature-gate-trait_upcasting.stderr | 10 +- ...exe.rs => anon-trait-static-method-exe.rs} | 0 ...pl_trait.rs => conservative-impl-trait.rs} | 0 src/test/ui/traits/trait-upcasting/basic.rs | 80 +++++++++ .../cyclic-trait-resolution.rs | 13 ++ .../cyclic-trait-resolution.stderr | 16 ++ src/test/ui/traits/trait-upcasting/diamond.rs | 108 ++++++++++++ .../traits/trait-upcasting/invalid-upcast.rs | 68 ++++++++ .../trait-upcasting/invalid-upcast.stderr | 135 +++++++++++++++ .../issue-11515-upcast-fn_mut-fn.rs} | 3 +- .../issue-11515-upcast-fn_mut-fn.stderr} | 3 +- .../ui/traits/trait-upcasting/lifetime.rs | 70 ++++++++ src/test/ui/traits/trait-upcasting/struct.rs | 155 ++++++++++++++++++ .../traits/trait-upcasting/subtrait-method.rs | 51 ++++++ .../trait-upcasting/subtrait-method.stderr | 68 ++++++++ .../trait-upcasting/upcast-fn_mut-fn-pass.rs | 12 ++ 22 files changed, 792 insertions(+), 9 deletions(-) rename src/test/ui/{feature-gate => feature-gates}/feature-gate-c_variadic.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/feature-gate-c_variadic.stderr (100%) rename src/test/ui/{feature-gate => feature-gates}/feature-gate-static-nobundle-2.rs (100%) rename src/test/ui/{feature-gate => feature-gates}/feature-gate-static-nobundle-2.stderr (100%) rename src/test/ui/traits/{anon_trait_static_method_exe.rs => anon-trait-static-method-exe.rs} (100%) rename src/test/ui/traits/{conservative_impl_trait.rs => conservative-impl-trait.rs} (100%) create mode 100644 src/test/ui/traits/trait-upcasting/basic.rs create mode 100644 src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.rs create mode 100644 src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.stderr create mode 100644 src/test/ui/traits/trait-upcasting/diamond.rs create mode 100644 src/test/ui/traits/trait-upcasting/invalid-upcast.rs create mode 100644 src/test/ui/traits/trait-upcasting/invalid-upcast.stderr rename src/test/ui/{issues/issue-11515.rs => traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs} (67%) rename src/test/ui/{issues/issue-11515.stderr => traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.stderr} (76%) create mode 100644 src/test/ui/traits/trait-upcasting/lifetime.rs create mode 100644 src/test/ui/traits/trait-upcasting/struct.rs create mode 100644 src/test/ui/traits/trait-upcasting/subtrait-method.rs create mode 100644 src/test/ui/traits/trait-upcasting/subtrait-method.stderr create mode 100644 src/test/ui/traits/trait-upcasting/upcast-fn_mut-fn-pass.rs diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index 9fcd4a1f9d0cd..3633ead054429 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -1686,7 +1686,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { let trait_ref = Binder::bind(TraitRef::identity(self.tcx, exp_found.found)); let supertraits = crate::traits::util::supertraits(self.tcx, trait_ref); if supertraits.into_iter().any(|trait_ref| trait_ref.def_id() == exp_found.expected) { - diag.note("add `#![feature(trait_upcasting)]` to the crate attributes to enable"); + diag.note( + "add `#![feature(trait_upcasting)]` to the crate attributes to enable \ + trait upcasting"); } } diff --git a/src/test/ui/feature-gate/feature-gate-c_variadic.rs b/src/test/ui/feature-gates/feature-gate-c_variadic.rs similarity index 100% rename from src/test/ui/feature-gate/feature-gate-c_variadic.rs rename to src/test/ui/feature-gates/feature-gate-c_variadic.rs diff --git a/src/test/ui/feature-gate/feature-gate-c_variadic.stderr b/src/test/ui/feature-gates/feature-gate-c_variadic.stderr similarity index 100% rename from src/test/ui/feature-gate/feature-gate-c_variadic.stderr rename to src/test/ui/feature-gates/feature-gate-c_variadic.stderr diff --git a/src/test/ui/feature-gate/feature-gate-static-nobundle-2.rs b/src/test/ui/feature-gates/feature-gate-static-nobundle-2.rs similarity index 100% rename from src/test/ui/feature-gate/feature-gate-static-nobundle-2.rs rename to src/test/ui/feature-gates/feature-gate-static-nobundle-2.rs diff --git a/src/test/ui/feature-gate/feature-gate-static-nobundle-2.stderr b/src/test/ui/feature-gates/feature-gate-static-nobundle-2.stderr similarity index 100% rename from src/test/ui/feature-gate/feature-gate-static-nobundle-2.stderr rename to src/test/ui/feature-gates/feature-gate-static-nobundle-2.stderr diff --git a/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs b/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs index f0facfd97fb7a..0abe68ee05924 100644 --- a/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs +++ b/src/test/ui/feature-gates/feature-gate-trait_upcasting.rs @@ -10,7 +10,8 @@ fn main() { let bar: &dyn Bar = &(); let foo: &dyn Foo = bar; //~^ ERROR mismatched types [E0308] - //~| NOTE expected type `&dyn Foo` //~| NOTE expected trait `Foo`, found trait `Bar` - //~| NOTE add `#![feature(trait_upcasting)]` to the crate attributes to enable + //~| NOTE expected due to this + //~| NOTE expected reference `&dyn Foo` + //~| NOTE add `#![feature(trait_upcasting)]` to the crate attributes to enable trait upcasting } diff --git a/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr b/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr index 452913f163ae2..a2e2986188e69 100644 --- a/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr +++ b/src/test/ui/feature-gates/feature-gate-trait_upcasting.stderr @@ -2,11 +2,13 @@ error[E0308]: mismatched types --> $DIR/feature-gate-trait_upcasting.rs:11:25 | LL | let foo: &dyn Foo = bar; - | ^^^ expected trait `Foo`, found trait `Bar` + | -------- ^^^ expected trait `Foo`, found trait `Bar` + | | + | expected due to this | - = note: add `#![feature(trait_upcasting)]` to the crate attributes to enable - = note: expected type `&dyn Foo` - found type `&dyn Bar` + = note: expected reference `&dyn Foo` + found reference `&dyn Bar` + = note: add `#![feature(trait_upcasting)]` to the crate attributes to enable trait upcasting error: aborting due to previous error diff --git a/src/test/ui/traits/anon_trait_static_method_exe.rs b/src/test/ui/traits/anon-trait-static-method-exe.rs similarity index 100% rename from src/test/ui/traits/anon_trait_static_method_exe.rs rename to src/test/ui/traits/anon-trait-static-method-exe.rs diff --git a/src/test/ui/traits/conservative_impl_trait.rs b/src/test/ui/traits/conservative-impl-trait.rs similarity index 100% rename from src/test/ui/traits/conservative_impl_trait.rs rename to src/test/ui/traits/conservative-impl-trait.rs diff --git a/src/test/ui/traits/trait-upcasting/basic.rs b/src/test/ui/traits/trait-upcasting/basic.rs new file mode 100644 index 0000000000000..78ddbe088887a --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/basic.rs @@ -0,0 +1,80 @@ +// run-pass + +#![feature(trait_upcasting)] + +trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + fn a(&self) -> i32 { 10 } + + fn z(&self) -> i32 { 11 } + + fn y(&self) -> i32 { 12 } +} + +trait Bar: Foo { + fn b(&self) -> i32 { 20 } + + fn w(&self) -> i32 { 21 } +} + +trait Baz: Bar { + fn c(&self) -> i32 { 30 } +} + +impl Foo for i32 { + fn a(&self) -> i32 { 100 } +} + +impl Bar for i32 { + fn b(&self) -> i32 { 200 } +} + +impl Baz for i32 { + fn c(&self) -> i32 { 300 } +} + +fn main() { + let baz: &dyn Baz = &1; + let _: &dyn std::fmt::Debug = baz; + let _: &(dyn Send + Sync) = baz; + let _: &dyn Send = baz; + let _: &dyn Sync = baz; + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + + let bar: &dyn Bar = baz; + let _: &dyn std::fmt::Debug = bar; + let _: &(dyn Send + Sync) = bar; + let _: &dyn Send = bar; + let _: &dyn Sync = bar; + assert_eq!(*bar, 1); + assert_eq!(bar.a(), 100); + assert_eq!(bar.b(), 200); + assert_eq!(bar.z(), 11); + assert_eq!(bar.y(), 12); + assert_eq!(bar.w(), 21); + + let foo: &dyn Foo = baz; + let _: &dyn std::fmt::Debug = foo; + let _: &(dyn Send + Sync) = foo; + let _: &dyn Send = foo; + let _: &dyn Sync = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + + let foo: &dyn Foo = bar; + let _: &dyn std::fmt::Debug = foo; + let _: &(dyn Send + Sync) = foo; + let _: &dyn Send = foo; + let _: &dyn Sync = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); +} diff --git a/src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.rs b/src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.rs new file mode 100644 index 0000000000000..1666b7ba292b2 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.rs @@ -0,0 +1,13 @@ +trait A: B + A {} +//~^ ERROR cycle detected when computing the supertraits of `A` [E0391] + +trait B {} + +impl A for () {} + +impl B for () {} + +fn main() { + let a: Box = Box::new(()); + let _b: Box = a; +} diff --git a/src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.stderr b/src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.stderr new file mode 100644 index 0000000000000..7a04c5d0dbbe5 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/cyclic-trait-resolution.stderr @@ -0,0 +1,16 @@ +error[E0391]: cycle detected when computing the supertraits of `A` + --> $DIR/cyclic-trait-resolution.rs:1:14 + | +LL | trait A: B + A {} + | ^ + | + = note: ...which again requires computing the supertraits of `A`, completing the cycle +note: cycle used when collecting item types in top-level module + --> $DIR/cyclic-trait-resolution.rs:1:1 + | +LL | trait A: B + A {} + | ^^^^^^^^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/src/test/ui/traits/trait-upcasting/diamond.rs b/src/test/ui/traits/trait-upcasting/diamond.rs new file mode 100644 index 0000000000000..531b40d83f9f7 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/diamond.rs @@ -0,0 +1,108 @@ +// run-pass + +#![feature(trait_upcasting)] + +trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + fn a(&self) -> i32 { 10 } + + fn z(&self) -> i32 { 11 } + + fn y(&self) -> i32 { 12 } +} + +trait Bar1: Foo { + fn b(&self) -> i32 { 20 } + + fn w(&self) -> i32 { 21 } +} + +trait Bar2: Foo { + fn c(&self) -> i32 { 30 } + + fn v(&self) -> i32 { 31 } +} + +trait Baz: Bar1 + Bar2 { + fn d(&self) -> i32 { 40 } +} + +impl Foo for i32 { + fn a(&self) -> i32 { 100 } +} + +impl Bar1 for i32 { + fn b(&self) -> i32 { 200 } +} + +impl Bar2 for i32 { + fn c(&self) -> i32 { 300 } +} + +impl Baz for i32 { + fn d(&self) -> i32 { 400 } +} + +fn main() { + let baz: &dyn Baz = &1; + let _: &dyn std::fmt::Debug = baz; + let _: &(dyn Send + Sync) = baz; + let _: &dyn Send = baz; + let _: &dyn Sync = baz; + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.d(), 400); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + assert_eq!(baz.v(), 31); + + let bar1: &dyn Bar1 = baz; + let _: &dyn std::fmt::Debug = bar1; + let _: &(dyn Send + Sync) = bar1; + let _: &dyn Send = bar1; + let _: &dyn Sync = bar1; + assert_eq!(*bar1, 1); + assert_eq!(bar1.a(), 100); + assert_eq!(bar1.b(), 200); + assert_eq!(bar1.z(), 11); + assert_eq!(bar1.y(), 12); + assert_eq!(bar1.w(), 21); + + let bar2: &dyn Bar2 = baz; + let _: &dyn std::fmt::Debug = bar2; + let _: &(dyn Send + Sync) = bar2; + let _: &dyn Send = bar2; + let _: &dyn Sync = bar2; + assert_eq!(*bar2, 1); + assert_eq!(bar2.a(), 100); + assert_eq!(bar2.c(), 300); + assert_eq!(bar2.z(), 11); + assert_eq!(bar2.y(), 12); + assert_eq!(bar2.v(), 31); + + let foo: &dyn Foo = baz; + let _: &dyn std::fmt::Debug = foo; + let _: &(dyn Send + Sync) = foo; + let _: &dyn Send = foo; + let _: &dyn Sync = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + + let foo: &dyn Foo = bar1; + let _: &dyn std::fmt::Debug = foo; + let _: &(dyn Send + Sync) = foo; + let _: &dyn Send = foo; + let _: &dyn Sync = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + + let foo: &dyn Foo = bar2; + let _: &dyn std::fmt::Debug = foo; + let _: &(dyn Send + Sync) = foo; + let _: &dyn Send = foo; + let _: &dyn Sync = foo; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); +} diff --git a/src/test/ui/traits/trait-upcasting/invalid-upcast.rs b/src/test/ui/traits/trait-upcasting/invalid-upcast.rs new file mode 100644 index 0000000000000..ac1ef6313fc2d --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/invalid-upcast.rs @@ -0,0 +1,68 @@ +#![feature(trait_upcasting)] + +trait Foo { + fn a(&self) -> i32 { 10 } + + fn z(&self) -> i32 { 11 } + + fn y(&self) -> i32 { 12 } +} + +trait Bar { + fn b(&self) -> i32 { 20 } + + fn w(&self) -> i32 { 21 } +} + +trait Baz { + fn c(&self) -> i32 { 30 } +} + +impl Foo for i32 { + fn a(&self) -> i32 { 100 } +} + +impl Bar for i32 { + fn b(&self) -> i32 { 200 } +} + +impl Baz for i32 { + fn c(&self) -> i32 { 300 } +} + +fn main() { + let baz: &dyn Baz = &1; + let _: &dyn std::fmt::Debug = baz; + //~^ ERROR `dyn Baz` doesn't implement `std::fmt::Debug` [E0277] + let _: &dyn Send = baz; + //~^ ERROR `dyn Baz` cannot be sent between threads safely [E0277] + let _: &dyn Sync = baz; + //~^ ERROR `dyn Baz` cannot be shared between threads safely [E0277] + + let bar: &dyn Bar = baz; + //~^ ERROR the trait bound `dyn Baz: Bar` is not satisfied [E0277] + let _: &dyn std::fmt::Debug = bar; + //~^ ERROR `dyn Bar` doesn't implement `std::fmt::Debug` [E0277] + let _: &dyn Send = bar; + //~^ ERROR `dyn Bar` cannot be sent between threads safely [E0277] + let _: &dyn Sync = bar; + //~^ ERROR `dyn Bar` cannot be shared between threads safely [E0277] + + let foo: &dyn Foo = baz; + //~^ ERROR the trait bound `dyn Baz: Foo` is not satisfied [E0277] + let _: &dyn std::fmt::Debug = foo; + //~^ ERROR `dyn Foo` doesn't implement `std::fmt::Debug` [E0277] + let _: &dyn Send = foo; + //~^ ERROR `dyn Foo` cannot be sent between threads safely [E0277] + let _: &dyn Sync = foo; + //~^ ERROR `dyn Foo` cannot be shared between threads safely [E0277] + + let foo: &dyn Foo = bar; + //~^ ERROR the trait bound `dyn Bar: Foo` is not satisfied [E0277] + let _: &dyn std::fmt::Debug = foo; + //~^ ERROR `dyn Foo` doesn't implement `std::fmt::Debug` [E0277] + let _: &dyn Send = foo; + //~^ ERROR `dyn Foo` cannot be sent between threads safely [E0277] + let _: &dyn Sync = foo; + //~^ ERROR `dyn Foo` cannot be shared between threads safely [E0277] +} diff --git a/src/test/ui/traits/trait-upcasting/invalid-upcast.stderr b/src/test/ui/traits/trait-upcasting/invalid-upcast.stderr new file mode 100644 index 0000000000000..731394d480aaa --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/invalid-upcast.stderr @@ -0,0 +1,135 @@ +error[E0277]: `dyn Baz` doesn't implement `std::fmt::Debug` + --> $DIR/invalid-upcast.rs:35:35 + | +LL | let _: &dyn std::fmt::Debug = baz; + | ^^^ `dyn Baz` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | + = help: the trait `std::fmt::Debug` is not implemented for `dyn Baz` + = note: required for the cast to the object type `dyn std::fmt::Debug` + +error[E0277]: `dyn Baz` cannot be sent between threads safely + --> $DIR/invalid-upcast.rs:37:24 + | +LL | let _: &dyn Send = baz; + | ^^^ `dyn Baz` cannot be sent between threads safely + | + = help: the trait `std::marker::Send` is not implemented for `dyn Baz` + = note: required for the cast to the object type `dyn std::marker::Send` + +error[E0277]: `dyn Baz` cannot be shared between threads safely + --> $DIR/invalid-upcast.rs:39:24 + | +LL | let _: &dyn Sync = baz; + | ^^^ `dyn Baz` cannot be shared between threads safely + | + = help: the trait `std::marker::Sync` is not implemented for `dyn Baz` + = note: required for the cast to the object type `dyn std::marker::Sync` + +error[E0277]: the trait bound `dyn Baz: Bar` is not satisfied + --> $DIR/invalid-upcast.rs:42:25 + | +LL | let bar: &dyn Bar = baz; + | ^^^ the trait `Bar` is not implemented for `dyn Baz` + | + = note: required for the cast to the object type `dyn Bar` + +error[E0277]: `dyn Bar` doesn't implement `std::fmt::Debug` + --> $DIR/invalid-upcast.rs:44:35 + | +LL | let _: &dyn std::fmt::Debug = bar; + | ^^^ `dyn Bar` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | + = help: the trait `std::fmt::Debug` is not implemented for `dyn Bar` + = note: required for the cast to the object type `dyn std::fmt::Debug` + +error[E0277]: `dyn Bar` cannot be sent between threads safely + --> $DIR/invalid-upcast.rs:46:24 + | +LL | let _: &dyn Send = bar; + | ^^^ `dyn Bar` cannot be sent between threads safely + | + = help: the trait `std::marker::Send` is not implemented for `dyn Bar` + = note: required for the cast to the object type `dyn std::marker::Send` + +error[E0277]: `dyn Bar` cannot be shared between threads safely + --> $DIR/invalid-upcast.rs:48:24 + | +LL | let _: &dyn Sync = bar; + | ^^^ `dyn Bar` cannot be shared between threads safely + | + = help: the trait `std::marker::Sync` is not implemented for `dyn Bar` + = note: required for the cast to the object type `dyn std::marker::Sync` + +error[E0277]: the trait bound `dyn Baz: Foo` is not satisfied + --> $DIR/invalid-upcast.rs:51:25 + | +LL | let foo: &dyn Foo = baz; + | ^^^ the trait `Foo` is not implemented for `dyn Baz` + | + = note: required for the cast to the object type `dyn Foo` + +error[E0277]: `dyn Foo` doesn't implement `std::fmt::Debug` + --> $DIR/invalid-upcast.rs:53:35 + | +LL | let _: &dyn std::fmt::Debug = foo; + | ^^^ `dyn Foo` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | + = help: the trait `std::fmt::Debug` is not implemented for `dyn Foo` + = note: required for the cast to the object type `dyn std::fmt::Debug` + +error[E0277]: `dyn Foo` cannot be sent between threads safely + --> $DIR/invalid-upcast.rs:55:24 + | +LL | let _: &dyn Send = foo; + | ^^^ `dyn Foo` cannot be sent between threads safely + | + = help: the trait `std::marker::Send` is not implemented for `dyn Foo` + = note: required for the cast to the object type `dyn std::marker::Send` + +error[E0277]: `dyn Foo` cannot be shared between threads safely + --> $DIR/invalid-upcast.rs:57:24 + | +LL | let _: &dyn Sync = foo; + | ^^^ `dyn Foo` cannot be shared between threads safely + | + = help: the trait `std::marker::Sync` is not implemented for `dyn Foo` + = note: required for the cast to the object type `dyn std::marker::Sync` + +error[E0277]: the trait bound `dyn Bar: Foo` is not satisfied + --> $DIR/invalid-upcast.rs:60:25 + | +LL | let foo: &dyn Foo = bar; + | ^^^ the trait `Foo` is not implemented for `dyn Bar` + | + = note: required for the cast to the object type `dyn Foo` + +error[E0277]: `dyn Foo` doesn't implement `std::fmt::Debug` + --> $DIR/invalid-upcast.rs:62:35 + | +LL | let _: &dyn std::fmt::Debug = foo; + | ^^^ `dyn Foo` cannot be formatted using `{:?}` because it doesn't implement `std::fmt::Debug` + | + = help: the trait `std::fmt::Debug` is not implemented for `dyn Foo` + = note: required for the cast to the object type `dyn std::fmt::Debug` + +error[E0277]: `dyn Foo` cannot be sent between threads safely + --> $DIR/invalid-upcast.rs:64:24 + | +LL | let _: &dyn Send = foo; + | ^^^ `dyn Foo` cannot be sent between threads safely + | + = help: the trait `std::marker::Send` is not implemented for `dyn Foo` + = note: required for the cast to the object type `dyn std::marker::Send` + +error[E0277]: `dyn Foo` cannot be shared between threads safely + --> $DIR/invalid-upcast.rs:66:24 + | +LL | let _: &dyn Sync = foo; + | ^^^ `dyn Foo` cannot be shared between threads safely + | + = help: the trait `std::marker::Sync` is not implemented for `dyn Foo` + = note: required for the cast to the object type `dyn std::marker::Sync` + +error: aborting due to 15 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/issues/issue-11515.rs b/src/test/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs similarity index 67% rename from src/test/ui/issues/issue-11515.rs rename to src/test/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs index a7671b9282a99..803b7a0b42392 100644 --- a/src/test/ui/issues/issue-11515.rs +++ b/src/test/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.rs @@ -6,5 +6,6 @@ struct Test { fn main() { let closure: Box = Box::new(|| ()); - let test = box Test { func: closure }; //~ ERROR mismatched types + let test = box Test { func: closure }; + //~^ ERROR mismatched types } diff --git a/src/test/ui/issues/issue-11515.stderr b/src/test/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.stderr similarity index 76% rename from src/test/ui/issues/issue-11515.stderr rename to src/test/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.stderr index b53563d7b653c..d5a6768bee784 100644 --- a/src/test/ui/issues/issue-11515.stderr +++ b/src/test/ui/traits/trait-upcasting/issue-11515-upcast-fn_mut-fn.stderr @@ -1,11 +1,12 @@ error[E0308]: mismatched types - --> $DIR/issue-11515.rs:9:33 + --> $DIR/issue-11515-upcast-fn_mut-fn.rs:9:33 | LL | let test = box Test { func: closure }; | ^^^^^^^ expected trait `std::ops::FnMut`, found trait `std::ops::Fn` | = note: expected struct `std::boxed::Box<(dyn std::ops::FnMut() + 'static)>` found struct `std::boxed::Box<(dyn std::ops::Fn() + 'static)>` + = note: add `#![feature(trait_upcasting)]` to the crate attributes to enable trait upcasting error: aborting due to previous error diff --git a/src/test/ui/traits/trait-upcasting/lifetime.rs b/src/test/ui/traits/trait-upcasting/lifetime.rs new file mode 100644 index 0000000000000..46b461583db48 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/lifetime.rs @@ -0,0 +1,70 @@ +// run-pass + +#![feature(trait_upcasting)] + +trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + fn a(&self) -> i32 { 10 } + + fn z(&self) -> i32 { 11 } + + fn y(&self) -> i32 { 12 } +} + +trait Bar: Foo { + fn b(&self) -> i32 { 20 } + + fn w(&self) -> i32 { 21 } +} + +trait Baz: Bar { + fn c(&self) -> i32 { 30 } +} + +impl Foo for i32 { + fn a(&self) -> i32 { 100 } +} + +impl Bar for i32 { + fn b(&self) -> i32 { 200 } +} + +impl Baz for i32 { + fn c(&self) -> i32 { 300 } +} + +// Note: upcast lifetime means a shorter lifetime. +fn upcast_baz<'a: 'b, 'b, T>(v: Box, _l: &'b T) -> Box { v } +fn upcast_bar<'a: 'b, 'b, T>(v: Box, _l: &'b T) -> Box { v } +fn upcast_foo<'a: 'b, 'b, T>(v: Box, _l: &'b T) -> Box { v } + +fn main() { + let v = Box::new(1); + let l = &(); // dummy lifetime (shorter than `baz`) + + let baz: Box = v.clone(); + let u = upcast_baz(baz, &l); + assert_eq!(*u, 1); + assert_eq!(u.a(), 100); + assert_eq!(u.b(), 200); + assert_eq!(u.c(), 300); + + let baz: Box = v.clone(); + let bar: Box = baz; + let u = upcast_bar(bar, &l); + assert_eq!(*u, 1); + assert_eq!(u.a(), 100); + assert_eq!(u.b(), 200); + + let baz: Box = v.clone(); + let foo: Box = baz; + let u = upcast_foo(foo, &l); + assert_eq!(*u, 1); + assert_eq!(u.a(), 100); + + let baz: Box = v.clone(); + let bar: Box = baz; + let foo: Box = bar; + let u = upcast_foo(foo, &l); + assert_eq!(*u, 1); + assert_eq!(u.a(), 100); +} diff --git a/src/test/ui/traits/trait-upcasting/struct.rs b/src/test/ui/traits/trait-upcasting/struct.rs new file mode 100644 index 0000000000000..cf71ed4955137 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/struct.rs @@ -0,0 +1,155 @@ +// run-pass + +#![feature(trait_upcasting)] + +use std::rc::Rc; +use std::sync::Arc; + +trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + fn a(&self) -> i32 { 10 } + + fn z(&self) -> i32 { 11 } + + fn y(&self) -> i32 { 12 } +} + +trait Bar: Foo { + fn b(&self) -> i32 { 20 } + + fn w(&self) -> i32 { 21 } +} + +trait Baz: Bar { + fn c(&self) -> i32 { 30 } +} + +impl Foo for i32 { + fn a(&self) -> i32 { 100 } +} + +impl Bar for i32 { + fn b(&self) -> i32 { 200 } +} + +impl Baz for i32 { + fn c(&self) -> i32 { 300 } +} + +fn test_box() { + let v = Box::new(1); + + let baz: Box = v.clone(); + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + + let baz: Box = v.clone(); + let bar: Box = baz; + assert_eq!(*bar, 1); + assert_eq!(bar.a(), 100); + assert_eq!(bar.b(), 200); + assert_eq!(bar.z(), 11); + assert_eq!(bar.y(), 12); + assert_eq!(bar.w(), 21); + + let baz: Box = v.clone(); + let foo: Box = baz; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + + let baz: Box = v.clone(); + let bar: Box = baz; + let foo: Box = bar; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); +} + +fn test_rc() { + let v = Rc::new(1); + + let baz: Rc = v.clone(); + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + + let baz: Rc = v.clone(); + let bar: Rc = baz; + assert_eq!(*bar, 1); + assert_eq!(bar.a(), 100); + assert_eq!(bar.b(), 200); + assert_eq!(bar.z(), 11); + assert_eq!(bar.y(), 12); + assert_eq!(bar.w(), 21); + + let baz: Rc = v.clone(); + let foo: Rc = baz; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + + let baz: Rc = v.clone(); + let bar: Rc = baz; + let foo: Rc = bar; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); +} + +fn test_arc() { + let v = Arc::new(1); + + let baz: Arc = v.clone(); + assert_eq!(*baz, 1); + assert_eq!(baz.a(), 100); + assert_eq!(baz.b(), 200); + assert_eq!(baz.c(), 300); + assert_eq!(baz.z(), 11); + assert_eq!(baz.y(), 12); + assert_eq!(baz.w(), 21); + + let baz: Arc = v.clone(); + let bar: Arc = baz; + assert_eq!(*bar, 1); + assert_eq!(bar.a(), 100); + assert_eq!(bar.b(), 200); + assert_eq!(bar.z(), 11); + assert_eq!(bar.y(), 12); + assert_eq!(bar.w(), 21); + + let baz: Arc = v.clone(); + let foo: Arc = baz; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); + + let baz: Arc = v.clone(); + let bar: Arc = baz; + let foo: Arc = bar; + assert_eq!(*foo, 1); + assert_eq!(foo.a(), 100); + assert_eq!(foo.z(), 11); + assert_eq!(foo.y(), 12); +} + +fn main() { + test_box(); + test_rc(); + test_arc(); +} diff --git a/src/test/ui/traits/trait-upcasting/subtrait-method.rs b/src/test/ui/traits/trait-upcasting/subtrait-method.rs new file mode 100644 index 0000000000000..0c3af54fe2b04 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/subtrait-method.rs @@ -0,0 +1,51 @@ +#![feature(trait_upcasting)] + +trait Foo: PartialEq + std::fmt::Debug + Send + Sync { + fn a(&self) -> i32 { 10 } + + fn z(&self) -> i32 { 11 } + + fn y(&self) -> i32 { 12 } +} + +trait Bar: Foo { + fn b(&self) -> i32 { 20 } + + fn w(&self) -> i32 { 21 } +} + +trait Baz: Bar { + fn c(&self) -> i32 { 30 } +} + +impl Foo for i32 { + fn a(&self) -> i32 { 100 } +} + +impl Bar for i32 { + fn b(&self) -> i32 { 200 } +} + +impl Baz for i32 { + fn c(&self) -> i32 { 300 } +} + +fn main() { + let baz: &dyn Baz = &1; + + let bar: &dyn Bar = baz; + bar.c(); + //~^ ERROR no method named `c` found for reference `&dyn Bar` in the current scope [E0599] + + let foo: &dyn Foo = baz; + foo.b(); + //~^ ERROR no method named `b` found for reference `&dyn Foo` in the current scope [E0599] + foo.c(); + //~^ ERROR no method named `c` found for reference `&dyn Foo` in the current scope [E0599] + + let foo: &dyn Foo = bar; + foo.b(); + //~^ ERROR no method named `b` found for reference `&dyn Foo` in the current scope [E0599] + foo.c(); + //~^ ERROR no method named `c` found for reference `&dyn Foo` in the current scope [E0599] +} diff --git a/src/test/ui/traits/trait-upcasting/subtrait-method.stderr b/src/test/ui/traits/trait-upcasting/subtrait-method.stderr new file mode 100644 index 0000000000000..4b0765cdb8235 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/subtrait-method.stderr @@ -0,0 +1,68 @@ +error[E0599]: no method named `c` found for reference `&dyn Bar` in the current scope + --> $DIR/subtrait-method.rs:37:9 + | +LL | bar.c(); + | ^ help: there is an associated function with a similar name: `a` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Baz` defines an item `c`, perhaps you need to implement it + --> $DIR/subtrait-method.rs:17:1 + | +LL | trait Baz: Bar { + | ^^^^^^^^^^^^^^ + +error[E0599]: no method named `b` found for reference `&dyn Foo` in the current scope + --> $DIR/subtrait-method.rs:41:9 + | +LL | foo.b(); + | ^ help: there is an associated function with a similar name: `a` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Bar` defines an item `b`, perhaps you need to implement it + --> $DIR/subtrait-method.rs:11:1 + | +LL | trait Bar: Foo { + | ^^^^^^^^^^^^^^ + +error[E0599]: no method named `c` found for reference `&dyn Foo` in the current scope + --> $DIR/subtrait-method.rs:43:9 + | +LL | foo.c(); + | ^ help: there is an associated function with a similar name: `a` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Baz` defines an item `c`, perhaps you need to implement it + --> $DIR/subtrait-method.rs:17:1 + | +LL | trait Baz: Bar { + | ^^^^^^^^^^^^^^ + +error[E0599]: no method named `b` found for reference `&dyn Foo` in the current scope + --> $DIR/subtrait-method.rs:47:9 + | +LL | foo.b(); + | ^ help: there is an associated function with a similar name: `a` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Bar` defines an item `b`, perhaps you need to implement it + --> $DIR/subtrait-method.rs:11:1 + | +LL | trait Bar: Foo { + | ^^^^^^^^^^^^^^ + +error[E0599]: no method named `c` found for reference `&dyn Foo` in the current scope + --> $DIR/subtrait-method.rs:49:9 + | +LL | foo.c(); + | ^ help: there is an associated function with a similar name: `a` + | + = help: items from traits can only be used if the trait is implemented and in scope +note: `Baz` defines an item `c`, perhaps you need to implement it + --> $DIR/subtrait-method.rs:17:1 + | +LL | trait Baz: Bar { + | ^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/traits/trait-upcasting/upcast-fn_mut-fn-pass.rs b/src/test/ui/traits/trait-upcasting/upcast-fn_mut-fn-pass.rs new file mode 100644 index 0000000000000..d3f4b73e7e216 --- /dev/null +++ b/src/test/ui/traits/trait-upcasting/upcast-fn_mut-fn-pass.rs @@ -0,0 +1,12 @@ +// run-pass + +#![feature(trait_upcasting)] + +fn call_it(f: Box T>) -> T { + f() +} + +fn main() { + let f: Box i32> = Box::new(|| 42); + assert_eq!(call_it(f), 42); +} From 394a9701f00058b94efcfcae6f92c064cd5f114e Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Mon, 6 Apr 2020 23:31:24 +0100 Subject: [PATCH 13/14] Fixed rebase fallout. --- src/librustc_codegen_ssa/base.rs | 6 +++--- src/librustc_middle/ty/mod.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs index b3051ae07fb01..3b1ee90731a52 100644 --- a/src/librustc_codegen_ssa/base.rs +++ b/src/librustc_codegen_ssa/base.rs @@ -39,7 +39,7 @@ use rustc_middle::middle::cstore::{self, LinkagePreference}; use rustc_middle::middle::lang_items; use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, MonoItem}; use rustc_middle::traits::Vtable; -use rustc_middle::ty::layout::{self, HasTyCtxt, TyAndLayout}; +use rustc_middle::ty::layout::{HasTyCtxt, TyAndLayout}; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_session::cgu_reuse_tracker::CguReuse; @@ -167,7 +167,7 @@ pub fn unsized_info<'tcx, 'a, Bx: BuilderMethods<'a, 'tcx>>( // Find the offset of the supertrait's vtable within the subtrait (parent) vtable. let trait_ref = target_trait_ref.with_self_ty(tcx, source); let vtable = tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)) - .unwrap_or_else(|| bug!("unsized_info: no vtable found")); + .unwrap_or_else(|| bug!("unsized_info: vtable not found")); let offset = match vtable { Vtable::VtableObject(ref data) => data.vtable_base, // HACK(alexreg): slightly dubious solution to ICE in @@ -434,7 +434,7 @@ pub fn from_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( pub fn to_immediate<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( bx: &mut Bx, val: Bx::Value, - layout: layout::TyAndLayout<'_>, + layout: TyAndLayout<'_>, ) -> Bx::Value { if let Abi::Scalar(ref scalar) = layout.abi { return to_immediate_scalar(bx, val, scalar); diff --git a/src/librustc_middle/ty/mod.rs b/src/librustc_middle/ty/mod.rs index 8bb9934789a23..1d36665d7a457 100644 --- a/src/librustc_middle/ty/mod.rs +++ b/src/librustc_middle/ty/mod.rs @@ -1431,7 +1431,7 @@ pub trait ToPredicate<'tcx> { impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { fn to_predicate(&self) -> Predicate<'tcx> { - ty::Predicate::Trait( + Predicate::Trait( ty::Binder::dummy(ty::TraitPredicate { trait_ref: self.value }), self.constness, ) @@ -1440,7 +1440,7 @@ impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<&TraitRef<'tcx>> { fn to_predicate(&self) -> Predicate<'tcx> { - ty::Predicate::Trait( + Predicate::Trait( ty::Binder::dummy(ty::TraitPredicate { trait_ref: *self.value }), self.constness, ) @@ -1449,13 +1449,13 @@ impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<&TraitRef<'tcx>> { impl<'tcx> ToPredicate<'tcx> for ConstnessAnd> { fn to_predicate(&self) -> Predicate<'tcx> { - ty::Predicate::Trait(self.value.to_poly_trait_predicate(), self.constness) + Predicate::Trait(self.value.to_poly_trait_predicate(), self.constness) } } impl<'tcx> ToPredicate<'tcx> for ConstnessAnd<&PolyTraitRef<'tcx>> { fn to_predicate(&self) -> Predicate<'tcx> { - ty::Predicate::Trait(self.value.to_poly_trait_predicate(), self.constness) + Predicate::Trait(self.value.to_poly_trait_predicate(), self.constness) } } From 041cbfb0f20db5e7f3e99f787ead05bff9dbcd18 Mon Sep 17 00:00:00 2001 From: Alexander Regueiro Date: Thu, 16 Apr 2020 19:13:02 +0100 Subject: [PATCH 14/14] Ran rustfmt. --- src/librustc_codegen_ssa/base.rs | 37 +++----- .../infer/error_reporting/mod.rs | 7 +- src/librustc_infer/traits/util.rs | 16 +--- src/librustc_mir/interpret/traits.rs | 11 +-- src/librustc_trait_selection/traits/mod.rs | 91 +++++++++---------- src/librustc_trait_selection/traits/select.rs | 56 ++++++------ src/librustc_typeck/astconv.rs | 7 +- 7 files changed, 101 insertions(+), 124 deletions(-) diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs index 3b1ee90731a52..682a8e1084f0b 100644 --- a/src/librustc_codegen_ssa/base.rs +++ b/src/librustc_codegen_ssa/base.rs @@ -166,7 +166,8 @@ pub fn unsized_info<'tcx, 'a, Bx: BuilderMethods<'a, 'tcx>>( let target_ptr = if let Some(target_trait_ref) = target_data.principal() { // Find the offset of the supertrait's vtable within the subtrait (parent) vtable. let trait_ref = target_trait_ref.with_self_ty(tcx, source); - let vtable = tcx.codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)) + let vtable = tcx + .codegen_fulfill_obligation((ty::ParamEnv::reveal_all(), trait_ref)) .unwrap_or_else(|| bug!("unsized_info: vtable not found")); let offset = match vtable { Vtable::VtableObject(ref data) => data.vtable_base, @@ -271,19 +272,14 @@ pub fn coerce_ptr_unsized<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // existing vtable. See the `get_vtable` fn in `meth.rs` for more details of vtable // layout. - let base = bx.pointercast( - base, - bx.cx().scalar_pair_element_backend_type(dst, 0, true), - ); + let base = bx.pointercast(base, bx.cx().scalar_pair_element_backend_type(dst, 0, true)); let info = match (&src.ty.kind, &dst.ty.kind) { - (&ty::Ref(_, a, _), - &ty::Ref(_, b, _)) | - (&ty::Ref(_, a, _), - &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) | - (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), - &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => { - unsized_info(bx, a, b, Some(info)) - } + (&ty::Ref(_, a, _), &ty::Ref(_, b, _)) + | (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) + | ( + &ty::RawPtr(ty::TypeAndMut { ty: a, .. }), + &ty::RawPtr(ty::TypeAndMut { ty: b, .. }), + ) => unsized_info(bx, a, b, Some(info)), (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => { assert_eq!(def_a, def_b); @@ -313,17 +309,14 @@ pub fn coerce_ptr_unsized<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. // FIXME(eddyb) move these out of this `match` arm, so they're always // applied, uniformly, no matter the source/destination types. - (bx.bitcast(base, bx.cx().scalar_pair_element_backend_type(dst, 0, true)), - bx.bitcast(info, bx.cx().scalar_pair_element_backend_type(dst, 1, true))) - } - OperandValue::Immediate(base) => { - unsize_thin_ptr(bx, base, src.ty, dst.ty) + ( + bx.bitcast(base, bx.cx().scalar_pair_element_backend_type(dst, 0, true)), + bx.bitcast(info, bx.cx().scalar_pair_element_backend_type(dst, 1, true)), + ) } + OperandValue::Immediate(base) => unsize_thin_ptr(bx, base, src.ty, dst.ty), OperandValue::Ref(..) => { - bug!( - "coerce_ptr_unsized: unexpected by-ref operand {:?}", - op - ); + bug!("coerce_ptr_unsized: unexpected by-ref operand {:?}", op); } }; OperandValue::Pair(base, info) diff --git a/src/librustc_infer/infer/error_reporting/mod.rs b/src/librustc_infer/infer/error_reporting/mod.rs index 3633ead054429..dfb78d255641e 100644 --- a/src/librustc_infer/infer/error_reporting/mod.rs +++ b/src/librustc_infer/infer/error_reporting/mod.rs @@ -1591,9 +1591,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { self.suggest_as_ref_where_appropriate(span, &exp_found, diag); if let &TypeError::Traits(ref exp_found_traits) = terr { - self.note_enable_trait_upcasting_where_appropriate( - &exp_found_traits, diag - ); + self.note_enable_trait_upcasting_where_appropriate(&exp_found_traits, diag); } } @@ -1688,7 +1686,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if supertraits.into_iter().any(|trait_ref| trait_ref.def_id() == exp_found.expected) { diag.note( "add `#![feature(trait_upcasting)]` to the crate attributes to enable \ - trait upcasting"); + trait upcasting", + ); } } diff --git a/src/librustc_infer/traits/util.rs b/src/librustc_infer/traits/util.rs index d234a8751375d..34083b8f6dfea 100644 --- a/src/librustc_infer/traits/util.rs +++ b/src/librustc_infer/traits/util.rs @@ -136,11 +136,7 @@ pub fn elaborate_obligations<'tcx>( ) -> Elaborator<'tcx> { let mut visited = PredicateSet::new(tcx); obligations.retain(|obligation| visited.insert(&obligation.predicate)); - Elaborator { - stack: obligations, - visited, - allow_repetitions: false, - } + Elaborator { stack: obligations, visited, allow_repetitions: false } } fn predicate_obligation<'tcx>( @@ -151,12 +147,7 @@ fn predicate_obligation<'tcx>( if let Some(span) = span { cause.span = span; } - Obligation { - cause, - param_env: ty::ParamEnv::empty(), - recursion_depth: 0, - predicate, - } + Obligation { cause, param_env: ty::ParamEnv::empty(), recursion_depth: 0, predicate } } impl Elaborator<'tcx> { @@ -185,7 +176,8 @@ impl Elaborator<'tcx> { // cases. One common case is when people define // `trait Sized: Sized { }` rather than `trait Sized { }`. let visited = &mut self.visited; - let obligations = obligations.filter(|o| allow_repetitions || visited.insert(&o.predicate)); + let obligations = + obligations.filter(|o| allow_repetitions || visited.insert(&o.predicate)); self.stack.extend(obligations); } diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index a36603a0f65ea..07c284d90acbc 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -58,12 +58,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let vtable = self.memory.allocate( // Compute size of vtable, including 3 entries per supertrait for (drop, size, align) // metadata. - ptr_size * ( - methods + ptr_size + * (methods .iter() .map(|l| u64::try_from(l.len()).unwrap().checked_add(3).unwrap()) - .sum() - ), + .sum()), ptr_align, MemoryKind::Vtable, ); @@ -73,9 +72,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { // one pointer size each time. let mut cur_ptr = vtable; let mut write_ptr = |memory: &mut Memory<'mir, 'tcx, M>, val| -> InterpResult<'_> { - let res = memory - .get_raw_mut(cur_ptr.alloc_id)? - .write_ptr_sized(tcx, cur_ptr, val)?; + let res = memory.get_raw_mut(cur_ptr.alloc_id)?.write_ptr_sized(tcx, cur_ptr, val)?; cur_ptr = cur_ptr.offset(ptr_size, tcx)?; Ok(res) }; diff --git a/src/librustc_trait_selection/traits/mod.rs b/src/librustc_trait_selection/traits/mod.rs index 2f86bcfecf33f..6b9fb12c45ace 100644 --- a/src/librustc_trait_selection/traits/mod.rs +++ b/src/librustc_trait_selection/traits/mod.rs @@ -66,8 +66,8 @@ pub use self::util::{ get_vtable_index_of_object_method, impl_item_is_final, predicate_for_trait_def, upcast_choices, }; pub use self::util::{ - supertraits, supertraits_with_repetitions, supertrait_def_ids, transitive_bounds, Supertraits, - SupertraitDefIds, + supertrait_def_ids, supertraits, supertraits_with_repetitions, transitive_bounds, + SupertraitDefIds, Supertraits, }; pub use rustc_infer::traits::*; @@ -472,52 +472,51 @@ fn vtable_methods<'tcx>( ) -> &'tcx [&'tcx [Option<(DefId, SubstsRef<'tcx>)>]] { debug!("vtable_methods({:?})", trait_ref); - tcx.arena.alloc_from_iter(supertraits_with_repetitions(tcx, trait_ref) - .map(move |trait_ref| { - let trait_methods = tcx - .associated_items(trait_ref.def_id()) - .in_definition_order() - .filter(|item| item.kind == ty::AssocKind::Fn); - - // Now, list each method's `DefId` and `InternalSubsts` (for within its trait). - // If the method can never be called from this object, produce `None`. - &*tcx.arena.alloc_from_iter(trait_methods.map(move |trait_method| { - debug!("vtable_methods: trait_method={:?}", trait_method); - let def_id = trait_method.def_id; - - // Some methods cannot be called on an object; skip those. - if !is_vtable_safe_method(tcx, trait_ref.def_id(), &trait_method) { - debug!("vtable_methods: not vtable safe"); - return None; - } + tcx.arena.alloc_from_iter(supertraits_with_repetitions(tcx, trait_ref).map(move |trait_ref| { + let trait_methods = tcx + .associated_items(trait_ref.def_id()) + .in_definition_order() + .filter(|item| item.kind == ty::AssocKind::Fn); + + // Now, list each method's `DefId` and `InternalSubsts` (for within its trait). + // If the method can never be called from this object, produce `None`. + &*tcx.arena.alloc_from_iter(trait_methods.map(move |trait_method| { + debug!("vtable_methods: trait_method={:?}", trait_method); + let def_id = trait_method.def_id; + + // Some methods cannot be called on an object; skip those. + if !is_vtable_safe_method(tcx, trait_ref.def_id(), &trait_method) { + debug!("vtable_methods: not vtable safe"); + return None; + } - // The method may have some early-bound lifetimes; add regions for those. - let substs = trait_ref.map_bound(|trait_ref| { - InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { - trait_ref.substs[param.index as usize] - } - }) - }); - - // The trait type may have higher-ranked lifetimes in it; - // erase them if they appear, so that we get the type - // at some particular call site. - let substs = - tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &substs); - - // It's possible that the method relies on where-clauses that - // do not hold for this particular set of type parameters. - // Note that this method could then never be called, so we - // do not want to try and codegen it, in that case (see #23435). - let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); - if !normalize_and_test_predicates(tcx, predicates.predicates) { - debug!("vtable_methods: predicates do not hold"); - return None; - } + // The method may have some early-bound lifetimes; add regions for those. + let substs = trait_ref.map_bound(|trait_ref| { + InternalSubsts::for_item(tcx, def_id, |param, _| match param.kind { + GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + GenericParamDefKind::Type { .. } | GenericParamDefKind::Const => { + trait_ref.substs[param.index as usize] + } + }) + }); + + // The trait type may have higher-ranked lifetimes in it; + // erase them if they appear, so that we get the type + // at some particular call site. + let substs = + tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &substs); + + // It's possible that the method relies on where-clauses that + // do not hold for this particular set of type parameters. + // Note that this method could then never be called, so we + // do not want to try and codegen it, in that case (see #23435). + let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); + if !normalize_and_test_predicates(tcx, predicates.predicates) { + debug!("vtable_methods: predicates do not hold"); + return None; + } - Some((def_id, substs)) + Some((def_id, substs)) })) })) } diff --git a/src/librustc_trait_selection/traits/select.rs b/src/librustc_trait_selection/traits/select.rs index 6480d239023ef..6a450d25aee9d 100644 --- a/src/librustc_trait_selection/traits/select.rs +++ b/src/librustc_trait_selection/traits/select.rs @@ -1869,8 +1869,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { true } else { // Just allow upcast kinds 2 and 3 from above. - data_a.principal_def_id() == data_b.principal_def_id() && - data_b.auto_traits() + data_a.principal_def_id() == data_b.principal_def_id() + && data_b + .auto_traits() // All of a's auto traits need to be in b's auto traits. .all(|b| data_a.auto_traits().any(|a| a == b)) } @@ -2696,19 +2697,20 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // where we can unify, because otherwise select would have // reported an ambiguity. (When we do find a match, also // record it for later.) - let nonmatching = util::supertraits_with_repetitions(tcx, poly_trait_ref) - .take_while( - |&trait_ref| match self.infcx.commit_if_ok( - |_| self.match_poly_trait_ref(obligation, trait_ref) - ) { + let nonmatching = + util::supertraits_with_repetitions(tcx, poly_trait_ref).take_while(|&trait_ref| { + match self + .infcx + .commit_if_ok(|_| self.match_poly_trait_ref(obligation, trait_ref)) + { Ok(obligations) => { upcast_trait_ref = Some(trait_ref); nested.extend(obligations); false } Err(_) => true, - }, - ); + } + }); // Additionally, for each of the non-matching predicates that // we pass over, we sum up the set of number of vtable @@ -2716,7 +2718,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // trait. vtable_base = nonmatching // Skip 3 entries in vtable per supertrait for `(drop, size, align)` metadata. - .map(|trait_ref| util::count_own_vtable_entries(tcx, trait_ref).checked_add(3).unwrap()) + .map(|trait_ref| { + util::count_own_vtable_entries(tcx, trait_ref).checked_add(3).unwrap() + }) .sum(); } @@ -2956,16 +2960,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let iter = data_a .principal() .map(ty::ExistentialPredicate::Trait) - .into_iter().chain( + .into_iter() + .chain( data_a .projection_bounds() .map(|x| ty::ExistentialPredicate::Projection(x)), ) - .chain( - data_b - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait), - ); + .chain(data_b.auto_traits().map(ty::ExistentialPredicate::AutoTrait)); tcx.mk_existential_predicates(iter) }); let source_with_target_auto_traits = @@ -2995,7 +2996,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // (it doesn't matter which one we choose), but rather than resolve that in the // general case (which is subtle), we can screen it out here easily enough. nested.extend( - data_b.iter() + data_b + .iter() // HACK(alexreg | nikomatsakis): we handle auto traits specially here // because of cases like like `dyn Foo + Send + 'a` -> // `dyn Foo + Send + 'b`, which requires proving the obligation @@ -3003,16 +3005,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // current trait solver model: it holds both because `Send` is a // supertrait of `Foo + Send` and because there's an automatic impl of // `Send` for the trait object. - .filter(|predicate| { - match predicate.skip_binder() { - ty::ExistentialPredicate::AutoTrait(did) => - !data_a.auto_traits().any(|did_a| did_a == *did), - _ => true, + .filter(|predicate| match predicate.skip_binder() { + ty::ExistentialPredicate::AutoTrait(did) => { + !data_a.auto_traits().any(|did_a| did_a == *did) } + _ => true, }) - .map(|predicate| + .map(|predicate| { predicate_to_obligation(predicate.with_self_ty(tcx, source)) - ), + }), ); } else { // Require that the traits involved in this upcast are **equal**; @@ -3031,7 +3032,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // because I want to continue rejecting that test (as we have // done for quite some time) before we are firmly comfortable // with what our behavior should be there. -nikomatsakis - let InferOk { obligations, .. } = self.infcx + let InferOk { obligations, .. } = self + .infcx .at(&obligation.cause, obligation.param_env) .eq(target, source_with_target_auto_traits) // FIXME: see above. .map_err(|_| Unimplemented)?; @@ -3040,9 +3042,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // Register an obligation for `'a: 'b`. let outlives = ty::OutlivesPredicate(region_a, region_b); - nested.push(predicate_to_obligation( - ty::Binder::bind(outlives).to_predicate(), - )); + nested.push(predicate_to_obligation(ty::Binder::bind(outlives).to_predicate())); } // `T` -> `Trait` diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index faa40a98cec6c..3e528469f2b6c 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -63,11 +63,8 @@ pub trait AstConv<'tcx> { fn get_type_parameter_bounds(&self, span: Span, def_id: DefId) -> ty::GenericPredicates<'tcx>; /// Returns the lifetime to use when a lifetime is omitted (and not elided). - fn re_infer( - &self, - param: Option<&ty::GenericParamDef>, - span: Span, - ) -> Option>; + fn re_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) + -> Option>; /// Returns the type to use when a type is omitted. fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx>;