diff options
Diffstat (limited to 'compiler')
42 files changed, 1024 insertions, 313 deletions
diff --git a/compiler/rustc_ast/src/util/comments/tests.rs b/compiler/rustc_ast/src/util/comments/tests.rs index 61bb2468e79..f88b534a0c1 100644 --- a/compiler/rustc_ast/src/util/comments/tests.rs +++ b/compiler/rustc_ast/src/util/comments/tests.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + use rustc_span::create_default_session_globals_then; use super::*; diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index aa3b772efb1..61a710517ea 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -512,11 +512,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { ); gate_all!(let_chains, "`let` expressions in this position are unstable"); gate_all!( - async_closure, - "async closures are unstable", - "to use an async block, remove the `||`: `async {`" - ); - gate_all!( async_trait_bounds, "`async` trait bounds are unstable", "use the desugared name of the async trait, such as `AsyncFn`" diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 8b4523bd252..d62632d1431 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -17,7 +17,9 @@ use super::debuginfo::{ DebugEmissionKind, DebugNameTableKind, }; -pub type Bool = c_uint; +/// In the LLVM-C API, boolean values are passed as `typedef int LLVMBool`, +/// which has a different ABI from Rust or C++ `bool`. +pub type Bool = c_int; pub const True: Bool = 1 as Bool; pub const False: Bool = 0 as Bool; diff --git a/compiler/rustc_error_codes/src/error_codes/E0307.md b/compiler/rustc_error_codes/src/error_codes/E0307.md index 0d29d56ea1a..b9c0493e8d6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0307.md +++ b/compiler/rustc_error_codes/src/error_codes/E0307.md @@ -65,8 +65,10 @@ impl Trait for Foo { ``` The nightly feature [Arbitrary self types][AST] extends the accepted -set of receiver types to also include any type that can dereference to -`Self`: +set of receiver types to also include any type that implements the +`Receiver` trait and can follow its chain of `Target` types to `Self`. +There's a blanket implementation of `Receiver` for `T: Deref`, so any +type which dereferences to `Self` can be used. ``` #![feature(arbitrary_self_types)] diff --git a/compiler/rustc_error_codes/src/error_codes/E0708.md b/compiler/rustc_error_codes/src/error_codes/E0708.md index 61a853ac446..f793470bafd 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0708.md +++ b/compiler/rustc_error_codes/src/error_codes/E0708.md @@ -5,8 +5,6 @@ Erroneous code example: ```edition2018 -#![feature(async_closure)] - fn main() { let add_one = async |num: u8| { num + 1 @@ -18,8 +16,6 @@ fn main() { version, you can use successfully by using move: ```edition2018 -#![feature(async_closure)] - fn main() { let add_one = async move |num: u8| { // ok! num + 1 diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index fc44340851c..25d41462556 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -94,8 +94,7 @@ mod styled_buffer; mod tests; pub mod translation; -pub type PErr<'a> = Diag<'a>; -pub type PResult<'a, T> = Result<T, PErr<'a>>; +pub type PResult<'a, T> = Result<T, Diag<'a>>; rustc_fluent_macro::fluent_messages! { "../messages.ftl" } @@ -576,6 +575,10 @@ pub enum StashKey { UndeterminedMacroResolution, /// Used by `Parser::maybe_recover_trailing_expr` ExprInPat, + /// If in the parser we detect a field expr with turbofish generic params it's possible that + /// it's a method call without parens. If later on in `hir_typeck` we find out that this is + /// the case we suppress this message and we give a better suggestion. + GenericInFieldExpr, } fn default_track_diagnostic<R>(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R { diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs index 53362cb2529..c5913ed27cf 100644 --- a/compiler/rustc_feature/src/accepted.rs +++ b/compiler/rustc_feature/src/accepted.rs @@ -72,6 +72,8 @@ declare_features! ( (accepted, associated_types, "1.0.0", None), /// Allows free and inherent `async fn`s, `async` blocks, and `<expr>.await` expressions. (accepted, async_await, "1.39.0", Some(50547)), + /// Allows `async || body` closures. + (accepted, async_closure, "CURRENT_RUSTC_VERSION", Some(62290)), /// Allows async functions to be declared, implemented, and used in traits. (accepted, async_fn_in_trait, "1.75.0", Some(91611)), /// Allows all literals in attribute lists and values of key-value pairs. diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 93a605e197c..3c9115be7f5 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -388,8 +388,8 @@ declare_features! ( (unstable, associated_const_equality, "1.58.0", Some(92827)), /// Allows associated type defaults. (unstable, associated_type_defaults, "1.2.0", Some(29661)), - /// Allows `async || body` closures. - (unstable, async_closure, "1.37.0", Some(62290)), + /// Allows async functions to be called from `dyn Trait`. + (incomplete, async_fn_in_dyn_trait, "CURRENT_RUSTC_VERSION", Some(133119)), /// Allows `#[track_caller]` on async functions. (unstable, async_fn_track_caller, "1.73.0", Some(110011)), /// Allows `for await` loops. diff --git a/compiler/rustc_hir/src/tests.rs b/compiler/rustc_hir/src/tests.rs index 5c10fa46971..953e48a6d33 100644 --- a/compiler/rustc_hir/src/tests.rs +++ b/compiler/rustc_hir/src/tests.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + use rustc_data_structures::stable_hasher::Hash64; use rustc_span::def_id::{DefPathHash, StableCrateId}; use rustc_span::edition::Edition; diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 32498d9c5ab..25feb95d5df 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -241,10 +241,10 @@ hir_analysis_invalid_generic_receiver_ty_help = use a concrete type such as `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`) hir_analysis_invalid_receiver_ty = invalid `self` parameter type: `{$receiver_ty}` - .note = type of `self` must be `Self` or a type that dereferences to it + .note = type of `self` must be `Self` or some type implementing `Receiver` hir_analysis_invalid_receiver_ty_help = - consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`) + consider changing to `self`, `&self`, `&mut self`, or a type implementing `Receiver` such as `self: Box<Self>`, `self: Rc<Self>`, or `self: Arc<Self>` hir_analysis_invalid_union_field = field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be used in a union diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs index 5a66c31a0cc..d8e9227a87c 100644 --- a/compiler/rustc_hir_analysis/src/autoderef.rs +++ b/compiler/rustc_hir_analysis/src/autoderef.rs @@ -18,7 +18,6 @@ pub enum AutoderefKind { /// A type which must dispatch to a `Deref` implementation. Overloaded, } - struct AutoderefSnapshot<'tcx> { at_start: bool, reached_recursion_limit: bool, @@ -27,6 +26,10 @@ struct AutoderefSnapshot<'tcx> { obligations: PredicateObligations<'tcx>, } +/// Recursively dereference a type, considering both built-in +/// dereferences (`*`) and the `Deref` trait. +/// Although called `Autoderef` it can be configured to use the +/// `Receiver` trait instead of the `Deref` trait. pub struct Autoderef<'a, 'tcx> { // Meta infos: infcx: &'a InferCtxt<'tcx>, @@ -39,6 +42,7 @@ pub struct Autoderef<'a, 'tcx> { // Configurations: include_raw_pointers: bool, + use_receiver_trait: bool, silence_errors: bool, } @@ -69,6 +73,10 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> { } // Otherwise, deref if type is derefable: + // NOTE: in the case of self.use_receiver_trait = true, you might think it would + // be better to skip this clause and use the Overloaded case only, since &T + // and &mut T implement Receiver. But built-in derefs apply equally to Receiver + // and Deref, and this has benefits for const and the emitted MIR. let (kind, new_ty) = if let Some(ty) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) { debug_assert_eq!(ty, self.infcx.resolve_vars_if_possible(ty)); @@ -111,7 +119,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { body_def_id: LocalDefId, span: Span, base_ty: Ty<'tcx>, - ) -> Autoderef<'a, 'tcx> { + ) -> Self { Autoderef { infcx, span, @@ -125,6 +133,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { reached_recursion_limit: false, }, include_raw_pointers: false, + use_receiver_trait: false, silence_errors: false, } } @@ -137,8 +146,13 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { return None; } - // <ty as Deref> - let trait_ref = ty::TraitRef::new(tcx, tcx.lang_items().deref_trait()?, [ty]); + // <ty as Deref>, or whatever the equivalent trait is that we've been asked to walk. + let (trait_def_id, trait_target_def_id) = if self.use_receiver_trait { + (tcx.lang_items().receiver_trait()?, tcx.lang_items().receiver_target()?) + } else { + (tcx.lang_items().deref_trait()?, tcx.lang_items().deref_target()?) + }; + let trait_ref = ty::TraitRef::new(tcx, trait_def_id, [ty]); let cause = traits::ObligationCause::misc(self.span, self.body_id); let obligation = traits::Obligation::new( tcx, @@ -151,11 +165,8 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { return None; } - let (normalized_ty, obligations) = self.structurally_normalize(Ty::new_projection( - tcx, - tcx.lang_items().deref_target()?, - [ty], - ))?; + let (normalized_ty, obligations) = + self.structurally_normalize(Ty::new_projection(tcx, trait_target_def_id, [ty]))?; debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); self.state.obligations.extend(obligations); @@ -234,6 +245,14 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { self } + /// Use `core::ops::Receiver` and `core::ops::Receiver::Target` as + /// the trait and associated type to iterate, instead of + /// `core::ops::Deref` and `core::ops::Deref::Target` + pub fn use_receiver_trait(mut self) -> Self { + self.use_receiver_trait = true; + self + } + pub fn silence_errors(mut self) -> Self { self.silence_errors = true; self diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index df4da03f0f5..90e93bdbb50 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -3,6 +3,7 @@ use std::assert_matches::debug_assert_matches; use rustc_abi::FieldIdx; use rustc_ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; use rustc_middle::bug; use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy}; @@ -21,6 +22,12 @@ pub struct InlineAsmCtxt<'a, 'tcx> { get_operand_ty: Box<dyn Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a>, } +enum NonAsmTypeReason<'tcx> { + UnevaluatedSIMDArrayLength(DefId, ty::Const<'tcx>), + Invalid(Ty<'tcx>), + InvalidElement(DefId, Ty<'tcx>), +} + impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self { InlineAsmCtxt { @@ -56,7 +63,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { false } - fn get_asm_ty(&self, ty: Ty<'tcx>) -> Option<InlineAsmType> { + fn get_asm_ty(&self, ty: Ty<'tcx>) -> Result<InlineAsmType, NonAsmTypeReason<'tcx>> { let asm_ty_isize = match self.tcx.sess.target.pointer_width { 16 => InlineAsmType::I16, 32 => InlineAsmType::I32, @@ -65,64 +72,62 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { }; match *ty.kind() { - ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8), - ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16), - ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32), - ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64), - ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128), - ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize), - ty::Float(FloatTy::F16) => Some(InlineAsmType::F16), - ty::Float(FloatTy::F32) => Some(InlineAsmType::F32), - ty::Float(FloatTy::F64) => Some(InlineAsmType::F64), - ty::Float(FloatTy::F128) => Some(InlineAsmType::F128), - ty::FnPtr(..) => Some(asm_ty_isize), - ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Some(asm_ty_isize), + ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::I8), + ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::I16), + ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::I32), + ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::I64), + ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Ok(InlineAsmType::I128), + ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Ok(asm_ty_isize), + ty::Float(FloatTy::F16) => Ok(InlineAsmType::F16), + ty::Float(FloatTy::F32) => Ok(InlineAsmType::F32), + ty::Float(FloatTy::F64) => Ok(InlineAsmType::F64), + ty::Float(FloatTy::F128) => Ok(InlineAsmType::F128), + ty::FnPtr(..) => Ok(asm_ty_isize), + ty::RawPtr(ty, _) if self.is_thin_ptr_ty(ty) => Ok(asm_ty_isize), ty::Adt(adt, args) if adt.repr().simd() => { let fields = &adt.non_enum_variant().fields; - let elem_ty = fields[FieldIdx::ZERO].ty(self.tcx, args); + let field = &fields[FieldIdx::ZERO]; + let elem_ty = field.ty(self.tcx, args); let (size, ty) = match elem_ty.kind() { ty::Array(ty, len) => { + let len = self.tcx.normalize_erasing_regions(self.typing_env, *len); if let Some(len) = len.try_to_target_usize(self.tcx) { (len, *ty) } else { - return None; + return Err(NonAsmTypeReason::UnevaluatedSIMDArrayLength( + field.did, len, + )); } } _ => (fields.len() as u64, elem_ty), }; match ty.kind() { - ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::VecI8(size)), - ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => { - Some(InlineAsmType::VecI16(size)) - } - ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => { - Some(InlineAsmType::VecI32(size)) - } - ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => { - Some(InlineAsmType::VecI64(size)) - } + ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Ok(InlineAsmType::VecI8(size)), + ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Ok(InlineAsmType::VecI16(size)), + ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Ok(InlineAsmType::VecI32(size)), + ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Ok(InlineAsmType::VecI64(size)), ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => { - Some(InlineAsmType::VecI128(size)) + Ok(InlineAsmType::VecI128(size)) } ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => { - Some(match self.tcx.sess.target.pointer_width { + Ok(match self.tcx.sess.target.pointer_width { 16 => InlineAsmType::VecI16(size), 32 => InlineAsmType::VecI32(size), 64 => InlineAsmType::VecI64(size), width => bug!("unsupported pointer width: {width}"), }) } - ty::Float(FloatTy::F16) => Some(InlineAsmType::VecF16(size)), - ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(size)), - ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(size)), - ty::Float(FloatTy::F128) => Some(InlineAsmType::VecF128(size)), - _ => None, + ty::Float(FloatTy::F16) => Ok(InlineAsmType::VecF16(size)), + ty::Float(FloatTy::F32) => Ok(InlineAsmType::VecF32(size)), + ty::Float(FloatTy::F64) => Ok(InlineAsmType::VecF64(size)), + ty::Float(FloatTy::F128) => Ok(InlineAsmType::VecF128(size)), + _ => Err(NonAsmTypeReason::InvalidElement(field.did, ty)), } } ty::Infer(_) => bug!("unexpected infer ty in asm operand"), - _ => None, + _ => Err(NonAsmTypeReason::Invalid(ty)), } } @@ -163,17 +168,42 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } _ => self.get_asm_ty(ty), }; - let Some(asm_ty) = asm_ty else { - let msg = format!("cannot use value of type `{ty}` for inline assembly"); - self.tcx - .dcx() - .struct_span_err(expr.span, msg) - .with_note( - "only integers, floats, SIMD vectors, pointers and function pointers \ - can be used as arguments for inline assembly", - ) - .emit(); - return None; + let asm_ty = match asm_ty { + Ok(asm_ty) => asm_ty, + Err(reason) => { + match reason { + NonAsmTypeReason::UnevaluatedSIMDArrayLength(did, len) => { + let msg = format!("cannot evaluate SIMD vector length `{len}`"); + self.tcx + .dcx() + .struct_span_err(self.tcx.def_span(did), msg) + .with_span_note( + expr.span, + "SIMD vector length needs to be known statically for use in `asm!`", + ) + .emit(); + } + NonAsmTypeReason::Invalid(ty) => { + let msg = format!("cannot use value of type `{ty}` for inline assembly"); + self.tcx.dcx().struct_span_err(expr.span, msg).with_note( + "only integers, floats, SIMD vectors, pointers and function pointers \ + can be used as arguments for inline assembly", + ).emit(); + } + NonAsmTypeReason::InvalidElement(did, ty) => { + let msg = format!( + "cannot use SIMD vector with element type `{ty}` for inline assembly" + ); + self.tcx.dcx() + .struct_span_err(self.tcx.def_span(did), msg).with_span_note( + expr.span, + "only integers, floats, SIMD vectors, pointers and function pointers \ + can be used as arguments for inline assembly", + ).emit(); + } + } + return None; + } }; // Check that the type implements Copy. The only case where this can diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index c9773972d9a..57264d0bd2a 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1821,13 +1821,18 @@ fn receiver_is_valid<'tcx>( let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty); + // The `arbitrary_self_types` feature allows custom smart pointer + // types to be method receivers, as identified by following the Receiver<Target=T> + // chain. + if arbitrary_self_types_enabled.is_some() { + autoderef = autoderef.use_receiver_trait(); + } + // The `arbitrary_self_types_pointers` feature allows raw pointer receivers like `self: *const Self`. if arbitrary_self_types_enabled == Some(ArbitrarySelfTypesLevel::WithPointers) { autoderef = autoderef.include_raw_pointers(); } - let receiver_trait_def_id = tcx.require_lang_item(LangItem::LegacyReceiver, Some(span)); - // Keep dereferencing `receiver_ty` until we get to `self_ty`. while let Some((potential_self_ty, _)) = autoderef.next() { debug!( @@ -1849,11 +1854,13 @@ fn receiver_is_valid<'tcx>( } // Without `feature(arbitrary_self_types)`, we require that each step in the - // deref chain implement `receiver`. + // deref chain implement `LegacyReceiver`. if arbitrary_self_types_enabled.is_none() { - if !receiver_is_implemented( + let legacy_receiver_trait_def_id = + tcx.require_lang_item(LangItem::LegacyReceiver, Some(span)); + if !legacy_receiver_is_implemented( wfcx, - receiver_trait_def_id, + legacy_receiver_trait_def_id, cause.clone(), potential_self_ty, ) { @@ -1866,7 +1873,7 @@ fn receiver_is_valid<'tcx>( cause.clone(), wfcx.param_env, potential_self_ty, - receiver_trait_def_id, + legacy_receiver_trait_def_id, ); } } @@ -1875,14 +1882,14 @@ fn receiver_is_valid<'tcx>( Err(ReceiverValidityError::DoesNotDeref) } -fn receiver_is_implemented<'tcx>( +fn legacy_receiver_is_implemented<'tcx>( wfcx: &WfCheckingCtxt<'_, 'tcx>, - receiver_trait_def_id: DefId, + legacy_receiver_trait_def_id: DefId, cause: ObligationCause<'tcx>, receiver_ty: Ty<'tcx>, ) -> bool { let tcx = wfcx.tcx(); - let trait_ref = ty::TraitRef::new(tcx, receiver_trait_def_id, [receiver_ty]); + let trait_ref = ty::TraitRef::new(tcx, legacy_receiver_trait_def_id, [receiver_ty]); let obligation = Obligation::new(tcx, cause, wfcx.param_env, trait_ref); @@ -1890,7 +1897,7 @@ fn receiver_is_implemented<'tcx>( true } else { debug!( - "receiver_is_implemented: type `{:?}` does not implement `Receiver` trait", + "receiver_is_implemented: type `{:?}` does not implement `LegacyReceiver` trait", receiver_ty ); false diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 3399a9fe880..d0272651c08 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -917,7 +917,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { [candidate] => format!( "the method of the same name on {} `{}`", match candidate.kind { - probe::CandidateKind::InherentImplCandidate(_) => "the inherent impl for", + probe::CandidateKind::InherentImplCandidate { .. } => "the inherent impl for", _ => "trait", }, self.tcx.def_path_str(candidate.item.container_id(self.tcx)) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index b3ce1df3def..56903865277 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3076,7 +3076,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help("methods are immutable and cannot be assigned to"); } - err.emit() + // See `StashKey::GenericInFieldExpr` for more info + self.dcx().try_steal_replace_and_emit_err(field.span, StashKey::GenericInFieldExpr, err) } fn point_at_param_definition(&self, err: &mut Diag<'_>, param: ty::ParamTy) { diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index ddd146fe785..b57c248ed2b 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -613,19 +613,16 @@ impl<'tcx> AnnotateUnitFallbackVisitor<'_, 'tcx> { if arg_segment.args.is_none() && let Some(all_args) = self.fcx.typeck_results.borrow().node_args_opt(id) && let generics = self.fcx.tcx.generics_of(def_id) - && let args = &all_args[generics.parent_count..] + && let args = all_args[generics.parent_count..].iter().zip(&generics.own_params) // We can't turbofish consts :( - && args.iter().all(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_) | ty::GenericArgKind::Lifetime(_))) + && args.clone().all(|(_, param)| matches!(param.kind, ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Lifetime)) { - let n_tys = args - .iter() - .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_))) - .count(); - for (idx, arg) in args - .iter() - .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_))) - .enumerate() - { + // We filter out APITs, which are not turbofished. + let non_apit_type_args = args.filter(|(_, param)| { + matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: false, .. }) + }); + let n_tys = non_apit_type_args.clone().count(); + for (idx, (arg, _)) in non_apit_type_args.enumerate() { if let Some(ty) = arg.as_type() && let Some(vid) = self.fcx.root_vid(ty) && self.reachable_vids.contains(&vid) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 039c117c099..3b377076545 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -79,12 +79,6 @@ pub(crate) struct ProbeContext<'a, 'tcx> { /// used for error reporting static_candidates: RefCell<Vec<CandidateSource>>, - /// Collects near misses when trait bounds for type parameters are unsatisfied and is only used - /// for error reporting - unsatisfied_predicates: RefCell< - Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>, - >, - scope_expr_id: HirId, /// Is this probe being done for a diagnostic? This will skip some error reporting @@ -109,7 +103,7 @@ pub(crate) struct Candidate<'tcx> { #[derive(Debug, Clone)] pub(crate) enum CandidateKind<'tcx> { - InherentImplCandidate(DefId), + InherentImplCandidate { impl_def_id: DefId, receiver_steps: usize }, ObjectCandidate(ty::PolyTraitRef<'tcx>), TraitCandidate(ty::PolyTraitRef<'tcx>), WhereClauseCandidate(ty::PolyTraitRef<'tcx>), @@ -162,6 +156,52 @@ impl AutorefOrPtrAdjustment { } } +/// Extra information required only for error reporting. +#[derive(Debug)] +struct PickDiagHints<'a, 'tcx> { + /// Unstable candidates alongside the stable ones. + unstable_candidates: Option<Vec<(Candidate<'tcx>, Symbol)>>, + + /// Collects near misses when trait bounds for type parameters are unsatisfied and is only used + /// for error reporting + unsatisfied_predicates: &'a mut Vec<( + ty::Predicate<'tcx>, + Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, + )>, +} + +/// Criteria to apply when searching for a given Pick. This is used during +/// the search for potentially shadowed methods to ensure we don't search +/// more candidates than strictly necessary. +#[derive(Debug)] +struct PickConstraintsForShadowed { + autoderefs: usize, + receiver_steps: Option<usize>, + def_id: DefId, +} + +impl PickConstraintsForShadowed { + fn may_shadow_based_on_autoderefs(&self, autoderefs: usize) -> bool { + autoderefs == self.autoderefs + } + + fn candidate_may_shadow(&self, candidate: &Candidate<'_>) -> bool { + // An item never shadows itself + candidate.item.def_id != self.def_id + // and we're only concerned about inherent impls doing the shadowing. + // Shadowing can only occur if the shadowed is further along + // the Receiver dereferencing chain than the shadowed. + && match candidate.kind { + CandidateKind::InherentImplCandidate { receiver_steps, .. } => match self.receiver_steps { + Some(shadowed_receiver_steps) => receiver_steps > shadowed_receiver_steps, + _ => false + }, + _ => false + } + } +} + #[derive(Debug, Clone)] pub(crate) struct Pick<'tcx> { pub item: ty::AssocItem, @@ -181,6 +221,11 @@ pub(crate) struct Pick<'tcx> { /// Unstable candidates alongside the stable ones. unstable_candidates: Vec<(Candidate<'tcx>, Symbol)>, + + /// Number of jumps along the `Receiver::Target` chain we followed + /// to identify this method. Used only for deshadowing errors. + /// Only applies for inherent impls. + pub receiver_steps: Option<usize>, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -366,6 +411,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { autoderefs: 0, from_unsafe_deref: false, unsize: false, + reachable_via_deref: true, }]), opt_bad_ty: None, reached_recursion_limit: false, @@ -516,47 +562,93 @@ fn method_autoderef_steps<'tcx>( let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); let ParamEnvAnd { param_env, value: self_ty } = goal; - let mut autoderef = + // If arbitrary self types is not enabled, we follow the chain of + // `Deref<Target=T>`. If arbitrary self types is enabled, we instead + // follow the chain of `Receiver<Target=T>`, but we also record whether + // such types are reachable by following the (potentially shorter) + // chain of `Deref<Target=T>`. We will use the first list when finding + // potentially relevant function implementations (e.g. relevant impl blocks) + // but the second list when determining types that the receiver may be + // converted to, in order to find out which of those methods might actually + // be callable. + let mut autoderef_via_deref = Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty) .include_raw_pointers() .silence_errors(); - let mut reached_raw_pointer = false; - let mut steps: Vec<_> = autoderef - .by_ref() - .map(|(ty, d)| { - let step = CandidateStep { - self_ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, ty), - autoderefs: d, - from_unsafe_deref: reached_raw_pointer, - unsize: false, - }; - if let ty::RawPtr(_, _) = ty.kind() { - // all the subsequent steps will be from_unsafe_deref - reached_raw_pointer = true; - } - step - }) - .collect(); - let final_ty = autoderef.final_ty(true); + let mut reached_raw_pointer = false; + let arbitrary_self_types_enabled = + tcx.features().arbitrary_self_types() || tcx.features().arbitrary_self_types_pointers(); + let (mut steps, reached_recursion_limit): (Vec<_>, bool) = if arbitrary_self_types_enabled { + let reachable_via_deref = + autoderef_via_deref.by_ref().map(|_| true).chain(std::iter::repeat(false)); + + let mut autoderef_via_receiver = + Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty) + .include_raw_pointers() + .use_receiver_trait() + .silence_errors(); + let steps = autoderef_via_receiver + .by_ref() + .zip(reachable_via_deref) + .map(|((ty, d), reachable_via_deref)| { + let step = CandidateStep { + self_ty: infcx + .make_query_response_ignoring_pending_obligations(inference_vars, ty), + autoderefs: d, + from_unsafe_deref: reached_raw_pointer, + unsize: false, + reachable_via_deref, + }; + if ty.is_unsafe_ptr() { + // all the subsequent steps will be from_unsafe_deref + reached_raw_pointer = true; + } + step + }) + .collect(); + (steps, autoderef_via_receiver.reached_recursion_limit()) + } else { + let steps = autoderef_via_deref + .by_ref() + .map(|(ty, d)| { + let step = CandidateStep { + self_ty: infcx + .make_query_response_ignoring_pending_obligations(inference_vars, ty), + autoderefs: d, + from_unsafe_deref: reached_raw_pointer, + unsize: false, + reachable_via_deref: true, + }; + if ty.is_unsafe_ptr() { + // all the subsequent steps will be from_unsafe_deref + reached_raw_pointer = true; + } + step + }) + .collect(); + (steps, autoderef_via_deref.reached_recursion_limit()) + }; + let final_ty = autoderef_via_deref.final_ty(true); let opt_bad_ty = match final_ty.kind() { ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { reached_raw_pointer, ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), }), ty::Array(elem_ty, _) => { - let dereferences = steps.len() - 1; - + let autoderefs = steps.iter().filter(|s| s.reachable_via_deref).count() - 1; steps.push(CandidateStep { self_ty: infcx.make_query_response_ignoring_pending_obligations( inference_vars, Ty::new_slice(infcx.tcx, *elem_ty), ), - autoderefs: dereferences, + autoderefs, // this could be from an unsafe deref if we had // a *mut/const [T; N] from_unsafe_deref: reached_raw_pointer, unsize: true, + reachable_via_deref: true, // this is always the final type from + // autoderef_via_deref }); None @@ -569,7 +661,7 @@ fn method_autoderef_steps<'tcx>( MethodAutoderefStepsResult { steps: tcx.arena.alloc_from_iter(steps), opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), - reached_recursion_limit: autoderef.reached_recursion_limit(), + reached_recursion_limit, } } @@ -600,7 +692,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { private_candidates: Vec::new(), private_candidate: Cell::new(None), static_candidates: RefCell::new(Vec::new()), - unsatisfied_predicates: RefCell::new(Vec::new()), scope_expr_id, is_suggestion, } @@ -613,7 +704,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.private_candidates.clear(); self.private_candidate.set(None); self.static_candidates.borrow_mut().clear(); - self.unsatisfied_predicates.borrow_mut().clear(); } /// When we're looking up a method by path (UFCS), we relate the receiver @@ -652,12 +742,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn assemble_inherent_candidates(&mut self) { for step in self.steps.iter() { - self.assemble_probe(&step.self_ty); + self.assemble_probe(&step.self_ty, step.autoderefs); } } #[instrument(level = "debug", skip(self))] - fn assemble_probe(&mut self, self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>) { + fn assemble_probe( + &mut self, + self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, + receiver_steps: usize, + ) { let raw_self_ty = self_ty.value.value; match *raw_self_ty.kind() { ty::Dynamic(data, ..) if let Some(p) = data.principal() => { @@ -682,22 +776,31 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.fcx.instantiate_canonical(self.span, self_ty); self.assemble_inherent_candidates_from_object(generalized_self_ty); - self.assemble_inherent_impl_candidates_for_type(p.def_id()); + self.assemble_inherent_impl_candidates_for_type(p.def_id(), receiver_steps); if self.tcx.has_attr(p.def_id(), sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); + self.assemble_inherent_candidates_for_incoherent_ty( + raw_self_ty, + receiver_steps, + ); } } ty::Adt(def, _) => { let def_id = def.did(); - self.assemble_inherent_impl_candidates_for_type(def_id); + self.assemble_inherent_impl_candidates_for_type(def_id, receiver_steps); if self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); + self.assemble_inherent_candidates_for_incoherent_ty( + raw_self_ty, + receiver_steps, + ); } } ty::Foreign(did) => { - self.assemble_inherent_impl_candidates_for_type(did); + self.assemble_inherent_impl_candidates_for_type(did, receiver_steps); if self.tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); + self.assemble_inherent_candidates_for_incoherent_ty( + raw_self_ty, + receiver_steps, + ); } } ty::Param(p) => { @@ -714,29 +817,35 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { | ty::RawPtr(_, _) | ty::Ref(..) | ty::Never - | ty::Tuple(..) => self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty), + | ty::Tuple(..) => { + self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty, receiver_steps) + } _ => {} } } - fn assemble_inherent_candidates_for_incoherent_ty(&mut self, self_ty: Ty<'tcx>) { + fn assemble_inherent_candidates_for_incoherent_ty( + &mut self, + self_ty: Ty<'tcx>, + receiver_steps: usize, + ) { let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::InstantiateWithInfer) else { bug!("unexpected incoherent type: {:?}", self_ty) }; for &impl_def_id in self.tcx.incoherent_impls(simp).into_iter() { - self.assemble_inherent_impl_probe(impl_def_id); + self.assemble_inherent_impl_probe(impl_def_id, receiver_steps); } } - fn assemble_inherent_impl_candidates_for_type(&mut self, def_id: DefId) { + fn assemble_inherent_impl_candidates_for_type(&mut self, def_id: DefId, receiver_steps: usize) { let impl_def_ids = self.tcx.at(self.span).inherent_impls(def_id).into_iter(); for &impl_def_id in impl_def_ids { - self.assemble_inherent_impl_probe(impl_def_id); + self.assemble_inherent_impl_probe(impl_def_id, receiver_steps); } } #[instrument(level = "debug", skip(self))] - fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId) { + fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId, receiver_steps: usize) { if !self.impl_dups.insert(impl_def_id) { return; // already visited } @@ -750,7 +859,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.push_candidate( Candidate { item, - kind: InherentImplCandidate(impl_def_id), + kind: InherentImplCandidate { impl_def_id, receiver_steps }, import_ids: smallvec![], }, true, @@ -989,7 +1098,9 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn pick(mut self) -> PickResult<'tcx> { assert!(self.method_name.is_some()); - if let Some(r) = self.pick_core() { + let mut unsatisfied_predicates = Vec::new(); + + if let Some(r) = self.pick_core(&mut unsatisfied_predicates) { return r; } @@ -1009,7 +1120,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let static_candidates = std::mem::take(self.static_candidates.get_mut()); let private_candidate = self.private_candidate.take(); - let unsatisfied_predicates = std::mem::take(self.unsatisfied_predicates.get_mut()); // things failed, so lets look at all traits, for diagnostic purposes now: self.reset(); @@ -1019,7 +1129,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { self.assemble_extension_candidates_for_all_traits(); - let out_of_scope_traits = match self.pick_core() { + let out_of_scope_traits = match self.pick_core(&mut Vec::new()) { Some(Ok(p)) => vec![p.item.container_id(self.tcx)], Some(Err(MethodError::Ambiguity(v))) => v .into_iter() @@ -1054,17 +1164,48 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { })) } - fn pick_core(&self) -> Option<PickResult<'tcx>> { + fn pick_core( + &self, + unsatisfied_predicates: &mut Vec<( + ty::Predicate<'tcx>, + Option<ty::Predicate<'tcx>>, + Option<ObligationCause<'tcx>>, + )>, + ) -> Option<PickResult<'tcx>> { // Pick stable methods only first, and consider unstable candidates if not found. - self.pick_all_method(Some(&mut vec![])).or_else(|| self.pick_all_method(None)) + self.pick_all_method(&mut PickDiagHints { + // This first cycle, maintain a list of unstable candidates which + // we encounter. This will end up in the Pick for diagnostics. + unstable_candidates: Some(Vec::new()), + // Contribute to the list of unsatisfied predicates which may + // also be used for diagnostics. + unsatisfied_predicates, + }) + .or_else(|| { + self.pick_all_method(&mut PickDiagHints { + // On the second search, don't provide a special list of unstable + // candidates. This indicates to the picking code that it should + // in fact include such unstable candidates in the actual + // search. + unstable_candidates: None, + // And there's no need to duplicate ourselves in the + // unsatisifed predicates list. Provide a throwaway list. + unsatisfied_predicates: &mut Vec::new(), + }) + }) } - fn pick_all_method( + fn pick_all_method<'b>( &self, - mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'b, 'tcx>, ) -> Option<PickResult<'tcx>> { + let track_unstable_candidates = pick_diag_hints.unstable_candidates.is_some(); self.steps .iter() + // At this point we're considering the types to which the receiver can be converted, + // so we want to follow the `Deref` chain not the `Receiver` chain. Filter out + // steps which can only be reached by following the (longer) `Receiver` chain. + .filter(|step| step.reachable_via_deref) .filter(|step| { debug!("pick_all_method: step={:?}", step); // skip types that are from a type error or that would require dereferencing @@ -1082,40 +1223,188 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { .unwrap_or_else(|_| { span_bug!(self.span, "{:?} was applicable but now isn't?", step.self_ty) }); - self.pick_by_value_method(step, self_ty, unstable_candidates.as_deref_mut()) - .or_else(|| { - self.pick_autorefd_method( - step, - self_ty, - hir::Mutability::Not, - unstable_candidates.as_deref_mut(), - ) - .or_else(|| { - self.pick_autorefd_method( + + let by_value_pick = self.pick_by_value_method(step, self_ty, pick_diag_hints); + + // Check for shadowing of a by-reference method by a by-value method (see comments on check_for_shadowing) + if let Some(by_value_pick) = by_value_pick { + if let Ok(by_value_pick) = by_value_pick.as_ref() { + if by_value_pick.kind == PickKind::InherentImplPick { + if let Err(e) = self.check_for_shadowed_autorefd_method( + by_value_pick, step, self_ty, - hir::Mutability::Mut, - unstable_candidates.as_deref_mut(), - ) - }) - .or_else(|| { - self.pick_const_ptr_method( + hir::Mutability::Not, + track_unstable_candidates, + ) { + return Some(Err(e)); + } + if let Err(e) = self.check_for_shadowed_autorefd_method( + by_value_pick, step, self_ty, - unstable_candidates.as_deref_mut(), - ) - }) - .or_else(|| { - self.pick_reborrow_pin_method( + hir::Mutability::Mut, + track_unstable_candidates, + ) { + return Some(Err(e)); + } + } + } + return Some(by_value_pick); + } + + let autoref_pick = self.pick_autorefd_method( + step, + self_ty, + hir::Mutability::Not, + pick_diag_hints, + None, + ); + // Check for shadowing of a by-mut-ref method by a by-reference method (see comments on check_for_shadowing) + if let Some(autoref_pick) = autoref_pick { + if let Ok(autoref_pick) = autoref_pick.as_ref() { + // Check we're not shadowing others + if autoref_pick.kind == PickKind::InherentImplPick { + if let Err(e) = self.check_for_shadowed_autorefd_method( + autoref_pick, step, self_ty, - unstable_candidates.as_deref_mut(), - ) - }) - }) + hir::Mutability::Mut, + track_unstable_candidates, + ) { + return Some(Err(e)); + } + } + } + return Some(autoref_pick); + } + + // Note that no shadowing errors are produced from here on, + // as we consider const ptr methods. + // We allow new methods that take *mut T to shadow + // methods which took *const T, so there is no entry in + // this list for the results of `pick_const_ptr_method`. + // The reason is that the standard pointer cast method + // (on a mutable pointer) always already shadows the + // cast method (on a const pointer). So, if we added + // `pick_const_ptr_method` to this method, the anti- + // shadowing algorithm would always complain about + // the conflict between *const::cast and *mut::cast. + // In practice therefore this does constrain us: + // we cannot add new + // self: *mut Self + // methods to types such as NonNull or anything else + // which implements Receiver, because this might in future + // shadow existing methods taking + // self: *const NonNull<Self> + // in the pointee. In practice, methods taking raw pointers + // are rare, and it seems that it should be easily possible + // to avoid such compatibility breaks. + // We also don't check for reborrowed pin methods which + // may be shadowed; these also seem unlikely to occur. + self.pick_autorefd_method( + step, + self_ty, + hir::Mutability::Mut, + pick_diag_hints, + None, + ) + .or_else(|| self.pick_const_ptr_method(step, self_ty, pick_diag_hints)) + .or_else(|| self.pick_reborrow_pin_method(step, self_ty, pick_diag_hints)) }) } + /// Check for cases where arbitrary self types allows shadowing + /// of methods that might be a compatibility break. Specifically, + /// we have something like: + /// ```ignore (illustrative) + /// struct A; + /// impl A { + /// fn foo(self: &NonNull<A>) {} + /// // note this is by reference + /// } + /// ``` + /// then we've come along and added this method to `NonNull`: + /// ```ignore (illustrative) + /// fn foo(self) // note this is by value + /// ``` + /// Report an error in this case. + fn check_for_shadowed_autorefd_method( + &self, + possible_shadower: &Pick<'tcx>, + step: &CandidateStep<'tcx>, + self_ty: Ty<'tcx>, + mutbl: hir::Mutability, + track_unstable_candidates: bool, + ) -> Result<(), MethodError<'tcx>> { + // We don't want to remember any of the diagnostic hints from this + // shadow search, but we do need to provide Some/None for the + // unstable_candidates in order to reflect the behavior of the + // main search. + let mut pick_diag_hints = PickDiagHints { + unstable_candidates: if track_unstable_candidates { Some(Vec::new()) } else { None }, + unsatisfied_predicates: &mut Vec::new(), + }; + // Set criteria for how we find methods possibly shadowed by 'possible_shadower' + let pick_constraints = PickConstraintsForShadowed { + // It's the same `self` type... + autoderefs: possible_shadower.autoderefs, + // ... but the method was found in an impl block determined + // by searching further along the Receiver chain than the other, + // showing that it's a smart pointer type causing the problem... + receiver_steps: possible_shadower.receiver_steps, + // ... and they don't end up pointing to the same item in the + // first place (could happen with things like blanket impls for T) + def_id: possible_shadower.item.def_id, + }; + // A note on the autoderefs above. Within pick_by_value_method, an extra + // autoderef may be applied in order to reborrow a reference with + // a different lifetime. That seems as though it would break the + // logic of these constraints, since the number of autoderefs could + // no longer be used to identify the fundamental type of the receiver. + // However, this extra autoderef is applied only to by-value calls + // where the receiver is already a reference. So this situation would + // only occur in cases where the shadowing looks like this: + // ``` + // struct A; + // impl A { + // fn foo(self: &&NonNull<A>) {} + // // note this is by DOUBLE reference + // } + // ``` + // then we've come along and added this method to `NonNull`: + // ``` + // fn foo(&self) // note this is by single reference + // ``` + // and the call is: + // ``` + // let bar = NonNull<Foo>; + // let bar = &foo; + // bar.foo(); + // ``` + // In these circumstances, the logic is wrong, and we wouldn't spot + // the shadowing, because the autoderef-based maths wouldn't line up. + // This is a niche case and we can live without generating an error + // in the case of such shadowing. + let potentially_shadowed_pick = self.pick_autorefd_method( + step, + self_ty, + mutbl, + &mut pick_diag_hints, + Some(&pick_constraints), + ); + // Look for actual pairs of shadower/shadowed which are + // the sort of shadowing case we want to avoid. Specifically... + if let Some(Ok(possible_shadowed)) = potentially_shadowed_pick.as_ref() { + let sources = [possible_shadower, possible_shadowed] + .into_iter() + .map(|p| self.candidate_source_from_pick(p)) + .collect(); + return Err(MethodError::Ambiguity(sources)); + } + Ok(()) + } + /// For each type `T` in the step list, this attempts to find a method where /// the (transformed) self type is exactly `T`. We do however do one /// transformation on the adjustment: if we are passing a region pointer in, @@ -1126,13 +1415,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, ) -> Option<PickResult<'tcx>> { if step.unsize { return None; } - self.pick_method(self_ty, unstable_candidates).map(|r| { + self.pick_method(self_ty, pick_diag_hints, None).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; @@ -1170,15 +1459,22 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, mutbl: hir::Mutability, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, + pick_constraints: Option<&PickConstraintsForShadowed>, ) -> Option<PickResult<'tcx>> { let tcx = self.tcx; + if let Some(pick_constraints) = pick_constraints { + if !pick_constraints.may_shadow_based_on_autoderefs(step.autoderefs) { + return None; + } + } + // In general, during probing we erase regions. let region = tcx.lifetimes.re_erased; let autoref_ty = Ty::new_ref(tcx, region, self_ty, mutbl); - self.pick_method(autoref_ty, unstable_candidates).map(|r| { + self.pick_method(autoref_ty, pick_diag_hints, pick_constraints).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; pick.autoref_or_ptr_adjustment = @@ -1189,12 +1485,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } /// Looks for applicable methods if we reborrow a `Pin<&mut T>` as a `Pin<&T>`. - #[instrument(level = "debug", skip(self, step, unstable_candidates))] + #[instrument(level = "debug", skip(self, step, pick_diag_hints))] fn pick_reborrow_pin_method( &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, ) -> Option<PickResult<'tcx>> { if !self.tcx.features().pin_ergonomics() { return None; @@ -1215,7 +1511,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let region = self.tcx.lifetimes.re_erased; let autopin_ty = Ty::new_pinned_ref(self.tcx, region, inner_ty, hir::Mutability::Not); - self.pick_method(autopin_ty, unstable_candidates).map(|r| { + self.pick_method(autopin_ty, pick_diag_hints, None).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; pick.autoref_or_ptr_adjustment = @@ -1232,7 +1528,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, step: &CandidateStep<'tcx>, self_ty: Ty<'tcx>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, ) -> Option<PickResult<'tcx>> { // Don't convert an unsized reference to ptr if step.unsize { @@ -1244,7 +1540,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { }; let const_ptr_ty = Ty::new_imm_ptr(self.tcx, ty); - self.pick_method(const_ptr_ty, unstable_candidates).map(|r| { + self.pick_method(const_ptr_ty, pick_diag_hints, None).map(|r| { r.map(|mut pick| { pick.autoderefs = step.autoderefs; pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr); @@ -1256,40 +1552,35 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { fn pick_method( &self, self_ty: Ty<'tcx>, - mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, + pick_constraints: Option<&PickConstraintsForShadowed>, ) -> Option<PickResult<'tcx>> { debug!("pick_method(self_ty={})", self.ty_to_string(self_ty)); - let mut possibly_unsatisfied_predicates = Vec::new(); - for (kind, candidates) in [("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] { debug!("searching {} candidates", kind); - let res = self.consider_candidates( - self_ty, - candidates, - &mut possibly_unsatisfied_predicates, - unstable_candidates.as_deref_mut(), - ); + let res = + self.consider_candidates(self_ty, candidates, pick_diag_hints, pick_constraints); if let Some(pick) = res { return Some(pick); } } if self.private_candidate.get().is_none() { - if let Some(Ok(pick)) = - self.consider_candidates(self_ty, &self.private_candidates, &mut vec![], None) - { + if let Some(Ok(pick)) = self.consider_candidates( + self_ty, + &self.private_candidates, + &mut PickDiagHints { + unstable_candidates: None, + unsatisfied_predicates: &mut vec![], + }, + None, + ) { self.private_candidate.set(Some((pick.item.kind.as_def_kind(), pick.item.def_id))); } } - - // `pick_method` may be called twice for the same self_ty if no stable methods - // match. Only extend once. - if unstable_candidates.is_some() { - self.unsatisfied_predicates.borrow_mut().extend(possibly_unsatisfied_predicates); - } None } @@ -1297,17 +1588,25 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { &self, self_ty: Ty<'tcx>, candidates: &[Candidate<'tcx>], - possibly_unsatisfied_predicates: &mut Vec<( - ty::Predicate<'tcx>, - Option<ty::Predicate<'tcx>>, - Option<ObligationCause<'tcx>>, - )>, - mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, + pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, + pick_constraints: Option<&PickConstraintsForShadowed>, ) -> Option<PickResult<'tcx>> { let mut applicable_candidates: Vec<_> = candidates .iter() + .filter(|candidate| { + pick_constraints + .map(|pick_constraints| pick_constraints.candidate_may_shadow(&candidate)) + .unwrap_or(true) + }) .map(|probe| { - (probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates)) + ( + probe, + self.consider_probe( + self_ty, + probe, + &mut pick_diag_hints.unsatisfied_predicates, + ), + ) }) .filter(|&(_, status)| status != ProbeResult::NoMatch) .collect(); @@ -1322,7 +1621,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - if let Some(uc) = &mut unstable_candidates { + if let Some(uc) = &mut pick_diag_hints.unstable_candidates { applicable_candidates.retain(|&(candidate, _)| { if let stability::EvalResult::Deny { feature, .. } = self.tcx.eval_stability(candidate.item.def_id, None, self.span, None) @@ -1340,10 +1639,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } applicable_candidates.pop().map(|(probe, status)| match status { - ProbeResult::Match => { - Ok(probe - .to_unadjusted_pick(self_ty, unstable_candidates.cloned().unwrap_or_default())) - } + ProbeResult::Match => Ok(probe.to_unadjusted_pick( + self_ty, + pick_diag_hints.unstable_candidates.clone().unwrap_or_default(), + )), ProbeResult::NoMatch | ProbeResult::BadReturnType => Err(MethodError::BadReturnType), }) } @@ -1372,6 +1671,7 @@ impl<'tcx> Pick<'tcx> { autoref_or_ptr_adjustment: _, self_ty, unstable_candidates: _, + receiver_steps: _, } = *self; self_ty != other.self_ty || def_id != other.item.def_id } @@ -1447,7 +1747,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { /// so do not use to make a decision that may lead to a successful compilation. fn candidate_source(&self, candidate: &Candidate<'tcx>, self_ty: Ty<'tcx>) -> CandidateSource { match candidate.kind { - InherentImplCandidate(_) => { + InherentImplCandidate { .. } => { CandidateSource::Impl(candidate.item.container_id(self.tcx)) } ObjectCandidate(_) | WhereClauseCandidate(_) => { @@ -1477,6 +1777,15 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } + fn candidate_source_from_pick(&self, pick: &Pick<'tcx>) -> CandidateSource { + match pick.kind { + InherentImplPick => CandidateSource::Impl(pick.item.container_id(self.tcx)), + ObjectPick | WhereClausePick(_) | TraitPick => { + CandidateSource::Trait(pick.item.container_id(self.tcx)) + } + } + } + #[instrument(level = "trace", skip(self, possibly_unsatisfied_predicates), ret)] fn consider_probe( &self, @@ -1501,7 +1810,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let (mut xform_self_ty, mut xform_ret_ty); match probe.kind { - InherentImplCandidate(impl_def_id) => { + InherentImplCandidate { impl_def_id, .. } => { let impl_args = self.fresh_args_for_item(self.span, impl_def_id); let impl_ty = self.tcx.type_of(impl_def_id).instantiate(self.tcx, impl_args); (xform_self_ty, xform_ret_ty) = @@ -1693,7 +2002,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { // We don't normalize the other candidates for perf/backwards-compat reasons... // but `self.return_type` is only set on the diagnostic-path, so we // should be okay doing it here. - if !matches!(probe.kind, InherentImplCandidate(_)) { + if !matches!(probe.kind, InherentImplCandidate { .. }) { xform_ret_ty = ocx.normalize(&cause, self.param_env, xform_ret_ty); } @@ -1771,6 +2080,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { autoref_or_ptr_adjustment: None, self_ty, unstable_candidates: vec![], + receiver_steps: None, }) } @@ -1808,7 +2118,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pcx.method_name = Some(method_name); pcx.assemble_inherent_candidates(); pcx.assemble_extension_candidates_for_all_traits(); - pcx.pick_core().and_then(|pick| pick.ok()).map(|pick| pick.item) + pcx.pick_core(&mut Vec::new()).and_then(|pick| pick.ok()).map(|pick| pick.item) }) .collect(); @@ -2042,7 +2352,7 @@ impl<'tcx> Candidate<'tcx> { Pick { item: self.item, kind: match self.kind { - InherentImplCandidate(_) => InherentImplPick, + InherentImplCandidate { .. } => InherentImplPick, ObjectCandidate(_) => ObjectPick, TraitCandidate(_) => TraitPick, WhereClauseCandidate(trait_ref) => { @@ -2064,6 +2374,10 @@ impl<'tcx> Candidate<'tcx> { autoref_or_ptr_adjustment: None, self_ty, unstable_candidates, + receiver_steps: match self.kind { + InherentImplCandidate { receiver_steps, .. } => Some(receiver_steps), + _ => None, + }, } } } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index b0c020dd7cb..4f283644cbe 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -1840,7 +1840,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// captured by move. /// /// ```rust -/// #![feature(async_closure)] /// let x = &1i32; // Let's call this lifetime `'1`. /// let c = async move || { /// println!("{:?}", *x); @@ -1855,7 +1854,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// child capture with the lifetime of the parent coroutine-closure's env. /// /// ```rust -/// #![feature(async_closure)] /// let mut x = 1i32; /// let c = async || { /// x = 1; diff --git a/compiler/rustc_lint/src/async_closures.rs b/compiler/rustc_lint/src/async_closures.rs index 2a821b71103..5d40b8ab2ee 100644 --- a/compiler/rustc_lint/src/async_closures.rs +++ b/compiler/rustc_lint/src/async_closures.rs @@ -12,7 +12,6 @@ declare_lint! { /// ### Example /// /// ```rust - /// #![feature(async_closure)] /// #![warn(closure_returning_async_block)] /// let c = |x: &str| async {}; /// ``` @@ -40,8 +39,6 @@ declare_lint! { /// But it does work with async closures: /// /// ```rust - /// #![feature(async_closure)] - /// /// async fn callback(x: &str) {} /// /// let captured_str = String::new(); @@ -52,7 +49,6 @@ declare_lint! { pub CLOSURE_RETURNING_ASYNC_BLOCK, Allow, "closure that returns `async {}` could be rewritten as an async closure", - @feature_gate = async_closure; } declare_lint_pass!( diff --git a/compiler/rustc_lint/src/tests.rs b/compiler/rustc_lint/src/tests.rs index 7fbf381a8d3..186dec5904b 100644 --- a/compiler/rustc_lint/src/tests.rs +++ b/compiler/rustc_lint/src/tests.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + use rustc_span::{Symbol, create_default_session_globals_then}; use crate::levels::parse_lint_and_tool_name; diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs index eeed5118592..f049da95f29 100644 --- a/compiler/rustc_middle/src/traits/query.rs +++ b/compiler/rustc_middle/src/traits/query.rs @@ -149,11 +149,21 @@ pub struct CandidateStep<'tcx> { /// `foo.by_raw_ptr()` will work and `foo.by_ref()` won't. pub from_unsafe_deref: bool, pub unsize: bool, + /// We will generate CandidateSteps which are reachable via a chain + /// of following `Receiver`. The first 'n' of those will be reachable + /// by following a chain of 'Deref' instead (since there's a blanket + /// implementation of Receiver for Deref). + /// We use the entire set of steps when identifying method candidates + /// (e.g. identifying relevant `impl` blocks) but only those that are + /// reachable via Deref when examining what the receiver type can + /// be converted into by autodereffing. + pub reachable_via_deref: bool, } #[derive(Copy, Clone, Debug, HashStable)] pub struct MethodAutoderefStepsResult<'tcx> { - /// The valid autoderef steps that could be found. + /// The valid autoderef steps that could be found by following a chain + /// of `Receiver<Target=T>` or `Deref<Target=T>` trait implementations. pub steps: &'tcx [CandidateStep<'tcx>], /// If Some(T), a type autoderef reported an error on. pub opt_bad_ty: Option<&'tcx MethodAutoderefBadTy<'tcx>>, diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 65c909e70f6..1dd564d9798 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -677,23 +677,26 @@ impl<'tcx> Instance<'tcx> { // // 1) The underlying method expects a caller location parameter // in the ABI - if resolved.def.requires_caller_location(tcx) - // 2) The caller location parameter comes from having `#[track_caller]` - // on the implementation, and *not* on the trait method. - && !tcx.should_inherit_track_caller(def) - // If the method implementation comes from the trait definition itself - // (e.g. `trait Foo { #[track_caller] my_fn() { /* impl */ } }`), - // then we don't need to generate a shim. This check is needed because - // `should_inherit_track_caller` returns `false` if our method - // implementation comes from the trait block, and not an impl block - && !matches!( - tcx.opt_associated_item(def), - Some(ty::AssocItem { - container: ty::AssocItemContainer::Trait, - .. - }) - ) - { + let needs_track_caller_shim = resolved.def.requires_caller_location(tcx) + // 2) The caller location parameter comes from having `#[track_caller]` + // on the implementation, and *not* on the trait method. + && !tcx.should_inherit_track_caller(def) + // If the method implementation comes from the trait definition itself + // (e.g. `trait Foo { #[track_caller] my_fn() { /* impl */ } }`), + // then we don't need to generate a shim. This check is needed because + // `should_inherit_track_caller` returns `false` if our method + // implementation comes from the trait block, and not an impl block + && !matches!( + tcx.opt_associated_item(def), + Some(ty::AssocItem { + container: ty::AssocItemContainer::Trait, + .. + }) + ); + // We also need to generate a shim if this is an AFIT. + let needs_rpitit_shim = + tcx.return_position_impl_trait_in_trait_shim_data(def).is_some(); + if needs_track_caller_shim || needs_rpitit_shim { if tcx.is_closure_like(def) { debug!( " => vtable fn pointer created for closure with #[track_caller]: {:?} for method {:?} {:?}", diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 70e0568b202..80b11892a42 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -146,6 +146,7 @@ mod opaque_types; mod parameterized; mod predicate; mod region; +mod return_position_impl_trait_in_trait; mod rvalue_scopes; mod structural_impls; #[allow(hidden_glob_reexports)] diff --git a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs new file mode 100644 index 00000000000..21c605f8296 --- /dev/null +++ b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs @@ -0,0 +1,95 @@ +use rustc_hir::def_id::DefId; + +use crate::ty::{self, ExistentialPredicateStableCmpExt, TyCtxt}; + +impl<'tcx> TyCtxt<'tcx> { + /// Given a `def_id` of a trait or impl method, compute whether that method needs to + /// have an RPITIT shim applied to it for it to be object safe. If so, return the + /// `def_id` of the RPITIT, and also the args of trait method that returns the RPITIT. + /// + /// NOTE that these args are not, in general, the same as than the RPITIT's args. They + /// are a subset of those args, since they do not include the late-bound lifetimes of + /// the RPITIT. Depending on the context, these will need to be dealt with in different + /// ways -- in codegen, it's okay to fill them with ReErased. + pub fn return_position_impl_trait_in_trait_shim_data( + self, + def_id: DefId, + ) -> Option<(DefId, ty::EarlyBinder<'tcx, ty::GenericArgsRef<'tcx>>)> { + let assoc_item = self.opt_associated_item(def_id)?; + + let (trait_item_def_id, opt_impl_def_id) = match assoc_item.container { + ty::AssocItemContainer::Impl => { + (assoc_item.trait_item_def_id?, Some(self.parent(def_id))) + } + ty::AssocItemContainer::Trait => (def_id, None), + }; + + let sig = self.fn_sig(trait_item_def_id); + + // Check if the trait returns an RPITIT. + let ty::Alias(ty::Projection, ty::AliasTy { def_id, .. }) = + *sig.skip_binder().skip_binder().output().kind() + else { + return None; + }; + if !self.is_impl_trait_in_trait(def_id) { + return None; + } + + let args = if let Some(impl_def_id) = opt_impl_def_id { + // Rebase the args from the RPITIT onto the impl trait ref, so we can later + // substitute them with the method args of the *impl* method, since that's + // the instance we're building a vtable shim for. + ty::GenericArgs::identity_for_item(self, trait_item_def_id).rebase_onto( + self, + self.parent(trait_item_def_id), + self.impl_trait_ref(impl_def_id) + .expect("expected impl trait ref from parent of impl item") + .instantiate_identity() + .args, + ) + } else { + // This is when we have a default trait implementation. + ty::GenericArgs::identity_for_item(self, trait_item_def_id) + }; + + Some((def_id, ty::EarlyBinder::bind(args))) + } + + /// Given a `DefId` of an RPITIT and its args, return the existential predicates + /// that corresponds to the RPITIT's bounds with the self type erased. + pub fn item_bounds_to_existential_predicates( + self, + def_id: DefId, + args: ty::GenericArgsRef<'tcx>, + ) -> &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> { + let mut bounds: Vec<_> = self + .item_super_predicates(def_id) + .iter_instantiated(self, args) + .filter_map(|clause| { + clause + .kind() + .map_bound(|clause| match clause { + ty::ClauseKind::Trait(trait_pred) => Some(ty::ExistentialPredicate::Trait( + ty::ExistentialTraitRef::erase_self_ty(self, trait_pred.trait_ref), + )), + ty::ClauseKind::Projection(projection_pred) => { + Some(ty::ExistentialPredicate::Projection( + ty::ExistentialProjection::erase_self_ty(self, projection_pred), + )) + } + ty::ClauseKind::TypeOutlives(_) => { + // Type outlives bounds don't really turn into anything, + // since we must use an intersection region for the `dyn*`'s + // region anyways. + None + } + _ => unreachable!("unexpected clause in item bounds: {clause:?}"), + }) + .transpose() + }) + .collect(); + bounds.sort_by(|a, b| a.skip_binder().stable_cmp(self, &b.skip_binder())); + self.mk_poly_existential_predicates(&bounds) + } +} diff --git a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs index df4c0593246..d79d2c316ee 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs @@ -4,52 +4,26 @@ //! field-deref on a local variable, `x.field`, has the same meaning //! in both domains). Indexed projections are the exception: `a[x]` //! needs to be treated as mapping to the same move path as `a[y]` as -//! well as `a[13]`, etc. +//! well as `a[13]`, etc. So we map these `x`/`y` values to `()`. //! //! (In theory, the analysis could be extended to work with sets of //! paths, so that `a[0]` and `a[13]` could be kept distinct, while //! `a[x]` would still overlap them both. But that is not this //! representation does today.) -use rustc_middle::mir::{Local, Operand, PlaceElem, ProjectionElem}; -use rustc_middle::ty::Ty; - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub(crate) struct AbstractOperand; -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub(crate) struct AbstractType; -pub(crate) type AbstractElem = ProjectionElem<AbstractOperand, AbstractType>; +use rustc_middle::mir::{PlaceElem, ProjectionElem, ProjectionKind}; pub(crate) trait Lift { - type Abstract; - fn lift(&self) -> Self::Abstract; -} -impl<'tcx> Lift for Operand<'tcx> { - type Abstract = AbstractOperand; - fn lift(&self) -> Self::Abstract { - AbstractOperand - } -} -impl Lift for Local { - type Abstract = AbstractOperand; - fn lift(&self) -> Self::Abstract { - AbstractOperand - } -} -impl<'tcx> Lift for Ty<'tcx> { - type Abstract = AbstractType; - fn lift(&self) -> Self::Abstract { - AbstractType - } + fn lift(&self) -> ProjectionKind; } + impl<'tcx> Lift for PlaceElem<'tcx> { - type Abstract = AbstractElem; - fn lift(&self) -> Self::Abstract { + fn lift(&self) -> ProjectionKind { match *self { ProjectionElem::Deref => ProjectionElem::Deref, - ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, ty.lift()), - ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(ty.lift()), - ProjectionElem::Index(ref i) => ProjectionElem::Index(i.lift()), + ProjectionElem::Field(f, _ty) => ProjectionElem::Field(f, ()), + ProjectionElem::OpaqueCast(_ty) => ProjectionElem::OpaqueCast(()), + ProjectionElem::Index(_i) => ProjectionElem::Index(()), ProjectionElem::Subslice { from, to, from_end } => { ProjectionElem::Subslice { from, to, from_end } } @@ -57,7 +31,7 @@ impl<'tcx> Lift for PlaceElem<'tcx> { ProjectionElem::ConstantIndex { offset, min_length, from_end } } ProjectionElem::Downcast(a, u) => ProjectionElem::Downcast(a, u), - ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(ty.lift()), + ProjectionElem::Subtype(_ty) => ProjectionElem::Subtype(()), } } } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs index 926bd187431..8aea8d2ae3c 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::{Ty, TyCtxt}; use rustc_span::Span; use smallvec::SmallVec; -use self::abs_domain::{AbstractElem, Lift}; +use self::abs_domain::Lift; use crate::un_derefer::UnDerefer; mod abs_domain; @@ -300,7 +300,7 @@ pub struct MovePathLookup<'tcx> { /// subsequent search so that it is solely relative to that /// base-place). For the remaining lookup, we map the projection /// elem to the associated MovePathIndex. - projections: FxHashMap<(MovePathIndex, AbstractElem), MovePathIndex>, + projections: FxHashMap<(MovePathIndex, ProjectionKind), MovePathIndex>, un_derefer: UnDerefer<'tcx>, } diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 36eb435c63a..ef61866e902 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -3,8 +3,6 @@ //! //! Consider an async closure like: //! ```rust -//! #![feature(async_closure)] -//! //! let x = vec![1, 2, 3]; //! //! let closure = async move || { diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index b8383e734e2..722da3c420d 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -9,6 +9,7 @@ use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::patch::MirPatch; use rustc_middle::mir::*; use rustc_middle::query::Providers; +use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{ self, CoroutineArgs, CoroutineArgsExt, EarlyBinder, GenericArgs, Ty, TyCtxt, }; @@ -710,6 +711,13 @@ fn build_call_shim<'tcx>( }; let def_id = instance.def_id(); + + let rpitit_shim = if let ty::InstanceKind::ReifyShim(..) = instance { + tcx.return_position_impl_trait_in_trait_shim_data(def_id) + } else { + None + }; + let sig = tcx.fn_sig(def_id); let sig = sig.map_bound(|sig| tcx.instantiate_bound_regions_with_erased(sig)); @@ -765,9 +773,34 @@ fn build_call_shim<'tcx>( let mut local_decls = local_decls_for_sig(&sig, span); let source_info = SourceInfo::outermost(span); + let mut destination = Place::return_place(); + if let Some((rpitit_def_id, fn_args)) = rpitit_shim { + let rpitit_args = + fn_args.instantiate_identity().extend_to(tcx, rpitit_def_id, |param, _| { + match param.kind { + ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + ty::GenericParamDefKind::Type { .. } + | ty::GenericParamDefKind::Const { .. } => { + unreachable!("rpitit should have no addition ty/ct") + } + } + }); + let dyn_star_ty = Ty::new_dynamic( + tcx, + tcx.item_bounds_to_existential_predicates(rpitit_def_id, rpitit_args), + tcx.lifetimes.re_erased, + ty::DynStar, + ); + destination = local_decls.push(local_decls[RETURN_PLACE].clone()).into(); + local_decls[RETURN_PLACE].ty = dyn_star_ty; + let mut inputs_and_output = sig.inputs_and_output.to_vec(); + *inputs_and_output.last_mut().unwrap() = dyn_star_ty; + sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); + } + let rcvr_place = || { assert!(rcvr_adjustment.is_some()); - Place::from(Local::new(1 + 0)) + Place::from(Local::new(1)) }; let mut statements = vec![]; @@ -854,7 +887,7 @@ fn build_call_shim<'tcx>( TerminatorKind::Call { func: callee, args, - destination: Place::return_place(), + destination, target: Some(BasicBlock::new(1)), unwind: if let Some(Adjustment::RefMut) = rcvr_adjustment { UnwindAction::Cleanup(BasicBlock::new(3)) @@ -882,7 +915,24 @@ fn build_call_shim<'tcx>( ); } // BB #1/#2 - return - block(&mut blocks, vec![], TerminatorKind::Return, false); + // NOTE: If this is an RPITIT in dyn, we also want to coerce + // the return type of the function into a `dyn*`. + let stmts = if rpitit_shim.is_some() { + vec![Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + Place::return_place(), + Rvalue::Cast( + CastKind::PointerCoercion(PointerCoercion::DynStar, CoercionSource::Implicit), + Operand::Move(destination), + sig.output(), + ), + ))), + }] + } else { + vec![] + }; + block(&mut blocks, stmts, TerminatorKind::Return, false); if let Some(Adjustment::RefMut) = rcvr_adjustment { // BB #3 - drop if closure panics block( diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index caae54cd559..714b64b3a23 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -42,7 +42,10 @@ fn custom_coerce_unsize_info<'tcx>( .. })) => Ok(tcx.coerce_unsized_info(impl_def_id)?.custom_kind.unwrap()), impl_source => { - bug!("invalid `CoerceUnsized` impl_source: {:?}", impl_source); + bug!( + "invalid `CoerceUnsized` from {source_ty} to {target_ty}: impl_source: {:?}", + impl_source + ); } } } diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index c6c9eb3b0b2..ee38f16d4ec 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -1,7 +1,7 @@ use rustc_ast::token::{self, Delimiter, Token}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_ast_pretty::pprust::token_to_string; -use rustc_errors::{Applicability, PErr}; +use rustc_errors::{Applicability, Diag}; use rustc_span::symbol::kw; use super::diagnostics::{report_suspicious_mismatch_block, same_indentation_level}; @@ -14,7 +14,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { pub(super) fn lex_token_trees( &mut self, is_delimited: bool, - ) -> (Spacing, TokenStream, Result<(), Vec<PErr<'psess>>>) { + ) -> (Spacing, TokenStream, Result<(), Vec<Diag<'psess>>>) { // Move past the opening delimiter. let open_spacing = self.bump_minimal(); @@ -56,7 +56,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { } } - fn eof_err(&mut self) -> PErr<'psess> { + fn eof_err(&mut self) -> Diag<'psess> { let msg = "this file contains an unclosed delimiter"; let mut err = self.dcx().struct_span_err(self.token.span, msg); @@ -98,7 +98,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { fn lex_token_tree_open_delim( &mut self, open_delim: Delimiter, - ) -> Result<TokenTree, Vec<PErr<'psess>>> { + ) -> Result<TokenTree, Vec<Diag<'psess>>> { // The span for beginning of the delimited section. let pre_span = self.token.span; @@ -250,8 +250,8 @@ impl<'psess, 'src> Lexer<'psess, 'src> { fn unclosed_delim_err( &mut self, tts: TokenStream, - mut errs: Vec<PErr<'psess>>, - ) -> Vec<PErr<'psess>> { + mut errs: Vec<Diag<'psess>>, + ) -> Vec<Diag<'psess>> { // If there are unclosed delims, see if there are diff markers and if so, point them // out instead of complaining about the unclosed delims. let mut parser = Parser::new(self.psess, tts, None); @@ -308,7 +308,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { errs } - fn close_delim_err(&mut self, delim: Delimiter) -> PErr<'psess> { + fn close_delim_err(&mut self, delim: Delimiter) -> Diag<'psess> { // An unexpected closing delimiter (i.e., there is no matching opening delimiter). let token_str = token_to_string(&self.token); let msg = format!("unexpected closing delimiter: `{token_str}`"); diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 34131e3af6e..e5edf605d82 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -16,7 +16,7 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, PErr, PResult, Subdiagnostic, + Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, PResult, Subdiagnostic, Suggestions, pluralize, }; use rustc_session::errors::ExprParenthesesNeeded; @@ -2132,7 +2132,7 @@ impl<'a> Parser<'a> { &mut self, delim: Delimiter, lo: Span, - err: PErr<'a>, + err: Diag<'a>, ) -> P<Expr> { let guar = err.emit(); // Recover from parse error, callers expect the closing delim to be consumed. @@ -3014,7 +3014,7 @@ impl<'a> Parser<'a> { } /// Check for exclusive ranges written as `..<` - pub(crate) fn maybe_err_dotdotlt_syntax(&self, maybe_lt: Token, mut err: PErr<'a>) -> PErr<'a> { + pub(crate) fn maybe_err_dotdotlt_syntax(&self, maybe_lt: Token, mut err: Diag<'a>) -> Diag<'a> { if maybe_lt == token::Lt && (self.expected_tokens.contains(&TokenType::Token(token::Gt)) || matches!(self.token.kind, token::Literal(..))) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index eeb83a85e59..44f42e5fbf2 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1369,11 +1369,14 @@ impl<'a> Parser<'a> { )) } else { // Field access `expr.f` + let span = lo.to(self.prev_token.span); if let Some(args) = seg.args { - self.dcx().emit_err(errors::FieldExpressionWithGeneric(args.span())); + // See `StashKey::GenericInFieldExpr` for more info on why we stash this. + self.dcx() + .create_err(errors::FieldExpressionWithGeneric(args.span())) + .stash(seg.ident.span, StashKey::GenericInFieldExpr); } - let span = lo.to(self.prev_token.span); Ok(self.mk_expr(span, ExprKind::Field(self_arg, seg.ident))) } } @@ -2363,10 +2366,7 @@ impl<'a> Parser<'a> { }; match coroutine_kind { - Some(CoroutineKind::Async { span, .. }) => { - // Feature-gate `async ||` closures. - self.psess.gated_spans.gate(sym::async_closure, span); - } + Some(CoroutineKind::Async { .. }) => {} Some(CoroutineKind::Gen { span, .. }) | Some(CoroutineKind::AsyncGen { span, .. }) => { // Feature-gate `gen ||` and `async gen ||` closures. // FIXME(gen_blocks): This perhaps should be a different gate. diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index decaecd2682..1813960dad0 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + use std::assert_matches::assert_matches; use std::io::prelude::*; use std::iter::Peekable; diff --git a/compiler/rustc_parse/src/parser/tokenstream/tests.rs b/compiler/rustc_parse/src/parser/tokenstream/tests.rs index efe266f5290..b13b68c266a 100644 --- a/compiler/rustc_parse/src/parser/tokenstream/tests.rs +++ b/compiler/rustc_parse/src/parser/tokenstream/tests.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + use rustc_ast::token::{self, IdentIsRaw}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_span::{BytePos, Span, Symbol, create_default_session_globals_then}; diff --git a/compiler/rustc_span/src/edit_distance/tests.rs b/compiler/rustc_span/src/edit_distance/tests.rs index c9c7a1f1bf2..9540f934d7e 100644 --- a/compiler/rustc_span/src/edit_distance/tests.rs +++ b/compiler/rustc_span/src/edit_distance/tests.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(bootstrap), allow(rustc::symbol_intern_string_literal))] + use super::*; #[test] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d30b17c9cd8..9b499c71603 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -461,6 +461,7 @@ symbols! { async_drop_slice, async_drop_surface_drop_in_place, async_fn, + async_fn_in_dyn_trait, async_fn_in_trait, async_fn_kind_helper, async_fn_kind_upvars, diff --git a/compiler/rustc_target/src/spec/base/windows_gnullvm.rs b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs index d5acd37092a..4f370ec8bd0 100644 --- a/compiler/rustc_target/src/spec/base/windows_gnullvm.rs +++ b/compiler/rustc_target/src/spec/base/windows_gnullvm.rs @@ -42,6 +42,8 @@ pub(crate) fn opts() -> TargetOptions { eh_frame_header: false, no_default_libraries: false, has_thread_local: true, + crt_static_allows_dylibs: true, + crt_static_respected: true, // FIXME(davidtwco): Support Split DWARF on Windows GNU - may require LLVM changes to // output DWO, despite using DWARF, doesn't use ELF.. debuginfo_kind: DebuginfoKind::Pdb, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 3a130607265..c89045c93e3 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -611,6 +611,8 @@ const HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[/*(512, "hvx-length64b"),*/ (1024, "hvx-length128b")]; const MIPS_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "msa")]; const CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "vdspv1")]; +const LOONGARCH_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = + &[(128, "lsx"), (256, "lasx")]; impl super::spec::Target { pub fn rust_target_features(&self) -> &'static [(&'static str, Stability, ImpliedFeatures)] { @@ -638,7 +640,7 @@ impl super::spec::Target { "aarch64" | "arm64ec" => AARCH64_FEATURES_FOR_CORRECT_VECTOR_ABI, "arm" => ARM_FEATURES_FOR_CORRECT_VECTOR_ABI, "powerpc" | "powerpc64" => POWERPC_FEATURES_FOR_CORRECT_VECTOR_ABI, - "loongarch64" => &[], // on-stack ABI, so we complain about all by-val vectors + "loongarch64" => LOONGARCH_FEATURES_FOR_CORRECT_VECTOR_ABI, "riscv32" | "riscv64" => RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI, "wasm32" | "wasm64" => WASM_FEATURES_FOR_CORRECT_VECTOR_ABI, "s390x" => S390X_FEATURES_FOR_CORRECT_VECTOR_ABI, diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index c9297027519..d2abd881c45 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -11,6 +11,7 @@ use rustc_abi::BackendRepr; use rustc_errors::FatalError; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::ty::{ self, EarlyBinder, ExistentialPredicateStableCmpExt as _, GenericArgs, Ty, TyCtxt, @@ -901,23 +902,59 @@ fn contains_illegal_impl_trait_in_trait<'tcx>( fn_def_id: DefId, ty: ty::Binder<'tcx, Ty<'tcx>>, ) -> Option<MethodViolationCode> { - // This would be caught below, but rendering the error as a separate - // `async-specific` message is better. + let ty = tcx.liberate_late_bound_regions(fn_def_id, ty); + if tcx.asyncness(fn_def_id).is_async() { - return Some(MethodViolationCode::AsyncFn); + // FIXME(async_fn_in_dyn_trait): Think of a better way to unify these code paths + // to issue an appropriate feature suggestion when users try to use AFIDT. + // Obviously we must only do this once AFIDT is finished enough to actually be usable. + if tcx.features().async_fn_in_dyn_trait() { + let ty::Alias(ty::Projection, proj) = *ty.kind() else { + bug!("expected async fn in trait to return an RPITIT"); + }; + assert!(tcx.is_impl_trait_in_trait(proj.def_id)); + + // FIXME(async_fn_in_dyn_trait): We should check that this bound is legal too, + // and stop relying on `async fn` in the definition. + for bound in tcx.item_bounds(proj.def_id).instantiate(tcx, proj.args) { + if let Some(violation) = bound + .visit_with(&mut IllegalRpititVisitor { tcx, allowed: Some(proj) }) + .break_value() + { + return Some(violation); + } + } + + None + } else { + // Rendering the error as a separate `async-specific` message is better. + Some(MethodViolationCode::AsyncFn) + } + } else { + ty.visit_with(&mut IllegalRpititVisitor { tcx, allowed: None }).break_value() } +} + +struct IllegalRpititVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + allowed: Option<ty::AliasTy<'tcx>>, +} + +impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for IllegalRpititVisitor<'tcx> { + type Result = ControlFlow<MethodViolationCode>; - // FIXME(RPITIT): Perhaps we should use a visitor here? - ty.skip_binder().walk().find_map(|arg| { - if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Alias(ty::Projection, proj) = ty.kind() - && tcx.is_impl_trait_in_trait(proj.def_id) + fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { + if let ty::Alias(ty::Projection, proj) = *ty.kind() + && Some(proj) != self.allowed + && self.tcx.is_impl_trait_in_trait(proj.def_id) { - Some(MethodViolationCode::ReferencesImplTraitInTrait(tcx.def_span(proj.def_id))) + ControlFlow::Break(MethodViolationCode::ReferencesImplTraitInTrait( + self.tcx.def_span(proj.def_id), + )) } else { - None + ty.super_visit_with(self) } - }) + } } pub(crate) fn provide(providers: &mut Providers) { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 541c0c915ff..5f1dbfeedfb 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -7,8 +7,8 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::DefKind; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_infer::infer::{DefineOpaqueTypes, RegionVariableOrigin}; use rustc_infer::traits::{ObligationCauseCode, PredicateObligations}; use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::{BuiltinImplSource, ImplSource, ImplSourceUserDefinedData}; @@ -18,6 +18,7 @@ use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, Term, Ty, TyCtxt, TypingMode, Upcast}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::sym; +use thin_vec::thin_vec; use tracing::{debug, instrument}; use super::{ @@ -61,6 +62,9 @@ enum ProjectionCandidate<'tcx> { /// Bounds specified on an object type Object(ty::PolyProjectionPredicate<'tcx>), + /// Built-in bound for a dyn async fn in trait + ObjectRpitit, + /// From an "impl" (or a "pseudo-impl" returned by select) Select(Selection<'tcx>), } @@ -827,6 +831,17 @@ fn assemble_candidates_from_object_ty<'cx, 'tcx>( env_predicates, false, ); + + // `dyn Trait` automagically project their AFITs to `dyn* Future`. + if tcx.is_impl_trait_in_trait(obligation.predicate.def_id) + && let Some(out_trait_def_id) = data.principal_def_id() + && let rpitit_trait_def_id = tcx.parent(obligation.predicate.def_id) + && tcx + .supertrait_def_ids(out_trait_def_id) + .any(|trait_def_id| trait_def_id == rpitit_trait_def_id) + { + candidate_set.push_candidate(ProjectionCandidate::ObjectRpitit); + } } #[instrument( @@ -1247,6 +1262,8 @@ fn confirm_candidate<'cx, 'tcx>( ProjectionCandidate::Select(impl_source) => { confirm_select_candidate(selcx, obligation, impl_source) } + + ProjectionCandidate::ObjectRpitit => confirm_object_rpitit_candidate(selcx, obligation), }; // When checking for cycle during evaluation, we compare predicates with @@ -2034,6 +2051,45 @@ fn confirm_impl_candidate<'cx, 'tcx>( } } +fn confirm_object_rpitit_candidate<'cx, 'tcx>( + selcx: &mut SelectionContext<'cx, 'tcx>, + obligation: &ProjectionTermObligation<'tcx>, +) -> Progress<'tcx> { + let tcx = selcx.tcx(); + let mut obligations = thin_vec![]; + + // Compute an intersection lifetime for all the input components of this GAT. + let intersection = + selcx.infcx.next_region_var(RegionVariableOrigin::MiscVariable(obligation.cause.span)); + for component in obligation.predicate.args { + match component.unpack() { + ty::GenericArgKind::Lifetime(lt) => { + obligations.push(obligation.with(tcx, ty::OutlivesPredicate(lt, intersection))); + } + ty::GenericArgKind::Type(ty) => { + obligations.push(obligation.with(tcx, ty::OutlivesPredicate(ty, intersection))); + } + ty::GenericArgKind::Const(_ct) => { + // Consts have no outlives... + } + } + } + + Progress { + term: Ty::new_dynamic( + tcx, + tcx.item_bounds_to_existential_predicates( + obligation.predicate.def_id, + obligation.predicate.args, + ), + intersection, + ty::DynStar, + ) + .into(), + obligations, + } +} + // Get obligations corresponding to the predicates from the where-clause of the // associated type itself. fn assoc_ty_own_obligations<'cx, 'tcx>( diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 2fbe2e1e323..962b6b94fa6 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -19,6 +19,7 @@ use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; use rustc_middle::ty::{self, GenericArgsRef, ToPolyTraitRef, Ty, TyCtxt, Upcast}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::DefId; +use rustc_type_ir::elaborate; use tracing::{debug, instrument}; use super::SelectionCandidate::{self, *}; @@ -624,6 +625,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { for assoc_type in assoc_types { let defs: &ty::Generics = tcx.generics_of(assoc_type); + // When `async_fn_in_dyn_trait` is enabled, we don't need to check the + // RPITIT for compatibility, since it's not provided by the user. + if tcx.features().async_fn_in_dyn_trait() && tcx.is_impl_trait_in_trait(assoc_type) { + continue; + } + if !defs.own_params.is_empty() { tcx.dcx().span_delayed_bug( obligation.cause.span, @@ -1175,6 +1182,38 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ty::ClauseKind::TypeOutlives(outlives).upcast(tcx), )); + // Require that all AFIT will return something that can be coerced into `dyn*` + // -- a shim will be responsible for doing the actual coercion to `dyn*`. + if let Some(principal) = data.principal() { + for supertrait in + elaborate::supertraits(tcx, principal.with_self_ty(tcx, source)) + { + if tcx.is_trait_alias(supertrait.def_id()) { + continue; + } + + for &assoc_item in tcx.associated_item_def_ids(supertrait.def_id()) { + if !tcx.is_impl_trait_in_trait(assoc_item) { + continue; + } + + // RPITITs with `Self: Sized` don't need to be checked. + if tcx.generics_require_sized_self(assoc_item) { + continue; + } + + let pointer_like_goal = pointer_like_goal_for_rpitit( + tcx, + supertrait, + assoc_item, + &obligation.cause, + ); + + nested.push(predicate_to_obligation(pointer_like_goal.upcast(tcx))); + } + } + } + ImplSource::Builtin(BuiltinImplSource::Misc, nested) } @@ -1280,3 +1319,43 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) } } + +/// Compute a goal that some RPITIT (right now, only RPITITs corresponding to Futures) +/// implements the `PointerLike` trait, which is a requirement for the RPITIT to be +/// coercible to `dyn* Future`, which is itself a requirement for the RPITIT's parent +/// trait to be coercible to `dyn Trait`. +/// +/// We do this given a supertrait's substitutions, and then augment the substitutions +/// with bound variables to compute the goal universally. Given that `PointerLike` has +/// no region requirements (at least for the built-in pointer types), this shouldn't +/// *really* matter, but it is the best choice for soundness. +fn pointer_like_goal_for_rpitit<'tcx>( + tcx: TyCtxt<'tcx>, + supertrait: ty::PolyTraitRef<'tcx>, + rpitit_item: DefId, + cause: &ObligationCause<'tcx>, +) -> ty::PolyTraitRef<'tcx> { + let mut bound_vars = supertrait.bound_vars().to_vec(); + + let args = supertrait.skip_binder().args.extend_to(tcx, rpitit_item, |arg, _| match arg.kind { + ty::GenericParamDefKind::Lifetime => { + let kind = ty::BoundRegionKind::Named(arg.def_id, tcx.item_name(arg.def_id)); + bound_vars.push(ty::BoundVariableKind::Region(kind)); + ty::Region::new_bound(tcx, ty::INNERMOST, ty::BoundRegion { + var: ty::BoundVar::from_usize(bound_vars.len() - 1), + kind, + }) + .into() + } + ty::GenericParamDefKind::Type { .. } | ty::GenericParamDefKind::Const { .. } => { + unreachable!() + } + }); + + ty::Binder::bind_with_vars( + ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::PointerLike, Some(cause.span)), [ + Ty::new_projection_from_args(tcx, rpitit_item, args), + ]), + tcx.mk_bound_variable_kinds(&bound_vars), + ) +} diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index ae6d697794f..b63534880d1 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -48,12 +48,38 @@ fn fn_sig_for_fn_abi<'tcx>( let mut sig = tcx .instantiate_bound_regions_with_erased(tcx.fn_sig(def_id).instantiate(tcx, args)); + // Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`. if let ty::InstanceKind::VTableShim(..) = instance.def { let mut inputs_and_output = sig.inputs_and_output.to_vec(); inputs_and_output[0] = Ty::new_mut_ptr(tcx, inputs_and_output[0]); sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); } + // Modify `fn() -> impl Future` to `fn() -> dyn* Future`. + if let ty::InstanceKind::ReifyShim(def_id, _) = instance.def + && let Some((rpitit_def_id, fn_args)) = + tcx.return_position_impl_trait_in_trait_shim_data(def_id) + { + let fn_args = fn_args.instantiate(tcx, args); + let rpitit_args = + fn_args.extend_to(tcx, rpitit_def_id, |param, _| match param.kind { + ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(), + ty::GenericParamDefKind::Type { .. } + | ty::GenericParamDefKind::Const { .. } => { + unreachable!("rpitit should have no addition ty/ct") + } + }); + let dyn_star_ty = Ty::new_dynamic( + tcx, + tcx.item_bounds_to_existential_predicates(rpitit_def_id, rpitit_args), + tcx.lifetimes.re_erased, + ty::DynStar, + ); + let mut inputs_and_output = sig.inputs_and_output.to_vec(); + *inputs_and_output.last_mut().unwrap() = dyn_star_ty; + sig.inputs_and_output = tcx.mk_type_list(&inputs_and_output); + } + sig } ty::Closure(def_id, args) => { |
