diff options
357 files changed, 7495 insertions, 3179 deletions
diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 2cf811e9122..7754ca0a0f5 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -10,8 +10,6 @@ use crate::{AssocItem, Expr, ForeignItem, Item, NodeId}; use crate::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility}; use crate::{AttrVec, Attribute, Stmt, StmtKind}; -use rustc_span::Span; - use std::fmt; use std::marker::PhantomData; @@ -91,37 +89,6 @@ impl<T: AstDeref<Target: HasNodeId>> HasNodeId for T { } } -/// A trait for AST nodes having a span. -pub trait HasSpan { - fn span(&self) -> Span; -} - -macro_rules! impl_has_span { - ($($T:ty),+ $(,)?) => { - $( - impl HasSpan for $T { - fn span(&self) -> Span { - self.span - } - } - )+ - }; -} - -impl_has_span!(AssocItem, Block, Expr, ForeignItem, Item, Pat, Path, Stmt, Ty, Visibility); - -impl<T: AstDeref<Target: HasSpan>> HasSpan for T { - fn span(&self) -> Span { - self.ast_deref().span() - } -} - -impl HasSpan for AttrItem { - fn span(&self) -> Span { - self.span() - } -} - /// A trait for AST nodes having (or not having) collected tokens. pub trait HasTokens { fn tokens(&self) -> Option<&LazyAttrTokenStream>; diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 65f1b5dbaf5..088ae9ba441 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -202,7 +202,8 @@ impl Attribute { } } - pub fn tokens(&self) -> TokenStream { + // Named `get_tokens` to distinguish it from the `<Attribute as HasTokens>::tokens` method. + pub fn get_tokens(&self) -> TokenStream { match &self.kind { AttrKind::Normal(normal) => TokenStream::new( normal diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 7ca950e50e6..846a108091f 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -44,7 +44,7 @@ pub mod tokenstream; pub mod visit; pub use self::ast::*; -pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasSpan, HasTokens}; +pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasTokens}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index f816375b912..cbf21317f1a 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -704,7 +704,7 @@ fn visit_attr_tt<T: MutVisitor>(tt: &mut AttrTokenTree, vis: &mut T) { visit_attr_tts(tts, vis); visit_delim_span(dspan, vis); } - AttrTokenTree::Attributes(AttributesData { attrs, tokens }) => { + AttrTokenTree::AttrsTarget(AttrsTarget { attrs, tokens }) => { visit_attrs(attrs, vis); visit_lazy_tts_opt_mut(Some(tokens), vis); } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 655c18e4559..ee068f19332 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -14,7 +14,7 @@ //! ownership of the original. use crate::ast::{AttrStyle, StmtKind}; -use crate::ast_traits::{HasAttrs, HasSpan, HasTokens}; +use crate::ast_traits::{HasAttrs, HasTokens}; use crate::token::{self, Delimiter, Nonterminal, Token, TokenKind}; use crate::AttrVec; @@ -170,8 +170,8 @@ pub enum AttrTokenTree { Delimited(DelimSpan, DelimSpacing, Delimiter, AttrTokenStream), /// Stores the attributes for an attribute target, /// along with the tokens for that attribute target. - /// See `AttributesData` for more information - Attributes(AttributesData), + /// See `AttrsTarget` for more information + AttrsTarget(AttrsTarget), } impl AttrTokenStream { @@ -180,7 +180,7 @@ impl AttrTokenStream { } /// Converts this `AttrTokenStream` to a plain `Vec<TokenTree>`. - /// During conversion, `AttrTokenTree::Attributes` get 'flattened' + /// During conversion, `AttrTokenTree::AttrsTarget` get 'flattened' /// back to a `TokenStream` of the form `outer_attr attr_target`. /// If there are inner attributes, they are inserted into the proper /// place in the attribute target tokens. @@ -199,13 +199,13 @@ impl AttrTokenStream { TokenStream::new(stream.to_token_trees()), )) } - AttrTokenTree::Attributes(data) => { - let idx = data + AttrTokenTree::AttrsTarget(target) => { + let idx = target .attrs .partition_point(|attr| matches!(attr.style, crate::AttrStyle::Outer)); - let (outer_attrs, inner_attrs) = data.attrs.split_at(idx); + let (outer_attrs, inner_attrs) = target.attrs.split_at(idx); - let mut target_tokens = data.tokens.to_attr_token_stream().to_token_trees(); + let mut target_tokens = target.tokens.to_attr_token_stream().to_token_trees(); if !inner_attrs.is_empty() { let mut found = false; // Check the last two trees (to account for a trailing semi) @@ -227,7 +227,7 @@ impl AttrTokenStream { let mut stream = TokenStream::default(); for inner_attr in inner_attrs { - stream.push_stream(inner_attr.tokens()); + stream.push_stream(inner_attr.get_tokens()); } stream.push_stream(delim_tokens.clone()); *tree = TokenTree::Delimited(*span, *spacing, *delim, stream); @@ -242,7 +242,7 @@ impl AttrTokenStream { ); } for attr in outer_attrs { - res.extend(attr.tokens().0.iter().cloned()); + res.extend(attr.get_tokens().0.iter().cloned()); } res.extend(target_tokens); } @@ -262,7 +262,7 @@ impl AttrTokenStream { /// have an `attrs` field containing the `#[cfg(FALSE)]` attr, /// and a `tokens` field storing the (unparsed) tokens `struct Foo {}` #[derive(Clone, Debug, Encodable, Decodable)] -pub struct AttributesData { +pub struct AttrsTarget { /// Attributes, both outer and inner. /// These are stored in the original order that they were parsed in. pub attrs: AttrVec, @@ -436,17 +436,17 @@ impl TokenStream { TokenStream::new(vec![TokenTree::token_alone(kind, span)]) } - pub fn from_ast(node: &(impl HasAttrs + HasSpan + HasTokens + fmt::Debug)) -> TokenStream { + pub fn from_ast(node: &(impl HasAttrs + HasTokens + fmt::Debug)) -> TokenStream { let Some(tokens) = node.tokens() else { - panic!("missing tokens for node at {:?}: {:?}", node.span(), node); + panic!("missing tokens for node: {:?}", node); }; let attrs = node.attrs(); let attr_stream = if attrs.is_empty() { tokens.to_attr_token_stream() } else { - let attr_data = - AttributesData { attrs: attrs.iter().cloned().collect(), tokens: tokens.clone() }; - AttrTokenStream::new(vec![AttrTokenTree::Attributes(attr_data)]) + let target = + AttrsTarget { attrs: attrs.iter().cloned().collect(), tokens: tokens.clone() }; + AttrTokenStream::new(vec![AttrTokenTree::AttrsTarget(target)]) }; TokenStream::new(attr_stream.to_token_trees()) } @@ -765,6 +765,7 @@ mod size_asserts { static_assert_size!(AttrTokenStream, 8); static_assert_size!(AttrTokenTree, 32); static_assert_size!(LazyAttrTokenStream, 8); + static_assert_size!(Option<LazyAttrTokenStream>, 8); // must be small, used in many AST nodes static_assert_size!(TokenStream, 8); static_assert_size!(TokenTree, 32); // tidy-alphabetical-end diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 36ba1d81be6..d258c68b959 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -727,6 +727,12 @@ impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> } self.mutate_place(loc, (*destination, span), Deep, flow_state); } + TerminatorKind::TailCall { func, args, fn_span: _ } => { + self.consume_operand(loc, (func, span), flow_state); + for arg in args { + self.consume_operand(loc, (&arg.node, arg.span), flow_state); + } + } TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { self.consume_operand(loc, (cond, span), flow_state); if let AssertKind::BoundsCheck { len, index } = &**msg { @@ -813,9 +819,8 @@ impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R> TerminatorKind::UnwindResume | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::CoroutineDrop => { - // Returning from the function implicitly kills storage for all locals and statics. - // Often, the storage will already have been killed by an explicit // StorageDead, but we don't always emit those (notably on unwind paths), // so this "extra check" serves as a kind of backup. let borrow_set = self.borrow_set.clone(); diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs index 6979910a02d..30dfc4c21b0 100644 --- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs @@ -125,6 +125,12 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> { } self.mutate_place(location, *destination, Deep); } + TerminatorKind::TailCall { func, args, .. } => { + self.consume_operand(location, func); + for arg in args { + self.consume_operand(location, &arg.node); + } + } TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => { self.consume_operand(location, cond); use rustc_middle::mir::AssertKind; diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index aa25e3adf28..db4b5209145 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -38,6 +38,7 @@ use rustc_span::def_id::CRATE_DEF_ID; use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::DUMMY_SP; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints; use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; @@ -49,6 +50,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces; use rustc_mir_dataflow::move_paths::MoveData; use rustc_mir_dataflow::ResultsCursor; +use crate::renumber::RegionCtxt; use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst}; use crate::{ borrow_set::BorrowSet, @@ -1352,7 +1354,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } // FIXME: check the values } - TerminatorKind::Call { func, args, destination, call_source, target, .. } => { + TerminatorKind::Call { func, args, .. } + | TerminatorKind::TailCall { func, args, .. } => { + let call_source = match term.kind { + TerminatorKind::Call { call_source, .. } => call_source, + TerminatorKind::TailCall { .. } => CallSource::Normal, + _ => unreachable!(), + }; + self.check_operand(func, term_location); for arg in args { self.check_operand(&arg.node, term_location); @@ -1425,7 +1434,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); } - self.check_call_dest(body, term, &sig, *destination, *target, term_location); + if let TerminatorKind::Call { destination, target, .. } = term.kind { + self.check_call_dest(body, term, &sig, destination, target, term_location); + } // The ordinary liveness rules will ensure that all // regions in the type of the callee are live here. We @@ -1443,7 +1454,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { .add_location(region_vid, term_location); } - self.check_call_inputs(body, term, func, &sig, args, term_location, *call_source); + self.check_call_inputs(body, term, func, &sig, args, term_location, call_source); } TerminatorKind::Assert { cond, msg, .. } => { self.check_operand(cond, term_location); @@ -1675,6 +1686,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { span_mirbug!(self, block_data, "return on cleanup block") } } + TerminatorKind::TailCall { .. } => { + if is_cleanup { + span_mirbug!(self, block_data, "tailcall on cleanup block") + } + } TerminatorKind::CoroutineDrop { .. } => { if is_cleanup { span_mirbug!(self, block_data, "coroutine_drop in cleanup block") @@ -2319,7 +2335,57 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let cast_ty_from = CastTy::from_ty(ty_from); let cast_ty_to = CastTy::from_ty(*ty); match (cast_ty_from, cast_ty_to) { - (Some(CastTy::Ptr(_)), Some(CastTy::Ptr(_))) => (), + (Some(CastTy::Ptr(src)), Some(CastTy::Ptr(dst))) => { + let mut normalize = |t| self.normalize(t, location); + let src_tail = + tcx.struct_tail_with_normalize(src.ty, &mut normalize, || ()); + let dst_tail = + tcx.struct_tail_with_normalize(dst.ty, &mut normalize, || ()); + + // This checks (lifetime part of) vtable validity for pointer casts, + // which is irrelevant when there are aren't principal traits on both sides (aka only auto traits). + // + // Note that other checks (such as denying `dyn Send` -> `dyn Debug`) are in `rustc_hir_typeck`. + if let ty::Dynamic(src_tty, ..) = src_tail.kind() + && let ty::Dynamic(dst_tty, ..) = dst_tail.kind() + && src_tty.principal().is_some() + && dst_tty.principal().is_some() + { + // Remove auto traits. + // Auto trait checks are handled in `rustc_hir_typeck` as FCW. + let src_obj = tcx.mk_ty_from_kind(ty::Dynamic( + tcx.mk_poly_existential_predicates( + &src_tty.without_auto_traits().collect::<Vec<_>>(), + ), + tcx.lifetimes.re_static, + ty::Dyn, + )); + let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic( + tcx.mk_poly_existential_predicates( + &dst_tty.without_auto_traits().collect::<Vec<_>>(), + ), + tcx.lifetimes.re_static, + ty::Dyn, + )); + + // Replace trait object lifetimes with fresh vars, to allow casts like + // `*mut dyn FnOnce() + 'a` -> `*mut dyn FnOnce() + 'static`, + let src_obj = + freshen_single_trait_object_lifetime(self.infcx, src_obj); + let dst_obj = + freshen_single_trait_object_lifetime(self.infcx, dst_obj); + + debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj); + + self.eq_types( + src_obj, + dst_obj, + location.to_locations(), + ConstraintCategory::Cast { unsize_to: None }, + ) + .unwrap(); + } + } _ => { span_mirbug!( self, @@ -2842,3 +2908,16 @@ impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> { Ok(output) } } + +fn freshen_single_trait_object_lifetime<'tcx>( + infcx: &BorrowckInferCtxt<'tcx>, + ty: Ty<'tcx>, +) -> Ty<'tcx> { + let &ty::Dynamic(tty, _, dyn_kind @ ty::Dyn) = ty.kind() else { bug!("expected trait object") }; + + let fresh = infcx + .next_region_var(rustc_infer::infer::RegionVariableOrigin::MiscVariable(DUMMY_SP), || { + RegionCtxt::Unknown + }); + infcx.tcx.mk_ty_from_kind(ty::Dynamic(tty, fresh, dyn_kind)) +} diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 884cebf1939..b09975c0ba7 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -193,7 +193,7 @@ impl CfgEval<'_> { // Re-parse the tokens, setting the `capture_cfg` flag to save extra information // to the captured `AttrTokenStream` (specifically, we capture - // `AttrTokenTree::AttributesData` for all occurrences of `#[cfg]` and `#[cfg_attr]`) + // `AttrTokenTree::AttrsTarget` for all occurrences of `#[cfg]` and `#[cfg_attr]`) let mut parser = Parser::new(&self.0.sess.psess, orig_tokens, None); parser.capture_cfg = true; match parse_annotatable_with(&mut parser) { diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index c5b4277015a..5adbbb09ac8 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -491,6 +491,11 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { ) }); } + // FIXME(explicit_tail_calls): add support for tail calls to the cranelift backend, once cranelift supports tail calls + TerminatorKind::TailCall { fn_span, .. } => span_bug!( + *fn_span, + "tail calls are not yet supported in `rustc_codegen_cranelift` backend" + ), TerminatorKind::InlineAsm { template, operands, diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 9f7b95261d5..fd34ed88c0b 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -567,6 +567,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>( { return None; } + TerminatorKind::TailCall { .. } => return None, TerminatorKind::Call { .. } => {} } } diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 0577ba32ffd..ac2b6ca4e95 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -281,6 +281,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::CoroutineDrop | TerminatorKind::Unreachable | TerminatorKind::SwitchInt { .. } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 4adb95f85d6..6a5525dc2b3 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1389,6 +1389,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn_span, mergeable_succ(), ), + mir::TerminatorKind::TailCall { .. } => { + // FIXME(explicit_tail_calls): implement tail calls in ssa backend + span_bug!( + terminator.source_info.span, + "`TailCall` terminator is not yet supported by `rustc_codegen_ssa`" + ) + } mir::TerminatorKind::CoroutineDrop | mir::TerminatorKind::Yield { .. } => { bug!("coroutine ops in codegen") } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 0d8a17775dd..412effba32d 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -135,6 +135,8 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> { ccx: &'mir ConstCx<'mir, 'tcx>, tainted_by_errors: Option<ErrorGuaranteed>, ) -> ConstQualifs { + // FIXME(explicit_tail_calls): uhhhh I think we can return without return now, does it change anything + // Find the `Return` terminator if one exists. // // If no `Return` terminator exists, this MIR is divergent. Just return the conservative @@ -711,7 +713,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { self.super_terminator(terminator, location); match &terminator.kind { - TerminatorKind::Call { func, args, fn_span, call_source, .. } => { + TerminatorKind::Call { func, args, fn_span, .. } + | TerminatorKind::TailCall { func, args, fn_span, .. } => { + let call_source = match terminator.kind { + TerminatorKind::Call { call_source, .. } => call_source, + TerminatorKind::TailCall { .. } => CallSource::Normal, + _ => unreachable!(), + }; + let ConstCx { tcx, body, param_env, .. } = *self.ccx; let caller = self.def_id(); @@ -783,7 +792,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { callee, args: fn_args, span: *fn_span, - call_source: *call_source, + call_source, feature: Some(if tcx.features().const_trait_impl { sym::effects } else { @@ -830,7 +839,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { callee, args: fn_args, span: *fn_span, - call_source: *call_source, + call_source, feature: None, }); return; diff --git a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs index f0763f1e490..f5e745454ab 100644 --- a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs +++ b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs @@ -108,6 +108,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> { mir::TerminatorKind::UnwindTerminate(_) | mir::TerminatorKind::Call { .. } + | mir::TerminatorKind::TailCall { .. } | mir::TerminatorKind::Assert { .. } | mir::TerminatorKind::FalseEdge { .. } | mir::TerminatorKind::FalseUnwind { .. } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 830c4bd3e26..6d3e5ea1031 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -26,8 +26,8 @@ use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayou use super::{ err_inval, throw_inval, throw_ub, throw_ub_custom, throw_unsup, GlobalId, Immediate, InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, MemoryKind, - OpTy, Operand, Place, PlaceTy, Pointer, PointerArithmetic, Projectable, Provenance, Scalar, - StackPopJump, + OpTy, Operand, Place, PlaceTy, Pointer, PointerArithmetic, Projectable, Provenance, + ReturnAction, Scalar, }; use crate::errors; use crate::util; @@ -159,6 +159,19 @@ pub enum StackPopCleanup { Root { cleanup: bool }, } +/// Return type of [`InterpCx::pop_stack_frame`]. +pub struct StackPopInfo<'tcx, Prov: Provenance> { + /// Additional information about the action to be performed when returning from the popped + /// stack frame. + pub return_action: ReturnAction, + + /// [`return_to_block`](Frame::return_to_block) of the popped stack frame. + pub return_to_block: StackPopCleanup, + + /// [`return_place`](Frame::return_place) of the popped stack frame. + pub return_place: MPlaceTy<'tcx, Prov>, +} + /// State of a local variable including a memoized layout #[derive(Clone)] pub struct LocalState<'tcx, Prov: Provenance = CtfeProvenance> { @@ -803,14 +816,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { return_to_block: StackPopCleanup, ) -> InterpResult<'tcx> { trace!("body: {:#?}", body); + + // First push a stack frame so we have access to the local args + self.push_new_stack_frame(instance, body, return_to_block, return_place.clone())?; + + self.after_stack_frame_push(instance, body)?; + + Ok(()) + } + + /// Creates a new stack frame, initializes it and pushes it onto the stack. + /// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame). + fn push_new_stack_frame( + &mut self, + instance: ty::Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, + return_to_block: StackPopCleanup, + return_place: MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx> { let dead_local = LocalState { value: LocalValue::Dead, layout: Cell::new(None) }; let locals = IndexVec::from_elem(dead_local, &body.local_decls); - // First push a stack frame so we have access to the local args let pre_frame = Frame { body, loc: Right(body.span), // Span used for errors caused during preamble. return_to_block, - return_place: return_place.clone(), + return_place, locals, instance, tracing_span: SpanGuard::new(), @@ -819,6 +849,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let frame = M::init_frame(self, pre_frame)?; self.stack_mut().push(frame); + Ok(()) + } + + /// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame). + fn after_stack_frame_push( + &mut self, + instance: ty::Instance<'tcx>, + body: &'tcx mir::Body<'tcx>, + ) -> InterpResult<'tcx> { // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check). for &const_ in &body.required_consts { let c = @@ -839,6 +878,61 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(()) } + /// Pops a stack frame from the stack and returns some information about it. + /// + /// This also deallocates locals, if necessary. + /// + /// [`M::before_stack_pop`] should be called before calling this function. + /// [`M::after_stack_pop`] is called by this function automatically. + /// + /// [`M::before_stack_pop`]: Machine::before_stack_pop + /// [`M::after_stack_pop`]: Machine::after_stack_pop + pub fn pop_stack_frame( + &mut self, + unwinding: bool, + ) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> { + let cleanup = self.cleanup_current_frame_locals()?; + + let frame = + self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); + + let return_to_block = frame.return_to_block; + let return_place = frame.return_place.clone(); + + let return_action; + if cleanup { + return_action = M::after_stack_pop(self, frame, unwinding)?; + assert_ne!(return_action, ReturnAction::NoCleanup); + } else { + return_action = ReturnAction::NoCleanup; + }; + + Ok(StackPopInfo { return_action, return_to_block, return_place }) + } + + /// A private helper for [`pop_stack_frame`](InterpCx::pop_stack_frame). + /// Returns `true` if cleanup has been done, `false` otherwise. + fn cleanup_current_frame_locals(&mut self) -> InterpResult<'tcx, bool> { + // Cleanup: deallocate locals. + // Usually we want to clean up (deallocate locals), but in a few rare cases we don't. + // We do this while the frame is still on the stack, so errors point to the callee. + let return_to_block = self.frame().return_to_block; + let cleanup = match return_to_block { + StackPopCleanup::Goto { .. } => true, + StackPopCleanup::Root { cleanup, .. } => cleanup, + }; + + if cleanup { + // We need to take the locals out, since we need to mutate while iterating. + let locals = mem::take(&mut self.frame_mut().locals); + for local in &locals { + self.deallocate_local(local.value)?; + } + } + + Ok(cleanup) + } + /// Jump to the given block. #[inline] pub fn go_to_block(&mut self, target: mir::BasicBlock) { @@ -886,7 +980,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } /// Pops the current frame from the stack, deallocating the - /// memory for allocated locals. + /// memory for allocated locals, and jumps to an appropriate place. /// /// If `unwinding` is `false`, then we are performing a normal return /// from a function. In this case, we jump back into the frame of the caller, @@ -899,7 +993,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// The cleanup block ends with a special `Resume` terminator, which will /// cause us to continue unwinding. #[instrument(skip(self), level = "debug")] - pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> { + pub(super) fn return_from_current_stack_frame( + &mut self, + unwinding: bool, + ) -> InterpResult<'tcx> { info!( "popping stack frame ({})", if unwinding { "during unwinding" } else { "returning from function" } @@ -947,45 +1044,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(()) }; - // Cleanup: deallocate locals. - // Usually we want to clean up (deallocate locals), but in a few rare cases we don't. - // We do this while the frame is still on the stack, so errors point to the callee. - let return_to_block = self.frame().return_to_block; - let cleanup = match return_to_block { - StackPopCleanup::Goto { .. } => true, - StackPopCleanup::Root { cleanup, .. } => cleanup, - }; - if cleanup { - // We need to take the locals out, since we need to mutate while iterating. - let locals = mem::take(&mut self.frame_mut().locals); - for local in &locals { - self.deallocate_local(local.value)?; - } - } - // All right, now it is time to actually pop the frame. - // Note that its locals are gone already, but that's fine. - let frame = - self.stack_mut().pop().expect("tried to pop a stack frame, but there were none"); + let stack_pop_info = self.pop_stack_frame(unwinding)?; + // Report error from return value copy, if any. copy_ret_result?; - // If we are not doing cleanup, also skip everything else. - if !cleanup { - assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked"); - assert!(!unwinding, "tried to skip cleanup during unwinding"); - // Skip machine hook. - return Ok(()); - } - if M::after_stack_pop(self, frame, unwinding)? == StackPopJump::NoJump { - // The hook already did everything. - return Ok(()); + match stack_pop_info.return_action { + ReturnAction::Normal => {} + ReturnAction::NoJump => { + // The hook already did everything. + return Ok(()); + } + ReturnAction::NoCleanup => { + // If we are not doing cleanup, also skip everything else. + assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked"); + assert!(!unwinding, "tried to skip cleanup during unwinding"); + // Skip machine hook. + return Ok(()); + } } // Normal return, figure out where to jump. if unwinding { // Follow the unwind edge. - let unwind = match return_to_block { + let unwind = match stack_pop_info.return_to_block { StackPopCleanup::Goto { unwind, .. } => unwind, StackPopCleanup::Root { .. } => { panic!("encountered StackPopCleanup::Root when unwinding!") @@ -995,7 +1078,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.unwind_to_block(unwind) } else { // Follow the normal return edge. - match return_to_block { + match stack_pop_info.return_to_block { StackPopCleanup::Goto { ret, .. } => self.return_to_block(ret), StackPopCleanup::Root { .. } => { assert!( diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index e91ab7c1791..7f2e9ce06a5 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -23,10 +23,11 @@ use super::{ MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance, }; -/// Data returned by Machine::stack_pop, -/// to provide further control over the popping of the stack frame +/// Data returned by [`Machine::after_stack_pop`], and consumed by +/// [`InterpCx::return_from_current_stack_frame`] to determine what actions should be done when +/// returning from a stack frame. #[derive(Eq, PartialEq, Debug, Copy, Clone)] -pub enum StackPopJump { +pub enum ReturnAction { /// Indicates that no special handling should be /// done - we'll either return normally or unwind /// based on the terminator for the function @@ -36,6 +37,9 @@ pub enum StackPopJump { /// Indicates that we should *not* jump to the return/unwind address, as the callback already /// took care of everything. NoJump, + + /// Returned by [`InterpCx::pop_stack_frame`] when no cleanup should be done. + NoCleanup, } /// Whether this kind of memory is allowed to leak @@ -522,10 +526,10 @@ pub trait Machine<'tcx>: Sized { _ecx: &mut InterpCx<'tcx, Self>, _frame: Frame<'tcx, Self::Provenance, Self::FrameExtra>, unwinding: bool, - ) -> InterpResult<'tcx, StackPopJump> { + ) -> InterpResult<'tcx, ReturnAction> { // By default, we do not support unwinding from panics assert!(!unwinding); - Ok(StackPopJump::Normal) + Ok(ReturnAction::Normal) } /// Called immediately after actual memory was allocated for a local diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 7d7b421f869..f703c6fbe3e 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -26,7 +26,7 @@ pub use self::intern::{ intern_const_alloc_for_constprop, intern_const_alloc_recursive, HasStaticRootDefId, InternKind, InternResult, }; -pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump}; +pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, ReturnAction}; pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind}; pub use self::operand::{ImmTy, Immediate, OpTy, Readable}; pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable}; diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 1baf62baa81..b3124dfdfbc 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -32,7 +32,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // We are unwinding and this fn has no cleanup code. // Just go on unwinding. trace!("unwinding: skipping frame"); - self.pop_stack_frame(/* unwinding */ true)?; + self.return_from_current_stack_frame(/* unwinding */ true)?; return Ok(true); }; let basic_block = &self.body().basic_blocks[loc.block]; diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index 68acddf63d8..25f6bd64055 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -4,9 +4,8 @@ use either::Either; use rustc_middle::ty::TyCtxt; use tracing::trace; -use rustc_middle::span_bug; use rustc_middle::{ - mir, + bug, mir, span_bug, ty::{ self, layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout}, @@ -26,7 +25,10 @@ use super::{ InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Projectable, Provenance, Scalar, StackPopCleanup, }; -use crate::fluent_generated as fluent; +use crate::{ + fluent_generated as fluent, + interpret::{eval_context::StackPopInfo, ReturnAction}, +}; /// An argment passed to a function. #[derive(Clone, Debug)] @@ -47,6 +49,15 @@ impl<'tcx, Prov: Provenance> FnArg<'tcx, Prov> { } } +struct EvaluatedCalleeAndArgs<'tcx, M: Machine<'tcx>> { + callee: FnVal<'tcx, M::ExtraFnVal>, + args: Vec<FnArg<'tcx, M::Provenance>>, + fn_sig: ty::FnSig<'tcx>, + fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>, + /// True if the function is marked as `#[track_caller]` ([`ty::InstanceKind::requires_caller_location`]) + with_caller_location: bool, +} + impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// Make a copy of the given fn_arg. Any `InPlace` are degenerated to copies, no protection of the /// original memory occurs. @@ -84,7 +95,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { use rustc_middle::mir::TerminatorKind::*; match terminator.kind { Return => { - self.pop_stack_frame(/* unwinding */ false)? + self.return_from_current_stack_frame(/* unwinding */ false)? } Goto { target } => self.go_to_block(target), @@ -124,40 +135,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } => { let old_stack = self.frame_idx(); let old_loc = self.frame().loc; - let func = self.eval_operand(func, None)?; - let args = self.eval_fn_call_arguments(args)?; - - let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx); - let fn_sig = - self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder); - let extra_args = &args[fn_sig.inputs().len()..]; - let extra_args = - self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty)); - - let (fn_val, fn_abi, with_caller_location) = match *func.layout.ty.kind() { - ty::FnPtr(_sig) => { - let fn_ptr = self.read_pointer(&func)?; - let fn_val = self.get_ptr_fn(fn_ptr)?; - (fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false) - } - ty::FnDef(def_id, args) => { - let instance = self.resolve(def_id, args)?; - ( - FnVal::Instance(instance), - self.fn_abi_of_instance(instance, extra_args)?, - instance.def.requires_caller_location(*self.tcx), - ) - } - _ => span_bug!( - terminator.source_info.span, - "invalid callee of type {}", - func.layout.ty - ), - }; + + let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = + self.eval_callee_and_args(terminator, func, args)?; let destination = self.force_allocation(&self.eval_place(destination)?)?; self.eval_fn_call( - fn_val, + callee, (fn_sig.abi, fn_abi), &args, with_caller_location, @@ -172,6 +156,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + TailCall { ref func, ref args, fn_span: _ } => { + let old_frame_idx = self.frame_idx(); + + let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } = + self.eval_callee_and_args(terminator, func, args)?; + + self.eval_fn_tail_call(callee, (fn_sig.abi, fn_abi), &args, with_caller_location)?; + + if self.frame_idx() != old_frame_idx { + span_bug!( + terminator.source_info.span, + "evaluating this tail call pushed a new stack frame" + ); + } + } + Drop { place, target, unwind, replace: _ } => { let place = self.eval_place(place)?; let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); @@ -209,7 +209,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { trace!("unwinding: resuming from cleanup"); // By definition, a Resume terminator means // that we're unwinding - self.pop_stack_frame(/* unwinding */ true)?; + self.return_from_current_stack_frame(/* unwinding */ true)?; return Ok(()); } @@ -514,6 +514,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Ok(()) } + /// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the + /// necessary information about callee and arguments to make a call. + fn eval_callee_and_args( + &self, + terminator: &mir::Terminator<'tcx>, + func: &mir::Operand<'tcx>, + args: &[Spanned<mir::Operand<'tcx>>], + ) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> { + let func = self.eval_operand(func, None)?; + let args = self.eval_fn_call_arguments(args)?; + + let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx); + let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder); + let extra_args = &args[fn_sig.inputs().len()..]; + let extra_args = + self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty)); + + let (callee, fn_abi, with_caller_location) = match *func.layout.ty.kind() { + ty::FnPtr(_sig) => { + let fn_ptr = self.read_pointer(&func)?; + let fn_val = self.get_ptr_fn(fn_ptr)?; + (fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false) + } + ty::FnDef(def_id, args) => { + let instance = self.resolve(def_id, args)?; + ( + FnVal::Instance(instance), + self.fn_abi_of_instance(instance, extra_args)?, + instance.def.requires_caller_location(*self.tcx), + ) + } + _ => { + span_bug!(terminator.source_info.span, "invalid callee of type {}", func.layout.ty) + } + }; + + Ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location }) + } + /// Call this function -- pushing the stack frame and initializing the arguments. /// /// `caller_fn_abi` is used to determine if all the arguments are passed the proper way. @@ -924,6 +963,49 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } + pub(crate) fn eval_fn_tail_call( + &mut self, + fn_val: FnVal<'tcx, M::ExtraFnVal>, + (caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>), + args: &[FnArg<'tcx, M::Provenance>], + with_caller_location: bool, + ) -> InterpResult<'tcx> { + trace!("eval_fn_call: {:#?}", fn_val); + + // This is the "canonical" implementation of tails calls, + // a pop of the current stack frame, followed by a normal call + // which pushes a new stack frame, with the return address from + // the popped stack frame. + // + // Note that we are using `pop_stack_frame` and not `return_from_current_stack_frame`, + // as the latter "executes" the goto to the return block, but we don't want to, + // only the tail called function should return to the current return block. + M::before_stack_pop(self, self.frame())?; + + let StackPopInfo { return_action, return_to_block, return_place } = + self.pop_stack_frame(false)?; + + assert_eq!(return_action, ReturnAction::Normal); + + let StackPopCleanup::Goto { ret, unwind } = return_to_block else { + bug!("can't tailcall as root"); + }; + + // FIXME(explicit_tail_calls): + // we should check if both caller&callee can/n't unwind, + // see <https://github.com/rust-lang/rust/pull/113128#issuecomment-1614979803> + + self.eval_fn_call( + fn_val, + (caller_abi, caller_fn_abi), + args, + with_caller_location, + &return_place, + ret, + unwind, + ) + } + fn check_fn_target_features(&self, instance: ty::Instance<'tcx>) -> InterpResult<'tcx, ()> { // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988 let attrs = self.tcx.codegen_fn_attrs(instance.def_id()); diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index 0af80bc5c67..e6ca1bf7bc4 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -298,15 +298,21 @@ impl IntoDiagArg for hir::def::Namespace { } #[derive(Clone)] -pub struct DiagSymbolList(Vec<Symbol>); +pub struct DiagSymbolList<S = Symbol>(Vec<S>); -impl From<Vec<Symbol>> for DiagSymbolList { - fn from(v: Vec<Symbol>) -> Self { +impl<S> From<Vec<S>> for DiagSymbolList<S> { + fn from(v: Vec<S>) -> Self { DiagSymbolList(v) } } -impl IntoDiagArg for DiagSymbolList { +impl<S> FromIterator<S> for DiagSymbolList<S> { + fn from_iter<T: IntoIterator<Item = S>>(iter: T) -> Self { + iter.into_iter().collect::<Vec<_>>().into() + } +} + +impl<S: std::fmt::Display> IntoDiagArg for DiagSymbolList<S> { fn into_diag_arg(self) -> DiagArgValue { DiagArgValue::StrListSepByAnd( self.0.into_iter().map(|sym| Cow::Owned(format!("`{sym}`"))).collect(), diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 56cbb54fcec..40e16b45115 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -172,7 +172,7 @@ impl<'a> StripUnconfigured<'a> { fn configure_tokens(&self, stream: &AttrTokenStream) -> AttrTokenStream { fn can_skip(stream: &AttrTokenStream) -> bool { stream.0.iter().all(|tree| match tree { - AttrTokenTree::Attributes(_) => false, + AttrTokenTree::AttrsTarget(_) => false, AttrTokenTree::Token(..) => true, AttrTokenTree::Delimited(.., inner) => can_skip(inner), }) @@ -185,22 +185,22 @@ impl<'a> StripUnconfigured<'a> { let trees: Vec<_> = stream .0 .iter() - .flat_map(|tree| match tree.clone() { - AttrTokenTree::Attributes(mut data) => { - data.attrs.flat_map_in_place(|attr| self.process_cfg_attr(&attr)); + .filter_map(|tree| match tree.clone() { + AttrTokenTree::AttrsTarget(mut target) => { + target.attrs.flat_map_in_place(|attr| self.process_cfg_attr(&attr)); - if self.in_cfg(&data.attrs) { - data.tokens = LazyAttrTokenStream::new( - self.configure_tokens(&data.tokens.to_attr_token_stream()), + if self.in_cfg(&target.attrs) { + target.tokens = LazyAttrTokenStream::new( + self.configure_tokens(&target.tokens.to_attr_token_stream()), ); - Some(AttrTokenTree::Attributes(data)).into_iter() + Some(AttrTokenTree::AttrsTarget(target)) } else { - None.into_iter() + None } } AttrTokenTree::Delimited(sp, spacing, delim, mut inner) => { inner = self.configure_tokens(&inner); - Some(AttrTokenTree::Delimited(sp, spacing, delim, inner)).into_iter() + Some(AttrTokenTree::Delimited(sp, spacing, delim, inner)) } AttrTokenTree::Token( Token { @@ -220,9 +220,7 @@ impl<'a> StripUnconfigured<'a> { ) => { panic!("Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}", tree); } - AttrTokenTree::Token(token, spacing) => { - Some(AttrTokenTree::Token(token, spacing)).into_iter() - } + AttrTokenTree::Token(token, spacing) => Some(AttrTokenTree::Token(token, spacing)), }) .collect(); AttrTokenStream::new(trees) @@ -294,7 +292,7 @@ impl<'a> StripUnconfigured<'a> { attr: &Attribute, (item, item_span): (ast::AttrItem, Span), ) -> Attribute { - let orig_tokens = attr.tokens(); + let orig_tokens = attr.get_tokens(); // We are taking an attribute of the form `#[cfg_attr(pred, attr)]` // and producing an attribute of the form `#[attr]`. We @@ -310,12 +308,11 @@ impl<'a> StripUnconfigured<'a> { else { panic!("Bad tokens for attribute {attr:?}"); }; - let pound_span = pound_token.span; // We don't really have a good span to use for the synthesized `[]` // in `#[attr]`, so just use the span of the `#` token. let bracket_group = AttrTokenTree::Delimited( - DelimSpan::from_single(pound_span), + DelimSpan::from_single(pound_token.span), DelimSpacing::new(Spacing::JointHidden, Spacing::Alone), Delimiter::Bracket, item.tokens diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 25958e03028..dbbd948fd70 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -1,4 +1,4 @@ -use rustc_ast::token::{self, Delimiter, IdentIsRaw}; +use rustc_ast::token::{self, Delimiter, IdentIsRaw, Lit, Token, TokenKind}; use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree}; use rustc_ast::{LitIntType, LitKind}; use rustc_ast_pretty::pprust; @@ -6,9 +6,10 @@ use rustc_errors::{Applicability, PResult}; use rustc_macros::{Decodable, Encodable}; use rustc_session::parse::ParseSess; use rustc_span::symbol::Ident; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers"; +pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal"; /// A meta-variable expression, for expansions based on properties of meta-variables. #[derive(Debug, PartialEq, Encodable, Decodable)] @@ -51,11 +52,26 @@ impl MetaVarExpr { let mut result = Vec::new(); loop { let is_var = try_eat_dollar(&mut iter); - let element_ident = parse_ident(&mut iter, psess, outer_span)?; + let token = parse_token(&mut iter, psess, outer_span)?; let element = if is_var { - MetaVarExprConcatElem::Var(element_ident) + MetaVarExprConcatElem::Var(parse_ident_from_token(psess, token)?) + } else if let TokenKind::Literal(Lit { + kind: token::LitKind::Str, + symbol, + suffix: None, + }) = token.kind + { + MetaVarExprConcatElem::Literal(symbol) } else { - MetaVarExprConcatElem::Ident(element_ident) + match parse_ident_from_token(psess, token) { + Err(err) => { + err.cancel(); + return Err(psess + .dcx() + .struct_span_err(token.span, UNSUPPORTED_CONCAT_ELEM_ERR)); + } + Ok(elem) => MetaVarExprConcatElem::Ident(elem), + } }; result.push(element); if iter.look_ahead(0).is_none() { @@ -105,11 +121,13 @@ impl MetaVarExpr { #[derive(Debug, Decodable, Encodable, PartialEq)] pub(crate) enum MetaVarExprConcatElem { - /// There is NO preceding dollar sign, which means that this identifier should be interpreted - /// as a literal. + /// Identifier WITHOUT a preceding dollar sign, which means that this identifier should be + /// interpreted as a literal. Ident(Ident), - /// There is a preceding dollar sign, which means that this identifier should be expanded - /// and interpreted as a variable. + /// For example, a number or a string. + Literal(Symbol), + /// Identifier WITH a preceding dollar sign, which means that this identifier should be + /// expanded and interpreted as a variable. Var(Ident), } @@ -158,7 +176,7 @@ fn parse_depth<'psess>( span: Span, ) -> PResult<'psess, usize> { let Some(tt) = iter.next() else { return Ok(0) }; - let TokenTree::Token(token::Token { kind: token::TokenKind::Literal(lit), .. }, _) = tt else { + let TokenTree::Token(Token { kind: TokenKind::Literal(lit), .. }, _) = tt else { return Err(psess .dcx() .struct_span_err(span, "meta-variable expression depth must be a literal")); @@ -180,12 +198,14 @@ fn parse_ident<'psess>( psess: &'psess ParseSess, fallback_span: Span, ) -> PResult<'psess, Ident> { - let Some(tt) = iter.next() else { - return Err(psess.dcx().struct_span_err(fallback_span, "expected identifier")); - }; - let TokenTree::Token(token, _) = tt else { - return Err(psess.dcx().struct_span_err(tt.span(), "expected identifier")); - }; + let token = parse_token(iter, psess, fallback_span)?; + parse_ident_from_token(psess, token) +} + +fn parse_ident_from_token<'psess>( + psess: &'psess ParseSess, + token: &Token, +) -> PResult<'psess, Ident> { if let Some((elem, is_raw)) = token.ident() { if let IdentIsRaw::Yes = is_raw { return Err(psess.dcx().struct_span_err(elem.span, RAW_IDENT_ERR)); @@ -205,10 +225,24 @@ fn parse_ident<'psess>( Err(err) } +fn parse_token<'psess, 't>( + iter: &mut RefTokenTreeCursor<'t>, + psess: &'psess ParseSess, + fallback_span: Span, +) -> PResult<'psess, &'t Token> { + let Some(tt) = iter.next() else { + return Err(psess.dcx().struct_span_err(fallback_span, UNSUPPORTED_CONCAT_ELEM_ERR)); + }; + let TokenTree::Token(token, _) = tt else { + return Err(psess.dcx().struct_span_err(tt.span(), UNSUPPORTED_CONCAT_ELEM_ERR)); + }; + Ok(token) +} + /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the /// iterator is not modified and the result is `false`. fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool { - if let Some(TokenTree::Token(token::Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) { + if let Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) { let _ = iter.next(); return true; } @@ -218,8 +252,7 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool { /// Tries to move the iterator forward returning `true` if there is a dollar sign. If not, then the /// iterator is not modified and the result is `false`. fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool { - if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) - { + if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) { let _ = iter.next(); return true; } @@ -232,8 +265,7 @@ fn eat_dollar<'psess>( psess: &'psess ParseSess, span: Span, ) -> PResult<'psess, ()> { - if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) - { + if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) { let _ = iter.next(); return Ok(()); } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index f935f1b77e0..9b4dc13c703 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -11,11 +11,13 @@ use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{pluralize, Diag, DiagCtxtHandle, PResult}; +use rustc_parse::lexer::nfc_normalize; use rustc_parse::parser::ParseNtResult; use rustc_session::parse::ParseSess; +use rustc_session::parse::SymbolGallery; use rustc_span::hygiene::{LocalExpnId, Transparency}; use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent}; -use rustc_span::{with_metavar_spans, Span, Symbol, SyntaxContext}; +use rustc_span::{with_metavar_spans, Span, SyntaxContext}; use smallvec::{smallvec, SmallVec}; use std::mem; @@ -312,7 +314,16 @@ pub(super) fn transcribe<'a>( // Replace meta-variable expressions with the result of their expansion. mbe::TokenTree::MetaVarExpr(sp, expr) => { - transcribe_metavar_expr(dcx, expr, interp, &mut marker, &repeats, &mut result, sp)?; + transcribe_metavar_expr( + dcx, + expr, + interp, + &mut marker, + &repeats, + &mut result, + sp, + &psess.symbol_gallery, + )?; } // If we are entering a new delimiter, we push its contents to the `stack` to be @@ -669,6 +680,7 @@ fn transcribe_metavar_expr<'a>( repeats: &[(usize, usize)], result: &mut Vec<TokenTree>, sp: &DelimSpan, + symbol_gallery: &SymbolGallery, ) -> PResult<'a, ()> { let mut visited_span = || { let mut span = sp.entire(); @@ -680,16 +692,26 @@ fn transcribe_metavar_expr<'a>( let mut concatenated = String::new(); for element in elements.into_iter() { let string = match element { - MetaVarExprConcatElem::Ident(ident) => ident.to_string(), - MetaVarExprConcatElem::Var(ident) => extract_ident(dcx, *ident, interp)?, + MetaVarExprConcatElem::Ident(elem) => elem.to_string(), + MetaVarExprConcatElem::Literal(elem) => elem.as_str().into(), + MetaVarExprConcatElem::Var(elem) => extract_ident(dcx, *elem, interp)?, }; concatenated.push_str(&string); } + let symbol = nfc_normalize(&concatenated); + let concatenated_span = visited_span(); + if !rustc_lexer::is_ident(symbol.as_str()) { + return Err(dcx.struct_span_err( + concatenated_span, + "`${concat(..)}` is not generating a valid identifier", + )); + } + symbol_gallery.insert(symbol, concatenated_span); // The current implementation marks the span as coming from the macro regardless of // contexts of the concatenated identifiers but this behavior may change in the // future. result.push(TokenTree::Token( - Token::from_ast_ident(Ident::new(Symbol::intern(&concatenated), visited_span())), + Token::from_ast_ident(Ident::new(symbol, concatenated_span)), Spacing::Alone, )); } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 0dbe64c3ea7..8a1ee9374c3 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -21,6 +21,7 @@ use rustc_middle::ty::{ use rustc_middle::ty::{GenericParamDefKind, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::Span; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::regions::InferCtxtRegionExt; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index f21aeb4c0b9..6282499883b 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -26,15 +26,12 @@ fn equate_intrinsic_type<'tcx>( n_cts: usize, sig: ty::PolyFnSig<'tcx>, ) { - let (own_counts, span) = match tcx.hir_node_by_def_id(def_id) { + let (generics, span) = match tcx.hir_node_by_def_id(def_id) { hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. }) | hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(.., generics, _), .. - }) => { - let own_counts = tcx.generics_of(def_id).own_counts(); - (own_counts, generics.span) - } + }) => (tcx.generics_of(def_id), generics.span), _ => { struct_span_code_err!(tcx.dcx(), span, E0622, "intrinsic must be a function") .with_span_label(span, "expected a function") @@ -42,6 +39,7 @@ fn equate_intrinsic_type<'tcx>( return; } }; + let own_counts = generics.own_counts(); let gen_count_ok = |found: usize, expected: usize, descr: &str| -> bool { if found != expected { @@ -57,9 +55,17 @@ fn equate_intrinsic_type<'tcx>( } }; + // the host effect param should be invisible as it shouldn't matter + // whether effects is enabled for the intrinsic provider crate. + let consts_count = if generics.host_effect_index.is_some() { + own_counts.consts - 1 + } else { + own_counts.consts + }; + if gen_count_ok(own_counts.lifetimes, n_lts, "lifetime") && gen_count_ok(own_counts.types, n_tps, "type") - && gen_count_ok(own_counts.consts, n_cts, "const") + && gen_count_ok(consts_count, n_cts, "const") { let _ = check_function_signature( tcx, diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 2230528a5ae..a0eede31eb0 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -39,6 +39,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _ use rustc_trait_selection::traits::{ self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc, }; +use rustc_type_ir::solve::NoSolution; use rustc_type_ir::TypeFlags; use std::cell::LazyCell; @@ -477,7 +478,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) { param_env, item_def_id, tcx.explicit_item_bounds(item_def_id) - .instantiate_identity_iter_copied() + .iter_identity_copied() .collect::<Vec<_>>(), &FxIndexSet::default(), gat_def_id, @@ -1204,17 +1205,16 @@ fn check_associated_type_bounds(wfcx: &WfCheckingCtxt<'_, '_>, item: ty::AssocIt let bounds = wfcx.tcx().explicit_item_bounds(item.def_id); debug!("check_associated_type_bounds: bounds={:?}", bounds); - let wf_obligations = - bounds.instantiate_identity_iter_copied().flat_map(|(bound, bound_span)| { - let normalized_bound = wfcx.normalize(span, None, bound); - traits::wf::clause_obligations( - wfcx.infcx, - wfcx.param_env, - wfcx.body_def_id, - normalized_bound, - bound_span, - ) - }); + let wf_obligations = bounds.iter_identity_copied().flat_map(|(bound, bound_span)| { + let normalized_bound = wfcx.normalize(span, None, bound); + traits::wf::clause_obligations( + wfcx.infcx, + wfcx.param_env, + wfcx.body_def_id, + normalized_bound, + bound_span, + ) + }); wfcx.register_obligations(wf_obligations); } @@ -1712,13 +1712,12 @@ fn receiver_is_valid<'tcx>( let cause = ObligationCause::new(span, wfcx.body_def_id, traits::ObligationCauseCode::MethodReceiver); - let can_eq_self = |ty| infcx.can_eq(wfcx.param_env, self_ty, ty); - - // `self: Self` is always valid. - if can_eq_self(receiver_ty) { - if let Err(err) = wfcx.eq(&cause, wfcx.param_env, self_ty, receiver_ty) { - infcx.err_ctxt().report_mismatched_types(&cause, self_ty, receiver_ty, err).emit(); - } + // Special case `receiver == self_ty`, which doesn't necessarily require the `Receiver` lang item. + if let Ok(()) = wfcx.infcx.commit_if_ok(|_| { + let ocx = ObligationCtxt::new(wfcx.infcx); + ocx.eq(&cause, wfcx.param_env, self_ty, receiver_ty)?; + if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) } + }) { return true; } @@ -1729,58 +1728,51 @@ fn receiver_is_valid<'tcx>( autoderef = autoderef.include_raw_pointers(); } - // The first type is `receiver_ty`, which we know its not equal to `self_ty`; skip it. - autoderef.next(); - let receiver_trait_def_id = tcx.require_lang_item(LangItem::Receiver, Some(span)); // Keep dereferencing `receiver_ty` until we get to `self_ty`. - loop { - if let Some((potential_self_ty, _)) = autoderef.next() { - debug!( - "receiver_is_valid: potential self type `{:?}` to match `{:?}`", - potential_self_ty, self_ty - ); - - if can_eq_self(potential_self_ty) { - wfcx.register_obligations(autoderef.into_obligations()); + while let Some((potential_self_ty, _)) = autoderef.next() { + debug!( + "receiver_is_valid: potential self type `{:?}` to match `{:?}`", + potential_self_ty, self_ty + ); - if let Err(err) = wfcx.eq(&cause, wfcx.param_env, self_ty, potential_self_ty) { - infcx - .err_ctxt() - .report_mismatched_types(&cause, self_ty, potential_self_ty, err) - .emit(); - } + // Check if the self type unifies. If it does, then commit the result + // since it may have region side-effects. + if let Ok(()) = wfcx.infcx.commit_if_ok(|_| { + let ocx = ObligationCtxt::new(wfcx.infcx); + ocx.eq(&cause, wfcx.param_env, self_ty, potential_self_ty)?; + if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) } + }) { + wfcx.register_obligations(autoderef.into_obligations()); + return true; + } + // Without `feature(arbitrary_self_types)`, we require that each step in the + // deref chain implement `receiver`. + if !arbitrary_self_types_enabled { + if !receiver_is_implemented( + wfcx, + receiver_trait_def_id, + cause.clone(), + potential_self_ty, + ) { + // We cannot proceed. break; - } else { - // Without `feature(arbitrary_self_types)`, we require that each step in the - // deref chain implement `receiver` - if !arbitrary_self_types_enabled - && !receiver_is_implemented( - wfcx, - receiver_trait_def_id, - cause.clone(), - potential_self_ty, - ) - { - return false; - } } - } else { - debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty); - return false; - } - } - // Without `feature(arbitrary_self_types)`, we require that `receiver_ty` implements `Receiver`. - if !arbitrary_self_types_enabled - && !receiver_is_implemented(wfcx, receiver_trait_def_id, cause.clone(), receiver_ty) - { - return false; + // Register the bound, in case it has any region side-effects. + wfcx.register_bound( + cause.clone(), + wfcx.param_env, + potential_self_ty, + receiver_trait_def_id, + ); + } } - true + debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty); + false } fn receiver_is_implemented<'tcx>( diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 5cb91603fd0..16f72f38d60 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -286,7 +286,7 @@ fn orphan_check<'tcx>( tcx: TyCtxt<'tcx>, impl_def_id: LocalDefId, mode: OrphanCheckMode, -) -> Result<(), OrphanCheckErr<'tcx, FxIndexSet<DefId>>> { +) -> Result<(), OrphanCheckErr<TyCtxt<'tcx>, FxIndexSet<DefId>>> { // We only accept this routine to be invoked on implementations // of a trait, not inherent implementations. let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); @@ -326,17 +326,16 @@ fn orphan_check<'tcx>( ty }; - Ok(ty) + Ok::<_, !>(ty) }; - let Ok(result) = traits::orphan_check_trait_ref::<!>( + let result = traits::orphan_check_trait_ref( &infcx, trait_ref, traits::InCrate::Local { mode }, lazily_normalize_ty, - ) else { - unreachable!() - }; + ) + .into_ok(); // (2) Try to map the remaining inference vars back to generic params. result.map_err(|err| match err { @@ -369,7 +368,7 @@ fn emit_orphan_check_error<'tcx>( tcx: TyCtxt<'tcx>, trait_ref: ty::TraitRef<'tcx>, impl_def_id: LocalDefId, - err: traits::OrphanCheckErr<'tcx, FxIndexSet<DefId>>, + err: traits::OrphanCheckErr<TyCtxt<'tcx>, FxIndexSet<DefId>>, ) -> ErrorGuaranteed { match err { traits::OrphanCheckErr::NonLocalInputType(tys) => { @@ -482,7 +481,7 @@ fn emit_orphan_check_error<'tcx>( fn lint_uncovered_ty_params<'tcx>( tcx: TyCtxt<'tcx>, - UncoveredTyParams { uncovered, local_ty }: UncoveredTyParams<'tcx, FxIndexSet<DefId>>, + UncoveredTyParams { uncovered, local_ty }: UncoveredTyParams<TyCtxt<'tcx>, FxIndexSet<DefId>>, impl_def_id: LocalDefId, ) { let hir_id = tcx.local_def_id_to_hir_id(impl_def_id); diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 41fbef48940..843e4d41e00 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1201,6 +1201,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { let is_marker = tcx.has_attr(def_id, sym::marker); let rustc_coinductive = tcx.has_attr(def_id, sym::rustc_coinductive); + let is_fundamental = tcx.has_attr(def_id, sym::fundamental); // FIXME: We could probably do way better attribute validation here. let mut skip_array_during_method_dispatch = false; @@ -1352,6 +1353,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef { has_auto_impl: is_auto, is_marker, is_coinductive: rustc_coinductive || is_auto, + is_fundamental, skip_array_during_method_dispatch, skip_boxed_slice_during_method_dispatch, specialization_kind, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 2a68d3915bb..02db0352daa 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -49,6 +49,7 @@ use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{sym, Span, DUMMY_SP}; use rustc_target::spec::abi; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, ObligationCtxt}; diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 44f1830a3b1..2f6b0a582e5 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -71,6 +71,7 @@ This API is completely unstable and subject to change. #![feature(rustdoc_internals)] #![feature(slice_partition_dedup)] #![feature(try_blocks)] +#![feature(unwrap_infallible)] // tidy-alphabetical-end #[macro_use] diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 3c5070bd006..239b0c44690 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -138,6 +138,11 @@ hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expecte hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}` hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}` +hir_typeck_ptr_cast_add_auto_to_object = adding {$traits_len -> + [1] an auto trait {$traits} + *[other] auto traits {$traits} +} to a trait object in a pointer cast may cause UB later on + hir_typeck_remove_semi_for_coerce = you might have meant to return the `match` expression hir_typeck_remove_semi_for_coerce_expr = this could be implicitly returned but it is a statement, not a tail expression hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this return type diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 53e44d6bcae..341d533492d 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -32,9 +32,9 @@ use super::FnCtxt; use crate::errors; use crate::type_error_struct; -use hir::ExprKind; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{codes::*, Applicability, Diag, ErrorGuaranteed}; -use rustc_hir as hir; +use rustc_hir::{self as hir, ExprKind}; use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::bug; use rustc_middle::mir::Mutability; @@ -44,7 +44,7 @@ use rustc_middle::ty::error::TypeError; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef}; use rustc_session::lint; -use rustc_span::def_id::{DefId, LOCAL_CRATE}; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_span::DUMMY_SP; @@ -73,7 +73,7 @@ enum PointerKind<'tcx> { /// No metadata attached, ie pointer to sized type or foreign type Thin, /// A trait object - VTable(Option<DefId>), + VTable(&'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>), /// Slice Length, /// The unsize info of this projection or opaque type @@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(match *t.kind() { ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())), + ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty)), ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() { None => Some(PointerKind::Thin), Some(f) => { @@ -755,7 +755,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { Err(CastError::IllegalCast) } - // ptr -> * + // ptr -> ptr (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast // ptr-addr-cast @@ -799,40 +799,126 @@ impl<'a, 'tcx> CastCheck<'tcx> { fn check_ptr_ptr_cast( &self, fcx: &FnCtxt<'a, 'tcx>, - m_expr: ty::TypeAndMut<'tcx>, - m_cast: ty::TypeAndMut<'tcx>, + m_src: ty::TypeAndMut<'tcx>, + m_dst: ty::TypeAndMut<'tcx>, ) -> Result<CastKind, CastError> { - debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_expr, m_cast); + debug!("check_ptr_ptr_cast m_src={m_src:?} m_dst={m_dst:?}"); // ptr-ptr cast. vtables must match. - let expr_kind = fcx.pointer_kind(m_expr.ty, self.span)?; - let cast_kind = fcx.pointer_kind(m_cast.ty, self.span)?; + let src_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_src.ty, self.span)?); + let dst_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_dst.ty, self.span)?); - let Some(cast_kind) = cast_kind else { + match (src_kind, dst_kind) { // We can't cast if target pointer kind is unknown - return Err(CastError::UnknownCastPtrKind); - }; + (_, None) => Err(CastError::UnknownCastPtrKind), + // Cast to thin pointer is OK + (_, Some(PointerKind::Thin)) => Ok(CastKind::PtrPtrCast), - // Cast to thin pointer is OK - if cast_kind == PointerKind::Thin { - return Ok(CastKind::PtrPtrCast); - } - - let Some(expr_kind) = expr_kind else { // We can't cast to fat pointer if source pointer kind is unknown - return Err(CastError::UnknownExprPtrKind); - }; + (None, _) => Err(CastError::UnknownExprPtrKind), + + // thin -> fat? report invalid cast (don't complain about vtable kinds) + (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast), + + // trait object -> trait object? need to do additional checks + (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => { + match (src_tty.principal(), dst_tty.principal()) { + // A<dyn Src<...> + SrcAuto> -> B<dyn Dst<...> + DstAuto>. need to make sure + // - `Src` and `Dst` traits are the same + // - traits have the same generic arguments + // - `SrcAuto` is a superset of `DstAuto` + (Some(src_principal), Some(dst_principal)) => { + let tcx = fcx.tcx; + + // Check that the traits are actually the same. + // The `dyn Src = dyn Dst` check below would suffice, + // but this may produce a better diagnostic. + // + // Note that trait upcasting goes through a different mechanism (`coerce_unsized`) + // and is unaffected by this check. + if src_principal.def_id() != dst_principal.def_id() { + return Err(CastError::DifferingKinds); + } - // thin -> fat? report invalid cast (don't complain about vtable kinds) - if expr_kind == PointerKind::Thin { - return Err(CastError::SizedUnsizedCast); - } + // We need to reconstruct trait object types. + // `m_src` and `m_dst` won't work for us here because they will potentially + // contain wrappers, which we do not care about. + // + // e.g. we want to allow `dyn T -> (dyn T,)`, etc. + // + // We also need to skip auto traits to emit an FCW and not an error. + let src_obj = tcx.mk_ty_from_kind(ty::Dynamic( + tcx.mk_poly_existential_predicates( + &src_tty.without_auto_traits().collect::<Vec<_>>(), + ), + tcx.lifetimes.re_erased, + ty::Dyn, + )); + let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic( + tcx.mk_poly_existential_predicates( + &dst_tty.without_auto_traits().collect::<Vec<_>>(), + ), + tcx.lifetimes.re_erased, + ty::Dyn, + )); - // vtable kinds must match - if fcx.tcx.erase_regions(cast_kind) == fcx.tcx.erase_regions(expr_kind) { - Ok(CastKind::PtrPtrCast) - } else { - Err(CastError::DifferingKinds) + // `dyn Src = dyn Dst`, this checks for matching traits/generics + fcx.demand_eqtype(self.span, src_obj, dst_obj); + + // Check that `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`. + // Emit an FCW otherwise. + let src_auto: FxHashSet<_> = src_tty + .auto_traits() + .chain( + tcx.supertrait_def_ids(src_principal.def_id()) + .filter(|def_id| tcx.trait_is_auto(*def_id)), + ) + .collect(); + + let added = dst_tty + .auto_traits() + .filter(|trait_did| !src_auto.contains(trait_did)) + .collect::<Vec<_>>(); + + if !added.is_empty() { + tcx.emit_node_span_lint( + lint::builtin::PTR_CAST_ADD_AUTO_TO_OBJECT, + self.expr.hir_id, + self.span, + errors::PtrCastAddAutoToObject { + traits_len: added.len(), + traits: { + let mut traits: Vec<_> = added + .into_iter() + .map(|trait_did| tcx.def_path_str(trait_did)) + .collect(); + + traits.sort(); + traits.into() + }, + }, + ) + } + + Ok(CastKind::PtrPtrCast) + } + + // dyn Auto -> dyn Auto'? ok. + (None, None) => Ok(CastKind::PtrPtrCast), + + // dyn Trait -> dyn Auto? should be ok, but we used to not allow it. + // FIXME: allow this + (Some(_), None) => Err(CastError::DifferingKinds), + + // dyn Auto -> dyn Trait? not ok. + (None, Some(_)) => Err(CastError::DifferingKinds), + } + } + + // fat -> fat? metadata kinds must match + (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(CastKind::PtrPtrCast), + + (_, _) => Err(CastError::DifferingKinds), } } diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index ac7ed3e26f9..53cf121dfeb 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -424,9 +424,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(trait_def_id) = trait_def_id { let found_kind = match closure_kind { hir::ClosureKind::Closure => self.tcx.fn_trait_kind_from_def_id(trait_def_id), - hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => { - self.tcx.async_fn_trait_kind_from_def_id(trait_def_id) - } + hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => self + .tcx + .async_fn_trait_kind_from_def_id(trait_def_id) + .or_else(|| self.tcx.fn_trait_kind_from_def_id(trait_def_id)), _ => None, }; @@ -470,14 +471,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // for closures and async closures, respectively. match closure_kind { hir::ClosureKind::Closure - if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() => {} + if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() => + { + self.extract_sig_from_projection(cause_span, projection) + } + hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) + if self.tcx.async_fn_trait_kind_from_def_id(trait_def_id).is_some() => + { + self.extract_sig_from_projection(cause_span, projection) + } + // It's possible we've passed the closure to a (somewhat out-of-fashion) + // `F: FnOnce() -> Fut, Fut: Future<Output = T>` style bound. Let's still + // guide inference here, since it's beneficial for the user. hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) - if self.tcx.async_fn_trait_kind_from_def_id(trait_def_id).is_some() => {} - _ => return None, + if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() => + { + self.extract_sig_from_projection_and_future_bound(cause_span, projection) + } + _ => None, } + } + + /// Given an `FnOnce::Output` or `AsyncFn::Output` projection, extract the args + /// and return type to infer a [`ty::PolyFnSig`] for the closure. + fn extract_sig_from_projection( + &self, + cause_span: Option<Span>, + projection: ty::PolyProjectionPredicate<'tcx>, + ) -> Option<ExpectedSig<'tcx>> { + let projection = self.resolve_vars_if_possible(projection); let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); - let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty); debug!(?arg_param_ty); let ty::Tuple(input_tys) = *arg_param_ty.kind() else { @@ -486,7 +510,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Since this is a return parameter type it is safe to unwrap. let ret_param_ty = projection.skip_binder().term.expect_type(); - let ret_param_ty = self.resolve_vars_if_possible(ret_param_ty); debug!(?ret_param_ty); let sig = projection.rebind(self.tcx.mk_fn_sig( @@ -500,6 +523,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(ExpectedSig { cause_span, sig }) } + /// When an async closure is passed to a function that has a "two-part" `Fn` + /// and `Future` trait bound, like: + /// + /// ```rust + /// use std::future::Future; + /// + /// fn not_exactly_an_async_closure<F, Fut>(_f: F) + /// where + /// F: FnOnce(String, u32) -> Fut, + /// Fut: Future<Output = i32>, + /// {} + /// ``` + /// + /// The we want to be able to extract the signature to guide inference in the async + /// closure. We will have two projection predicates registered in this case. First, + /// we identify the `FnOnce<Args, Output = ?Fut>` bound, and if the output type is + /// an inference variable `?Fut`, we check if that is bounded by a `Future<Output = Ty>` + /// projection. + fn extract_sig_from_projection_and_future_bound( + &self, + cause_span: Option<Span>, + projection: ty::PolyProjectionPredicate<'tcx>, + ) -> Option<ExpectedSig<'tcx>> { + let projection = self.resolve_vars_if_possible(projection); + + let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1); + debug!(?arg_param_ty); + + let ty::Tuple(input_tys) = *arg_param_ty.kind() else { + return None; + }; + + // If the return type is a type variable, look for bounds on it. + // We could theoretically support other kinds of return types here, + // but none of them would be useful, since async closures return + // concrete anonymous future types, and their futures are not coerced + // into any other type within the body of the async closure. + let ty::Infer(ty::TyVar(return_vid)) = *projection.skip_binder().term.expect_type().kind() + else { + return None; + }; + + // FIXME: We may want to elaborate here, though I assume this will be exceedingly rare. + for bound in self.obligations_for_self_ty(return_vid) { + if let Some(ret_projection) = bound.predicate.as_projection_clause() + && let Some(ret_projection) = ret_projection.no_bound_vars() + && self.tcx.is_lang_item(ret_projection.def_id(), LangItem::FutureOutput) + { + let sig = projection.rebind(self.tcx.mk_fn_sig( + input_tys, + ret_projection.term.expect_type(), + false, + hir::Safety::Safe, + Abi::Rust, + )); + + return Some(ExpectedSig { cause_span, sig }); + } + } + + None + } + fn sig_of_closure( &self, expr_def_id: LocalDefId, diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index f72e8a4afde..4f0a089ee95 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1752,10 +1752,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx.probe(|_| { let ocx = ObligationCtxt::new(fcx); ocx.register_obligations( - fcx.tcx - .item_super_predicates(rpit_def_id) - .instantiate_identity_iter() - .filter_map(|clause| { + fcx.tcx.item_super_predicates(rpit_def_id).iter_identity().filter_map( + |clause| { let predicate = clause .kind() .map_bound(|clause| match clause { @@ -1776,7 +1774,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx.param_env, predicate, )) - }), + }, + ), ); ocx.select_where_possible().is_empty() }) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index ad9c1e28211..4f1c2fdd922 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -13,6 +13,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AssocItem, Ty, TypeFoldable, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCause; use super::method::probe; diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index f1ed2ade3d4..8b5b0a9b92f 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -4,8 +4,8 @@ use std::borrow::Cow; use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, Applicability, Diag, DiagArgValue, EmissionGuarantee, IntoDiagArg, MultiSpan, - SubdiagMessageOp, Subdiagnostic, + codes::*, Applicability, Diag, DiagArgValue, DiagSymbolList, EmissionGuarantee, IntoDiagArg, + MultiSpan, SubdiagMessageOp, Subdiagnostic, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; @@ -253,6 +253,13 @@ pub struct LossyProvenanceInt2Ptr<'tcx> { pub sugg: LossyProvenanceInt2PtrSuggestion, } +#[derive(LintDiagnostic)] +#[diag(hir_typeck_ptr_cast_add_auto_to_object)] +pub struct PtrCastAddAutoToObject { + pub traits_len: usize, + pub traits: DiagSymbolList<String>, +} + #[derive(Subdiagnostic)] #[multipart_suggestion(hir_typeck_suggestion, applicability = "has-placeholders")] pub struct LossyProvenanceInt2PtrSuggestion { diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index c8ab0429ffc..193dbbbcdf4 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -734,7 +734,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // struct; however, when EUV is run during typeck, it // may not. This will generate an error earlier in typeck, // so we can just ignore it. - span_bug!(with_expr.span, "with expression doesn't evaluate to a struct"); + if self.cx.tainted_by_errors().is_ok() { + span_bug!(with_expr.span, "with expression doesn't evaluate to a struct"); + } } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 047d0850d4c..10092dce587 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -40,6 +40,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::Session; use rustc_span::symbol::{kw, Ident}; use rustc_span::{sym, BytePos, Span, DUMMY_SP}; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; use std::iter; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 8d380caf916..fca0a3e195d 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2582,7 +2582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } (hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr), _, &ty::Ref(_, checked, _)) - if self.can_sub(self.param_env, checked, expected) => + if self.can_eq(self.param_env, checked, expected) => { let make_sugg = |start: Span, end: BytePos| { // skip `(` for tuples such as `(c) = (&123)`. diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 63b30d79aba..6a7af5510e0 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -33,6 +33,7 @@ use rustc_span::edit_distance::{ }; use rustc_span::symbol::sym; use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP}; +use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy; use rustc_trait_selection::traits::query::method_autoderef::{ @@ -857,7 +858,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { let args = self.fresh_args_for_item(self.span, method.def_id); let fty = self.tcx.fn_sig(method.def_id).instantiate(self.tcx, args); let fty = self.instantiate_binder_with_fresh_vars(self.span, infer::FnCall, fty); - self.can_sub(self.param_env, fty.output(), expected) + self.can_eq(self.param_env, fty.output(), expected) }), _ => false, } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 5e0f37ed792..be526e1c26c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -19,6 +19,7 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{BytePos, Span, DUMMY_SP}; use rustc_target::abi::FieldIdx; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; use ty::VariantDef; diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index 209996b12e2..f35a8162d96 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -151,6 +151,10 @@ impl<'tcx> InferCtxtLike for InferCtxt<'tcx> { .eq_structurally_relating_aliases_no_trace(lhs, rhs) } + fn shallow_resolve(&self, ty: Ty<'tcx>) -> Ty<'tcx> { + self.shallow_resolve(ty) + } + fn resolve_vars_if_possible<T>(&self, value: T) -> T where T: TypeFoldable<TyCtxt<'tcx>>, diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs index fd50d1eb438..d5e7de897d0 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs @@ -820,7 +820,7 @@ fn foo(&self) -> Self::T { String::new() } tcx.defaultness(item.id.owner_id) { let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity(); - if self.infcx.can_eq(param_env, assoc_ty, found) { + if self.infcx.can_eq_shallow(param_env, assoc_ty, found) { diag.span_label( item.span, "associated type defaults can't be assumed inside the \ @@ -843,7 +843,7 @@ fn foo(&self) -> Self::T { String::new() } let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity(); if let hir::Defaultness::Default { has_value: true } = tcx.defaultness(item.id.owner_id) - && self.infcx.can_eq(param_env, assoc_ty, found) + && self.infcx.can_eq_shallow(param_env, assoc_ty, found) { diag.span_label( item.span, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index edbb741036f..9f55939c165 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -768,19 +768,9 @@ impl<'tcx> InferCtxt<'tcx> { .collect() } - pub fn can_sub<T>(&self, param_env: ty::ParamEnv<'tcx>, expected: T, actual: T) -> bool - where - T: at::ToTrace<'tcx>, - { - let origin = &ObligationCause::dummy(); - self.probe(|_| { - // We're only answering whether there could be a subtyping relation, and with - // opaque types, "there could be one", via registering a hidden type. - self.at(origin, param_env).sub(DefineOpaqueTypes::Yes, expected, actual).is_ok() - }) - } - - pub fn can_eq<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool + // FIXME(-Znext-solver): Get rid of this method, it's never correct. Either that, + // or we need to process the obligations. + pub fn can_eq_shallow<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool where T: at::ToTrace<'tcx>, { diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs index b269bfcbfeb..f54d0418595 100644 --- a/compiler/rustc_infer/src/traits/util.rs +++ b/compiler/rustc_infer/src/traits/util.rs @@ -1,12 +1,10 @@ -use smallvec::smallvec; - use crate::traits::{self, Obligation, ObligationCauseCode, PredicateObligation}; use rustc_data_structures::fx::FxHashSet; use rustc_middle::ty::ToPolyTraitRef; -use rustc_middle::ty::{self, Ty, TyCtxt, Upcast}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::Ident; use rustc_span::Span; -use rustc_type_ir::outlives::{push_outlives_components, Component}; +pub use rustc_type_ir::elaborate::*; pub fn anonymize_predicate<'tcx>( tcx: TyCtxt<'tcx>, @@ -64,50 +62,9 @@ impl<'tcx> Extend<ty::Predicate<'tcx>> for PredicateSet<'tcx> { } } -/////////////////////////////////////////////////////////////////////////// -// `Elaboration` iterator -/////////////////////////////////////////////////////////////////////////// - -/// "Elaboration" is the process of identifying all the predicates that -/// are implied by a source predicate. Currently, this basically means -/// walking the "supertraits" and other similar assumptions. For example, -/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd` -/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that -/// `T: Foo`, then we know that `T: 'static`. -pub struct Elaborator<'tcx, O> { - stack: Vec<O>, - visited: PredicateSet<'tcx>, - mode: Filter, -} - -enum Filter { - All, - OnlySelf, -} - -/// Describes how to elaborate an obligation into a sub-obligation. -/// /// For [`Obligation`], a sub-obligation is combined with the current obligation's -/// param-env and cause code. For [`ty::Predicate`], none of this is needed, since -/// there is no param-env or cause code to copy over. -pub trait Elaboratable<'tcx> { - fn predicate(&self) -> ty::Predicate<'tcx>; - - // Makes a new `Self` but with a different clause that comes from elaboration. - fn child(&self, clause: ty::Clause<'tcx>) -> Self; - - // Makes a new `Self` but with a different clause and a different cause - // code (if `Self` has one, such as [`PredicateObligation`]). - fn child_with_derived_cause( - &self, - clause: ty::Clause<'tcx>, - span: Span, - parent_trait_pred: ty::PolyTraitPredicate<'tcx>, - index: usize, - ) -> Self; -} - -impl<'tcx> Elaboratable<'tcx> for PredicateObligation<'tcx> { +/// param-env and cause code. +impl<'tcx> Elaboratable<TyCtxt<'tcx>> for PredicateObligation<'tcx> { fn predicate(&self) -> ty::Predicate<'tcx> { self.predicate } @@ -145,270 +102,6 @@ impl<'tcx> Elaboratable<'tcx> for PredicateObligation<'tcx> { } } -impl<'tcx> Elaboratable<'tcx> for ty::Predicate<'tcx> { - fn predicate(&self) -> ty::Predicate<'tcx> { - *self - } - - fn child(&self, clause: ty::Clause<'tcx>) -> Self { - clause.as_predicate() - } - - fn child_with_derived_cause( - &self, - clause: ty::Clause<'tcx>, - _span: Span, - _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, - _index: usize, - ) -> Self { - clause.as_predicate() - } -} - -impl<'tcx> Elaboratable<'tcx> for (ty::Predicate<'tcx>, Span) { - fn predicate(&self) -> ty::Predicate<'tcx> { - self.0 - } - - fn child(&self, clause: ty::Clause<'tcx>) -> Self { - (clause.as_predicate(), self.1) - } - - fn child_with_derived_cause( - &self, - clause: ty::Clause<'tcx>, - _span: Span, - _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, - _index: usize, - ) -> Self { - (clause.as_predicate(), self.1) - } -} - -impl<'tcx> Elaboratable<'tcx> for (ty::Clause<'tcx>, Span) { - fn predicate(&self) -> ty::Predicate<'tcx> { - self.0.as_predicate() - } - - fn child(&self, clause: ty::Clause<'tcx>) -> Self { - (clause, self.1) - } - - fn child_with_derived_cause( - &self, - clause: ty::Clause<'tcx>, - _span: Span, - _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, - _index: usize, - ) -> Self { - (clause, self.1) - } -} - -impl<'tcx> Elaboratable<'tcx> for ty::Clause<'tcx> { - fn predicate(&self) -> ty::Predicate<'tcx> { - self.as_predicate() - } - - fn child(&self, clause: ty::Clause<'tcx>) -> Self { - clause - } - - fn child_with_derived_cause( - &self, - clause: ty::Clause<'tcx>, - _span: Span, - _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, - _index: usize, - ) -> Self { - clause - } -} - -pub fn elaborate<'tcx, O: Elaboratable<'tcx>>( - tcx: TyCtxt<'tcx>, - obligations: impl IntoIterator<Item = O>, -) -> Elaborator<'tcx, O> { - let mut elaborator = - Elaborator { stack: Vec::new(), visited: PredicateSet::new(tcx), mode: Filter::All }; - elaborator.extend_deduped(obligations); - elaborator -} - -impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> { - fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = O>) { - // Only keep those bounds that we haven't already seen. - // This is necessary to prevent infinite recursion in some - // cases. One common case is when people define - // `trait Sized: Sized { }` rather than `trait Sized { }`. - // let visited = &mut self.visited; - self.stack.extend(obligations.into_iter().filter(|o| self.visited.insert(o.predicate()))); - } - - /// Filter to only the supertraits of trait predicates, i.e. only the predicates - /// that have `Self` as their self type, instead of all implied predicates. - pub fn filter_only_self(mut self) -> Self { - self.mode = Filter::OnlySelf; - self - } - - fn elaborate(&mut self, elaboratable: &O) { - let tcx = self.visited.tcx; - - // We only elaborate clauses. - let Some(clause) = elaboratable.predicate().as_clause() else { - return; - }; - - let bound_clause = clause.kind(); - match bound_clause.skip_binder() { - ty::ClauseKind::Trait(data) => { - // Negative trait bounds do not imply any supertrait bounds - if data.polarity != ty::PredicatePolarity::Positive { - return; - } - // Get predicates implied by the trait, or only super predicates if we only care about self predicates. - let predicates = match self.mode { - Filter::All => tcx.explicit_implied_predicates_of(data.def_id()), - Filter::OnlySelf => tcx.explicit_super_predicates_of(data.def_id()), - }; - - let obligations = - predicates.predicates.iter().enumerate().map(|(index, &(clause, span))| { - elaboratable.child_with_derived_cause( - clause.instantiate_supertrait(tcx, bound_clause.rebind(data.trait_ref)), - span, - bound_clause.rebind(data), - index, - ) - }); - debug!(?data, ?obligations, "super_predicates"); - self.extend_deduped(obligations); - } - ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => { - // We know that `T: 'a` for some type `T`. We can - // often elaborate this. For example, if we know that - // `[U]: 'a`, that implies that `U: 'a`. Similarly, if - // we know `&'a U: 'b`, then we know that `'a: 'b` and - // `U: 'b`. - // - // We can basically ignore bound regions here. So for - // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to - // `'a: 'b`. - - // Ignore `for<'a> T: 'a` -- we might in the future - // consider this as evidence that `T: 'static`, but - // I'm a bit wary of such constructions and so for now - // I want to be conservative. --nmatsakis - if r_min.is_bound() { - return; - } - - let mut components = smallvec![]; - push_outlives_components(tcx, ty_max, &mut components); - self.extend_deduped( - components - .into_iter() - .filter_map(|component| match component { - Component::Region(r) => { - if r.is_bound() { - None - } else { - Some(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate( - r, r_min, - ))) - } - } - - Component::Param(p) => { - let ty = Ty::new_param(tcx, p.index, p.name); - Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min))) - } - - Component::Placeholder(p) => { - let ty = Ty::new_placeholder(tcx, p); - Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min))) - } - - Component::UnresolvedInferenceVariable(_) => None, - - Component::Alias(alias_ty) => { - // We might end up here if we have `Foo<<Bar as Baz>::Assoc>: 'a`. - // With this, we can deduce that `<Bar as Baz>::Assoc: 'a`. - Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( - alias_ty.to_ty(tcx), - r_min, - ))) - } - - Component::EscapingAlias(_) => { - // We might be able to do more here, but we don't - // want to deal with escaping vars right now. - None - } - }) - .map(|clause| elaboratable.child(bound_clause.rebind(clause).upcast(tcx))), - ); - } - ty::ClauseKind::RegionOutlives(..) => { - // Nothing to elaborate from `'a: 'b`. - } - ty::ClauseKind::WellFormed(..) => { - // Currently, we do not elaborate WF predicates, - // although we easily could. - } - ty::ClauseKind::Projection(..) => { - // Nothing to elaborate in a projection predicate. - } - ty::ClauseKind::ConstEvaluatable(..) => { - // Currently, we do not elaborate const-evaluatable - // predicates. - } - ty::ClauseKind::ConstArgHasType(..) => { - // Nothing to elaborate - } - } - } -} - -impl<'tcx, O: Elaboratable<'tcx>> Iterator for Elaborator<'tcx, O> { - type Item = O; - - fn size_hint(&self) -> (usize, Option<usize>) { - (self.stack.len(), None) - } - - fn next(&mut self) -> Option<Self::Item> { - // Extract next item from top-most stack frame, if any. - if let Some(obligation) = self.stack.pop() { - self.elaborate(&obligation); - Some(obligation) - } else { - None - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Supertrait iterator -/////////////////////////////////////////////////////////////////////////// - -pub fn supertraits<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::PolyTraitRef<'tcx>, -) -> FilterToTraits<Elaborator<'tcx, ty::Clause<'tcx>>> { - elaborate(tcx, [trait_ref.upcast(tcx)]).filter_only_self().filter_to_traits() -} - -pub fn transitive_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>, -) -> FilterToTraits<Elaborator<'tcx, ty::Clause<'tcx>>> { - elaborate(tcx, trait_refs.map(|trait_ref| trait_ref.upcast(tcx))) - .filter_only_self() - .filter_to_traits() -} - /// A specialized variant of `elaborate` that only elaborates trait references that may /// define the given associated item with the name `assoc_name`. It uses the /// `explicit_supertraits_containing_assoc_item` query to avoid enumerating super-predicates that @@ -443,37 +136,3 @@ pub fn transitive_bounds_that_define_assoc_item<'tcx>( None }) } - -/////////////////////////////////////////////////////////////////////////// -// Other -/////////////////////////////////////////////////////////////////////////// - -impl<'tcx> Elaborator<'tcx, ty::Clause<'tcx>> { - fn filter_to_traits(self) -> FilterToTraits<Self> { - FilterToTraits { base_iterator: self } - } -} - -/// A filter around an iterator of predicates that makes it yield up -/// just trait references. -pub struct FilterToTraits<I> { - base_iterator: I, -} - -impl<'tcx, I: Iterator<Item = ty::Clause<'tcx>>> Iterator for FilterToTraits<I> { - type Item = ty::PolyTraitRef<'tcx>; - - fn next(&mut self) -> Option<ty::PolyTraitRef<'tcx>> { - while let Some(pred) = self.base_iterator.next() { - if let Some(data) = pred.as_trait_clause() { - return Some(data.map_bound(|t| t.trait_ref)); - } - } - None - } - - fn size_hint(&self) -> (usize, Option<usize>) { - let (_, upper) = self.base_iterator.size_hint(); - (0, upper) - } -} diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 8b669bcc13f..5ee73dbfdc6 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -76,9 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // For every projection predicate in the opaque type's explicit bounds, // check that the type that we're assigning actually satisfies the bounds // of the associated type. - for (pred, pred_span) in - cx.tcx.explicit_item_bounds(def_id).instantiate_identity_iter_copied() - { + for (pred, pred_span) in cx.tcx.explicit_item_bounds(def_id).iter_identity_copied() { infcx.enter_forall(pred.kind(), |predicate| { let ty::ClauseKind::Projection(proj) = predicate else { return; diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 195a0f72475..65d42ed8054 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -298,9 +298,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { elaborate( cx.tcx, - cx.tcx - .explicit_item_super_predicates(def) - .instantiate_identity_iter_copied(), + cx.tcx.explicit_item_super_predicates(def).iter_identity_copied(), ) // We only care about self bounds for the impl-trait .filter_only_self() diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 2ade6964ca8..bc93248252d 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -80,6 +80,7 @@ declare_lint_pass! { PRIVATE_BOUNDS, PRIVATE_INTERFACES, PROC_MACRO_DERIVE_RESOLUTION_FALLBACK, + PTR_CAST_ADD_AUTO_TO_OBJECT, PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_LIFETIMES, REFINING_IMPL_TRAIT_INTERNAL, @@ -4189,6 +4190,7 @@ declare_lint! { reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange, reference: "issue #123748 <https://github.com/rust-lang/rust/issues/123748>", }; + @edition Edition2024 => Deny; report_in_external_macro } @@ -4938,6 +4940,58 @@ declare_lint! { } declare_lint! { + /// The `ptr_cast_add_auto_to_object` lint detects casts of raw pointers to trait + /// objects, which add auto traits. + /// + /// ### Example + /// + /// ```rust,edition2021,compile_fail + /// let ptr: *const dyn core::any::Any = &(); + /// _ = ptr as *const dyn core::any::Any + Send; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Adding an auto trait can make the vtable invalid, potentially causing + /// UB in safe code afterwards. For example: + /// + /// ```ignore (causes a warning) + /// #![feature(arbitrary_self_types)] + /// + /// trait Trait { + /// fn f(self: *const Self) + /// where + /// Self: Send; + /// } + /// + /// impl Trait for *const () { + /// fn f(self: *const Self) { + /// unreachable!() + /// } + /// } + /// + /// fn main() { + /// let unsend: *const () = &(); + /// let unsend: *const dyn Trait = &unsend; + /// let send_bad: *const (dyn Trait + Send) = unsend as _; + /// send_bad.f(); // this crashes, since vtable for `*const ()` does not have an entry for `f` + /// } + /// ``` + /// + /// Generally you must ensure that vtable is right for the pointer's type, + /// before passing the pointer to safe code. + pub PTR_CAST_ADD_AUTO_TO_OBJECT, + Warn, + "detects `as` casts from pointers to `dyn Trait` to pointers to `dyn Trait + Auto`", + @future_incompatible = FutureIncompatibleInfo { + reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps, + reference: "issue #127323 <https://github.com/rust-lang/rust/issues/127323>", + }; +} + +declare_lint! { /// The `out_of_scope_macro_calls` lint detects `macro_rules` called when they are not in scope, /// above their definition, which may happen in key-value attributes. /// diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 305ba1ef3bb..2f3a6ee601b 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -31,9 +31,18 @@ pub struct Map<'hir> { /// An iterator that walks up the ancestor tree of a given `HirId`. /// Constructed using `tcx.hir().parent_iter(hir_id)`. -pub struct ParentHirIterator<'hir> { +struct ParentHirIterator<'hir> { current_id: HirId, map: Map<'hir>, + // Cache the current value of `hir_owner_nodes` to avoid repeatedly calling the same query for + // the same owner, which will uselessly record many times the same query dependency. + current_owner_nodes: Option<&'hir OwnerNodes<'hir>>, +} + +impl<'hir> ParentHirIterator<'hir> { + fn new(map: Map<'hir>, current_id: HirId) -> ParentHirIterator<'hir> { + ParentHirIterator { current_id, map, current_owner_nodes: None } + } } impl<'hir> Iterator for ParentHirIterator<'hir> { @@ -44,13 +53,22 @@ impl<'hir> Iterator for ParentHirIterator<'hir> { return None; } - // There are nodes that do not have entries, so we need to skip them. - let parent_id = self.map.tcx.parent_hir_id(self.current_id); + let HirId { owner, local_id } = self.current_id; - if parent_id == self.current_id { - self.current_id = CRATE_HIR_ID; - return None; - } + let parent_id = if local_id == ItemLocalId::ZERO { + // We go from an owner to its parent, so clear the cache. + self.current_owner_nodes = None; + self.map.tcx.hir_owner_parent(owner) + } else { + let owner_nodes = + self.current_owner_nodes.get_or_insert_with(|| self.map.tcx.hir_owner_nodes(owner)); + let parent_local_id = owner_nodes.nodes[local_id].parent; + // HIR indexing should have checked that. + debug_assert_ne!(parent_local_id, local_id); + HirId { owner, local_id: parent_local_id } + }; + + debug_assert_ne!(parent_id, self.current_id); self.current_id = parent_id; return Some(parent_id); @@ -479,7 +497,7 @@ impl<'hir> Map<'hir> { /// until the crate root is reached. Prefer this over your own loop using `parent_id`. #[inline] pub fn parent_id_iter(self, current_id: HirId) -> impl Iterator<Item = HirId> + 'hir { - ParentHirIterator { current_id, map: self } + ParentHirIterator::new(self, current_id) } /// Returns an iterator for the nodes in the ancestor tree of the `current_id` diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index af19ed95193..82625ae3d47 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -854,6 +854,16 @@ impl<'tcx> TerminatorKind<'tcx> { } write!(fmt, ")") } + TailCall { func, args, .. } => { + write!(fmt, "tailcall {func:?}(")?; + for (index, arg) in args.iter().enumerate() { + if index > 0 { + write!(fmt, ", ")?; + } + write!(fmt, "{:?}", arg)?; + } + write!(fmt, ")") + } Assert { cond, expected, msg, .. } => { write!(fmt, "assert(")?; if !expected { @@ -921,7 +931,12 @@ impl<'tcx> TerminatorKind<'tcx> { pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> { use self::TerminatorKind::*; match *self { - Return | UnwindResume | UnwindTerminate(_) | Unreachable | CoroutineDrop => vec![], + Return + | TailCall { .. } + | UnwindResume + | UnwindTerminate(_) + | Unreachable + | CoroutineDrop => vec![], Goto { .. } => vec!["".into()], SwitchInt { ref targets, .. } => targets .values diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 2c2884f1897..620fa962d79 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -744,6 +744,36 @@ pub enum TerminatorKind<'tcx> { fn_span: Span, }, + /// Tail call. + /// + /// Roughly speaking this is a chimera of [`Call`] and [`Return`], with some caveats. + /// Semantically tail calls consists of two actions: + /// - pop of the current stack frame + /// - a call to the `func`, with the return address of the **current** caller + /// - so that a `return` inside `func` returns to the caller of the caller + /// of the function that is currently being executed + /// + /// Note that in difference with [`Call`] this is missing + /// - `destination` (because it's always the return place) + /// - `target` (because it's always taken from the current stack frame) + /// - `unwind` (because it's always taken from the current stack frame) + /// + /// [`Call`]: TerminatorKind::Call + /// [`Return`]: TerminatorKind::Return + TailCall { + /// The function that’s being called. + func: Operand<'tcx>, + /// Arguments the function is called with. + /// These are owned by the callee, which is free to modify them. + /// This allows the memory occupied by "by-value" arguments to be + /// reused across function calls without duplicating the contents. + args: Box<[Spanned<Operand<'tcx>>]>, + // FIXME(explicit_tail_calls): should we have the span for `become`? is this span accurate? do we need it? + /// This `Span` is the span of the function, without the dot and receiver + /// (e.g. `foo(a, b)` in `x.foo(a, b)` + fn_span: Span, + }, + /// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`, /// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some /// unspecified constant as the function to call, all the operands stored in the `AssertMessage` @@ -870,6 +900,7 @@ impl TerminatorKind<'_> { TerminatorKind::Unreachable => "Unreachable", TerminatorKind::Drop { .. } => "Drop", TerminatorKind::Call { .. } => "Call", + TerminatorKind::TailCall { .. } => "TailCall", TerminatorKind::Assert { .. } => "Assert", TerminatorKind::Yield { .. } => "Yield", TerminatorKind::CoroutineDrop => "CoroutineDrop", diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index ed592612358..5b035d9579d 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -439,6 +439,7 @@ mod helper { | CoroutineDrop | Return | Unreachable + | TailCall { .. } | Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None), InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => { targets.iter().copied().chain(Some(u)) @@ -479,6 +480,7 @@ mod helper { | CoroutineDrop | Return | Unreachable + | TailCall { .. } | Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None), InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => { targets.iter_mut().chain(Some(u)) @@ -501,6 +503,7 @@ impl<'tcx> TerminatorKind<'tcx> { | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::CoroutineDrop | TerminatorKind::Yield { .. } @@ -521,6 +524,7 @@ impl<'tcx> TerminatorKind<'tcx> { | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::CoroutineDrop | TerminatorKind::Yield { .. } @@ -606,9 +610,12 @@ impl<'tcx> TerminatorKind<'tcx> { pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> { use TerminatorKind::*; match *self { - Return | UnwindResume | UnwindTerminate(_) | CoroutineDrop | Unreachable => { - TerminatorEdges::None - } + Return + | TailCall { .. } + | UnwindResume + | UnwindTerminate(_) + | CoroutineDrop + | Unreachable => TerminatorEdges::None, Goto { target } => TerminatorEdges::Single(target), diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 7628a1ed2fe..0d3c419748b 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -540,6 +540,17 @@ macro_rules! make_mir_visitor { ); } + TerminatorKind::TailCall { + func, + args, + fn_span: _, + } => { + self.visit_operand(func, location); + for arg in args { + self.visit_operand(&$($mutability)? arg.node, location); + } + }, + TerminatorKind::Assert { cond, expected: _, diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index b4e3fae1b43..b74775142e4 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -7,7 +7,6 @@ pub mod select; pub mod solve; pub mod specialization_graph; mod structural_impls; -pub mod util; use crate::mir::ConstraintCategory; use crate::ty::abstract_const::NotConstEvaluatable; diff --git a/compiler/rustc_middle/src/traits/util.rs b/compiler/rustc_middle/src/traits/util.rs deleted file mode 100644 index 7437be7a74c..00000000000 --- a/compiler/rustc_middle/src/traits/util.rs +++ /dev/null @@ -1,62 +0,0 @@ -use rustc_data_structures::fx::FxHashSet; - -use crate::ty::{Clause, PolyTraitRef, ToPolyTraitRef, TyCtxt, Upcast}; - -/// Given a [`PolyTraitRef`], get the [`Clause`]s implied by the trait's definition. -/// -/// This only exists in `rustc_middle` because the more powerful elaborator depends on -/// `rustc_infer` for elaborating outlives bounds -- this should only be used for pretty -/// printing. -pub fn super_predicates_for_pretty_printing<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: PolyTraitRef<'tcx>, -) -> impl Iterator<Item = Clause<'tcx>> { - let clause = trait_ref.upcast(tcx); - Elaborator { tcx, visited: FxHashSet::from_iter([clause]), stack: vec![clause] } -} - -/// Like [`super_predicates_for_pretty_printing`], except it only returns traits and filters out -/// all other [`Clause`]s. -pub fn supertraits_for_pretty_printing<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: PolyTraitRef<'tcx>, -) -> impl Iterator<Item = PolyTraitRef<'tcx>> { - super_predicates_for_pretty_printing(tcx, trait_ref).filter_map(|clause| { - clause.as_trait_clause().map(|trait_clause| trait_clause.to_poly_trait_ref()) - }) -} - -struct Elaborator<'tcx> { - tcx: TyCtxt<'tcx>, - visited: FxHashSet<Clause<'tcx>>, - stack: Vec<Clause<'tcx>>, -} - -impl<'tcx> Elaborator<'tcx> { - fn elaborate(&mut self, trait_ref: PolyTraitRef<'tcx>) { - let super_predicates = - self.tcx.explicit_super_predicates_of(trait_ref.def_id()).predicates.iter().filter_map( - |&(pred, _)| { - let clause = pred.instantiate_supertrait(self.tcx, trait_ref); - self.visited.insert(clause).then_some(clause) - }, - ); - - self.stack.extend(super_predicates); - } -} - -impl<'tcx> Iterator for Elaborator<'tcx> { - type Item = Clause<'tcx>; - - fn next(&mut self) -> Option<Clause<'tcx>> { - if let Some(clause) = self.stack.pop() { - if let Some(trait_clause) = clause.as_trait_clause() { - self.elaborate(trait_clause.to_poly_trait_ref()); - } - Some(clause) - } else { - None - } - } -} diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 8e221cdc603..88ee32eae95 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -229,6 +229,10 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> { fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> { self.sized_constraint(tcx) } + + fn is_fundamental(self) -> bool { + self.is_fundamental() + } } #[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable, TyEncodable, TyDecodable)] diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index dca48069974..aee42bfe3aa 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -37,7 +37,7 @@ use crate::ty::{GenericArg, GenericArgs, GenericArgsRef}; use rustc_ast::{self as ast, attr}; use rustc_data_structures::defer; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::intern::Interned; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap}; @@ -347,12 +347,16 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn explicit_super_predicates_of( self, def_id: DefId, - ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Clause<'tcx>>> { + ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>> { + ty::EarlyBinder::bind(self.explicit_super_predicates_of(def_id).instantiate_identity(self)) + } + + fn explicit_implied_predicates_of( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>> { ty::EarlyBinder::bind( - self.explicit_super_predicates_of(def_id) - .instantiate_identity(self) - .predicates - .into_iter(), + self.explicit_implied_predicates_of(def_id).instantiate_identity(self), ) } @@ -524,12 +528,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.is_object_safe(trait_def_id) } - fn trait_may_be_implemented_via_object(self, trait_def_id: DefId) -> bool { - self.trait_def(trait_def_id).implement_via_object + fn trait_is_fundamental(self, def_id: DefId) -> bool { + self.trait_def(def_id).is_fundamental } - fn supertrait_def_ids(self, trait_def_id: DefId) -> impl IntoIterator<Item = DefId> { - self.supertrait_def_ids(trait_def_id) + fn trait_may_be_implemented_via_object(self, trait_def_id: DefId) -> bool { + self.trait_def(trait_def_id).implement_via_object } fn delay_bug(self, msg: impl ToString) -> ErrorGuaranteed { @@ -569,6 +573,13 @@ impl<'tcx> Interner for TyCtxt<'tcx> { ) -> Ty<'tcx> { placeholder.find_const_ty_from_env(param_env) } + + fn anonymize_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>( + self, + binder: ty::Binder<'tcx, T>, + ) -> ty::Binder<'tcx, T> { + self.anonymize_bound_vars(binder) + } } macro_rules! bidirectional_lang_item_map { @@ -635,6 +646,10 @@ bidirectional_lang_item_map! { } impl<'tcx> rustc_type_ir::inherent::DefId<TyCtxt<'tcx>> for DefId { + fn is_local(self) -> bool { + self.is_local() + } + fn as_local(self) -> Option<LocalDefId> { self.as_local() } @@ -2484,25 +2499,7 @@ impl<'tcx> TyCtxt<'tcx> { /// to identify which traits may define a given associated type to help avoid cycle errors, /// and to make size estimates for vtable layout computation. pub fn supertrait_def_ids(self, trait_def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx { - let mut set = FxHashSet::default(); - let mut stack = vec![trait_def_id]; - - set.insert(trait_def_id); - - iter::from_fn(move || -> Option<DefId> { - let trait_did = stack.pop()?; - let generic_predicates = self.explicit_super_predicates_of(trait_did); - - for (predicate, _) in generic_predicates.predicates { - if let ty::ClauseKind::Trait(data) = predicate.kind().skip_binder() { - if set.insert(data.def_id()) { - stack.push(data.def_id()); - } - } - } - - Some(trait_did) - }) + rustc_type_ir::elaborate::supertrait_def_ids(self, trait_def_id) } /// Given a closure signature, returns an equivalent fn signature. Detuples diff --git a/compiler/rustc_middle/src/ty/elaborate_impl.rs b/compiler/rustc_middle/src/ty/elaborate_impl.rs new file mode 100644 index 00000000000..8c89a2d884b --- /dev/null +++ b/compiler/rustc_middle/src/ty/elaborate_impl.rs @@ -0,0 +1,84 @@ +use rustc_span::Span; +use rustc_type_ir::elaborate::Elaboratable; + +use crate::ty::{self, TyCtxt}; + +impl<'tcx> Elaboratable<TyCtxt<'tcx>> for ty::Clause<'tcx> { + fn predicate(&self) -> ty::Predicate<'tcx> { + self.as_predicate() + } + + fn child(&self, clause: ty::Clause<'tcx>) -> Self { + clause + } + + fn child_with_derived_cause( + &self, + clause: ty::Clause<'tcx>, + _span: Span, + _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, + _index: usize, + ) -> Self { + clause + } +} + +impl<'tcx> Elaboratable<TyCtxt<'tcx>> for ty::Predicate<'tcx> { + fn predicate(&self) -> ty::Predicate<'tcx> { + *self + } + + fn child(&self, clause: ty::Clause<'tcx>) -> Self { + clause.as_predicate() + } + + fn child_with_derived_cause( + &self, + clause: ty::Clause<'tcx>, + _span: Span, + _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, + _index: usize, + ) -> Self { + clause.as_predicate() + } +} + +impl<'tcx> Elaboratable<TyCtxt<'tcx>> for (ty::Predicate<'tcx>, Span) { + fn predicate(&self) -> ty::Predicate<'tcx> { + self.0 + } + + fn child(&self, clause: ty::Clause<'tcx>) -> Self { + (clause.as_predicate(), self.1) + } + + fn child_with_derived_cause( + &self, + clause: ty::Clause<'tcx>, + _span: Span, + _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, + _index: usize, + ) -> Self { + (clause.as_predicate(), self.1) + } +} + +impl<'tcx> Elaboratable<TyCtxt<'tcx>> for (ty::Clause<'tcx>, Span) { + fn predicate(&self) -> ty::Predicate<'tcx> { + self.0.as_predicate() + } + + fn child(&self, clause: ty::Clause<'tcx>) -> Self { + (clause, self.1) + } + + fn child_with_derived_cause( + &self, + clause: ty::Clause<'tcx>, + _span: Span, + _parent_trait_pred: ty::PolyTraitPredicate<'tcx>, + _index: usize, + ) -> Self { + (clause, self.1) + } +} diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs index 844023df1e3..11ed0bdaa70 100644 --- a/compiler/rustc_middle/src/ty/generics.rs +++ b/compiler/rustc_middle/src/ty/generics.rs @@ -394,7 +394,7 @@ impl<'tcx> GenericPredicates<'tcx> { } pub fn instantiate_own_identity(&self) -> impl Iterator<Item = (Clause<'tcx>, Span)> { - EarlyBinder::bind(self.predicates).instantiate_identity_iter_copied() + EarlyBinder::bind(self.predicates).iter_identity_copied() } #[instrument(level = "debug", skip(self, tcx))] diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 7d57d88f40f..4470db47474 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -148,6 +148,7 @@ mod closure; mod consts; mod context; mod diagnostics; +mod elaborate_impl; mod erase_regions; mod generic_args; mod generics; diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs index e9b37503bb3..5d6352c57ce 100644 --- a/compiler/rustc_middle/src/ty/predicate.rs +++ b/compiler/rustc_middle/src/ty/predicate.rs @@ -46,6 +46,10 @@ pub struct Predicate<'tcx>( ); impl<'tcx> rustc_type_ir::inherent::Predicate<TyCtxt<'tcx>> for Predicate<'tcx> { + fn as_clause(self) -> Option<ty::Clause<'tcx>> { + self.as_clause() + } + fn is_coinductive(self, interner: TyCtxt<'tcx>) -> bool { self.is_coinductive(interner) } @@ -173,7 +177,11 @@ pub struct Clause<'tcx>( pub(super) Interned<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>, ); -impl<'tcx> rustc_type_ir::inherent::Clause<TyCtxt<'tcx>> for Clause<'tcx> {} +impl<'tcx> rustc_type_ir::inherent::Clause<TyCtxt<'tcx>> for Clause<'tcx> { + fn instantiate_supertrait(self, tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> Self { + self.instantiate_supertrait(tcx, trait_ref) + } +} impl<'tcx> rustc_type_ir::inherent::IntoKind for Clause<'tcx> { type Kind = ty::Binder<'tcx, ClauseKind<'tcx>>; @@ -341,6 +349,14 @@ impl<'tcx> ty::List<ty::PolyExistentialPredicate<'tcx>> { _ => None, }) } + + pub fn without_auto_traits( + &self, + ) -> impl Iterator<Item = ty::PolyExistentialPredicate<'tcx>> + '_ { + self.iter().filter(|predicate| { + !matches!(predicate.as_ref().skip_binder(), ExistentialPredicate::AutoTrait(_)) + }) + } } pub type PolyTraitRef<'tcx> = ty::Binder<'tcx, TraitRef<'tcx>>; diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 7e64c507406..df080b2887b 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1,7 +1,6 @@ use crate::mir::interpret::{AllocRange, GlobalAlloc, Pointer, Provenance, Scalar}; use crate::query::IntoQueryParam; use crate::query::Providers; -use crate::traits::util::{super_predicates_for_pretty_printing, supertraits_for_pretty_printing}; use crate::ty::GenericArgKind; use crate::ty::{ ConstInt, Expr, ParamConst, ScalarInt, Term, TermKind, TypeFoldable, TypeSuperFoldable, @@ -23,6 +22,7 @@ use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::FileNameDisplayPreference; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; +use rustc_type_ir::{elaborate, Upcast as _}; use smallvec::SmallVec; use std::cell::Cell; @@ -1255,14 +1255,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { entry.has_fn_once = true; return; } else if self.tcx().is_lang_item(trait_def_id, LangItem::FnMut) { - let super_trait_ref = supertraits_for_pretty_printing(self.tcx(), trait_ref) + let super_trait_ref = elaborate::supertraits(self.tcx(), trait_ref) .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait) .unwrap(); fn_traits.entry(super_trait_ref).or_default().fn_mut_trait_ref = Some(trait_ref); return; } else if self.tcx().is_lang_item(trait_def_id, LangItem::Fn) { - let super_trait_ref = supertraits_for_pretty_printing(self.tcx(), trait_ref) + let super_trait_ref = elaborate::supertraits(self.tcx(), trait_ref) .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait) .unwrap(); @@ -1343,10 +1343,11 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { let bound_principal_with_self = bound_principal .with_self_ty(cx.tcx(), cx.tcx().types.trait_object_dummy_self); - let super_projections: Vec<_> = - super_predicates_for_pretty_printing(cx.tcx(), bound_principal_with_self) - .filter_map(|clause| clause.as_projection_clause()) - .collect(); + let clause: ty::Clause<'tcx> = bound_principal_with_self.upcast(cx.tcx()); + let super_projections: Vec<_> = elaborate::elaborate(cx.tcx(), [clause]) + .filter_only_self() + .filter_map(|clause| clause.as_projection_clause()) + .collect(); let mut projections: Vec<_> = predicates .projection_bounds() diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 52690ae678d..d2b444a066b 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -811,6 +811,14 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> { Ty::new_var(tcx, vid) } + fn new_param(tcx: TyCtxt<'tcx>, param: ty::ParamTy) -> Self { + Ty::new_param(tcx, param.index, param.name) + } + + fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderType) -> Self { + Ty::new_placeholder(tcx, placeholder) + } + fn new_bound(interner: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundTy) -> Self { Ty::new_bound(interner, debruijn, var) } diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 4dba97c3b5b..076a74ca6f8 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -31,7 +31,7 @@ pub struct TraitDef { /// and thus `impl`s of it are allowed to overlap. pub is_marker: bool, - /// If `true`, then this trait has to `#[rustc_coinductive]` attribute or + /// If `true`, then this trait has the `#[rustc_coinductive]` attribute or /// is an auto trait. This indicates that trait solver cycles involving an /// `X: ThisTrait` goal are accepted. /// @@ -40,6 +40,11 @@ pub struct TraitDef { /// also have already switched to the new trait solver. pub is_coinductive: bool, + /// If `true`, then this trait has the `#[fundamental]` attribute. This + /// affects how conherence computes whether a trait may have trait implementations + /// added in the future. + pub is_fundamental: bool, + /// If `true`, then this trait has the `#[rustc_skip_during_method_dispatch(array)]` /// attribute, indicating that editions before 2021 should not consider this trait /// during method dispatch if the receiver is an array. diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs index 2bdeb579a02..88b76c46c90 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -2,7 +2,9 @@ use crate::build::scope::BreakableTarget; use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; use rustc_middle::middle::region; use rustc_middle::mir::*; +use rustc_middle::span_bug; use rustc_middle::thir::*; +use rustc_span::source_map::Spanned; use tracing::debug; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -91,9 +93,42 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Return { value } => { this.break_scope(block, value, BreakableTarget::Return, source_info) } - // FIXME(explicit_tail_calls): properly lower tail calls here ExprKind::Become { value } => { - this.break_scope(block, Some(value), BreakableTarget::Return, source_info) + let v = &this.thir[value]; + let ExprKind::Scope { value, lint_level, region_scope } = v.kind else { + span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}") + }; + + let v = &this.thir[value]; + let ExprKind::Call { ref args, fun, fn_span, .. } = v.kind else { + span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}") + }; + + this.in_scope((region_scope, source_info), lint_level, |this| { + let fun = unpack!(block = this.as_local_operand(block, fun)); + let args: Box<[_]> = args + .into_iter() + .copied() + .map(|arg| Spanned { + node: unpack!(block = this.as_local_call_operand(block, arg)), + span: this.thir.exprs[arg].span, + }) + .collect(); + + this.record_operands_moved(&args); + + debug!("expr_into_dest: fn_span={:?}", fn_span); + + unpack!(block = this.break_for_tail_call(block, &args, source_info)); + + this.cfg.terminate( + block, + source_info, + TerminatorKind::TailCall { func: fun, args, fn_span }, + ); + + this.cfg.start_new_block().unit() + }) } _ => { assert!( diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 5b6de39bb2e..948301e2ece 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -745,6 +745,91 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate(block, source_info, TerminatorKind::UnwindResume); } + /// Sets up the drops for explict tail calls. + /// + /// Unlike other kinds of early exits, tail calls do not go through the drop tree. + /// Instead, all scheduled drops are immediately added to the CFG. + pub(crate) fn break_for_tail_call( + &mut self, + mut block: BasicBlock, + args: &[Spanned<Operand<'tcx>>], + source_info: SourceInfo, + ) -> BlockAnd<()> { + let arg_drops: Vec<_> = args + .iter() + .rev() + .filter_map(|arg| match &arg.node { + Operand::Copy(_) => bug!("copy op in tail call args"), + Operand::Move(place) => { + let local = + place.as_local().unwrap_or_else(|| bug!("projection in tail call args")); + + Some(DropData { source_info, local, kind: DropKind::Value }) + } + Operand::Constant(_) => None, + }) + .collect(); + + let mut unwind_to = self.diverge_cleanup_target( + self.scopes.scopes.iter().rev().nth(1).unwrap().region_scope, + DUMMY_SP, + ); + let unwind_drops = &mut self.scopes.unwind_drops; + + // the innermost scope contains only the destructors for the tail call arguments + // we only want to drop these in case of a panic, so we skip it + for scope in self.scopes.scopes[1..].iter().rev().skip(1) { + // FIXME(explicit_tail_calls) code duplication with `build_scope_drops` + for drop_data in scope.drops.iter().rev() { + let source_info = drop_data.source_info; + let local = drop_data.local; + + match drop_data.kind { + DropKind::Value => { + // `unwind_to` should drop the value that we're about to + // schedule. If dropping this value panics, then we continue + // with the *next* value on the unwind path. + debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local); + debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); + unwind_to = unwind_drops.drops[unwind_to].next; + + let mut unwind_entry_point = unwind_to; + + // the tail call arguments must be dropped if any of these drops panic + for drop in arg_drops.iter().copied() { + unwind_entry_point = unwind_drops.add_drop(drop, unwind_entry_point); + } + + unwind_drops.add_entry_point(block, unwind_entry_point); + + let next = self.cfg.start_new_block(); + self.cfg.terminate( + block, + source_info, + TerminatorKind::Drop { + place: local.into(), + target: next, + unwind: UnwindAction::Continue, + replace: false, + }, + ); + block = next; + } + DropKind::Storage => { + // Only temps and vars need their storage dead. + assert!(local.index() > self.arg_count); + self.cfg.push( + block, + Statement { source_info, kind: StatementKind::StorageDead(local) }, + ); + } + } + } + } + + block.unit() + } + fn leave_top_scope(&mut self, block: BasicBlock) -> BasicBlock { // If we are emitting a `drop` statement, we need to have the cached // diverge cleanup pads ready in case that drop panics. @@ -1523,6 +1608,7 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind { | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::Yield { .. } | TerminatorKind::CoroutineDrop diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs index 1c7aa9f9ed0..263e777d03a 100644 --- a/compiler/rustc_mir_build/src/lints.rs +++ b/compiler/rustc_mir_build/src/lints.rs @@ -217,12 +217,28 @@ impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor<BasicBlocks<'tcx | TerminatorKind::FalseUnwind { .. } | TerminatorKind::Goto { .. } | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue(()), + + // Note that tail call terminator technically returns to the caller, + // but for purposes of this lint it makes sense to count it as possibly recursive, + // since it's still a call. + // + // If this'll be repurposed for something else, this might need to be changed. + TerminatorKind::TailCall { .. } => ControlFlow::Continue(()), } } fn node_settled(&mut self, bb: BasicBlock) -> ControlFlow<Self::BreakVal> { // When we examine a node for the last time, remember it if it is a recursive call. let terminator = self.body[bb].terminator(); + + // FIXME(explicit_tail_calls): highlight tail calls as "recursive call site" + // + // We don't want to lint functions that recurse only through tail calls + // (such as `fn g() { become () }`), so just adding `| TailCall { ... }` + // here won't work. + // + // But at the same time we would like to highlight both calls in a function like + // `fn f() { if false { become f() } else { f() } }`, so we need to figure something out. if self.classifier.is_recursive_terminator(self.tcx, self.body, terminator) { self.reachable_recursive_calls.push(terminator.source_info.span); } diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index 706bb796349..574da949b0e 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -145,6 +145,7 @@ where | TerminatorKind::InlineAsm { .. } | TerminatorKind::UnwindResume | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable | TerminatorKind::Yield { .. } => {} diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 29169c31263..f850a710277 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -288,6 +288,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { | TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable => {} } @@ -325,6 +326,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { | TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::SwitchInt { .. } | TerminatorKind::Unreachable => {} } diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 1fb77bef3d4..7b39db821d8 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -489,6 +489,12 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly); } } + TerminatorKind::TailCall { ref func, ref args, .. } => { + self.gather_operand(func); + for arg in args { + self.gather_operand(&arg.node); + } + } TerminatorKind::InlineAsm { template: _, ref operands, diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 7c1ff6fda53..1582c2e8a90 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -269,6 +269,9 @@ pub trait ValueAnalysis<'tcx> { TerminatorKind::SwitchInt { discr, targets } => { return self.handle_switch_int(discr, targets, state); } + TerminatorKind::TailCall { .. } => { + // FIXME(explicit_tail_calls): determine if we need to do something here (probably not) + } TerminatorKind::Goto { .. } | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 9d44001f915..fa5da89e8ba 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -1367,6 +1367,10 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { | TerminatorKind::Call { .. } | TerminatorKind::InlineAsm { .. } | TerminatorKind::Assert { .. } => return true, + + TerminatorKind::TailCall { .. } => { + unreachable!("tail calls can't be present in generators") + } } } @@ -1916,6 +1920,7 @@ impl<'tcx> Visitor<'tcx> for EnsureCoroutineFieldAssignmentsNeverAlias<'_> { | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } | TerminatorKind::Assert { .. } diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs index 3333bebff3a..a1c1422912e 100644 --- a/compiler/rustc_mir_transform/src/cost_checker.rs +++ b/compiler/rustc_mir_transform/src/cost_checker.rs @@ -40,7 +40,9 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> { fn is_call_like(bbd: &BasicBlockData<'_>) -> bool { use TerminatorKind::*; match bbd.terminator().kind { - Call { .. } | Drop { .. } | Assert { .. } | InlineAsm { .. } => true, + Call { .. } | TailCall { .. } | Drop { .. } | Assert { .. } | InlineAsm { .. } => { + true + } Goto { .. } | SwitchInt { .. } @@ -137,6 +139,9 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> { self.penalty += LANDINGPAD_PENALTY; } } + TerminatorKind::TailCall { .. } => { + self.penalty += CALL_PENALTY; + } TerminatorKind::SwitchInt { discr, targets } => { if discr.constant().is_some() { // Not only will this become a `Goto`, but likely other diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs index 360dccb240d..83fb9ff9743 100644 --- a/compiler/rustc_mir_transform/src/coverage/graph.rs +++ b/compiler/rustc_mir_transform/src/coverage/graph.rs @@ -358,9 +358,12 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera } // These terminators have no coverage-relevant successors. - CoroutineDrop | Return | Unreachable | UnwindResume | UnwindTerminate(_) => { - CoverageSuccessors::NotChainable(&[]) - } + CoroutineDrop + | Return + | TailCall { .. } + | Unreachable + | UnwindResume + | UnwindTerminate(_) => CoverageSuccessors::NotChainable(&[]), } } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index d55bde311c1..2efca40d180 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -8,6 +8,10 @@ mod spans; mod tests; mod unexpand; +use rustc_hir as hir; +use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; use rustc_middle::mir::coverage::{ CodeRegion, CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind, }; @@ -465,6 +469,9 @@ struct ExtractedHirInfo { /// Must have the same context and filename as the body span. fn_sig_span_extended: Option<Span>, body_span: Span, + /// "Holes" are regions within the body span that should not be included in + /// coverage spans for this function (e.g. closures and nested items). + hole_spans: Vec<Span>, } fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHirInfo { @@ -480,7 +487,7 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir let mut body_span = hir_body.value.span; - use rustc_hir::{Closure, Expr, ExprKind, Node}; + use hir::{Closure, Expr, ExprKind, Node}; // Unexpand a closure's body span back to the context of its declaration. // This helps with closure bodies that consist of just a single bang-macro, // and also with closure bodies produced by async desugaring. @@ -507,11 +514,78 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir let function_source_hash = hash_mir_source(tcx, hir_body); - ExtractedHirInfo { function_source_hash, is_async_fn, fn_sig_span_extended, body_span } + let hole_spans = extract_hole_spans_from_hir(tcx, body_span, hir_body); + + ExtractedHirInfo { + function_source_hash, + is_async_fn, + fn_sig_span_extended, + body_span, + hole_spans, + } } -fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 { +fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx hir::Body<'tcx>) -> u64 { // FIXME(cjgillot) Stop hashing HIR manually here. let owner = hir_body.id().hir_id.owner; tcx.hir_owner_nodes(owner).opt_hash_including_bodies.unwrap().to_smaller_hash().as_u64() } + +fn extract_hole_spans_from_hir<'tcx>( + tcx: TyCtxt<'tcx>, + body_span: Span, // Usually `hir_body.value.span`, but not always + hir_body: &hir::Body<'tcx>, +) -> Vec<Span> { + struct HolesVisitor<'hir, F> { + hir: Map<'hir>, + visit_hole_span: F, + } + + impl<'hir, F: FnMut(Span)> Visitor<'hir> for HolesVisitor<'hir, F> { + /// - We need `NestedFilter::INTRA = true` so that `visit_item` will be called. + /// - Bodies of nested items don't actually get visited, because of the + /// `visit_item` override. + /// - For nested bodies that are not part of an item, we do want to visit any + /// items contained within them. + type NestedFilter = nested_filter::All; + + fn nested_visit_map(&mut self) -> Self::Map { + self.hir + } + + fn visit_item(&mut self, item: &'hir hir::Item<'hir>) { + (self.visit_hole_span)(item.span); + // Having visited this item, we don't care about its children, + // so don't call `walk_item`. + } + + // We override `visit_expr` instead of the more specific expression + // visitors, so that we have direct access to the expression span. + fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) { + match expr.kind { + hir::ExprKind::Closure(_) | hir::ExprKind::ConstBlock(_) => { + (self.visit_hole_span)(expr.span); + // Having visited this expression, we don't care about its + // children, so don't call `walk_expr`. + } + + // For other expressions, recursively visit as normal. + _ => walk_expr(self, expr), + } + } + } + + let mut hole_spans = vec![]; + let mut visitor = HolesVisitor { + hir: tcx.hir(), + visit_hole_span: |hole_span| { + // Discard any holes that aren't directly visible within the body span. + if body_span.contains(hole_span) && body_span.eq_ctxt(hole_span) { + hole_spans.push(hole_span); + } + }, + }; + + visitor.visit_body(hir_body); + hole_spans +} diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 7612c01c52e..dbc26a2808e 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -8,7 +8,7 @@ use rustc_span::Span; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; use crate::coverage::mappings; use crate::coverage::spans::from_mir::{ - extract_covspans_and_holes_from_mir, ExtractedCovspans, Hole, SpanFromMir, + extract_covspans_from_mir, ExtractedCovspans, Hole, SpanFromMir, }; use crate::coverage::ExtractedHirInfo; @@ -20,8 +20,8 @@ pub(super) fn extract_refined_covspans( basic_coverage_blocks: &CoverageGraph, code_mappings: &mut impl Extend<mappings::CodeMapping>, ) { - let ExtractedCovspans { mut covspans, mut holes } = - extract_covspans_and_holes_from_mir(mir_body, hir_info, basic_coverage_blocks); + let ExtractedCovspans { mut covspans } = + extract_covspans_from_mir(mir_body, hir_info, basic_coverage_blocks); // First, perform the passes that need macro information. covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb)); @@ -45,6 +45,7 @@ pub(super) fn extract_refined_covspans( covspans.dedup_by(|b, a| a.span.source_equal(b.span)); // Sort the holes, and merge overlapping/adjacent holes. + let mut holes = hir_info.hole_spans.iter().map(|&span| Hole { span }).collect::<Vec<_>>(); holes.sort_by(|a, b| compare_spans(a.span, b.span)); holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b)); diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 2ca166929ee..32bd25bf4b9 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -1,8 +1,7 @@ use rustc_middle::bug; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::{ - self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, - TerminatorKind, + self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_span::{Span, Symbol}; @@ -15,13 +14,12 @@ use crate::coverage::ExtractedHirInfo; pub(crate) struct ExtractedCovspans { pub(crate) covspans: Vec<SpanFromMir>, - pub(crate) holes: Vec<Hole>, } /// Traverses the MIR body to produce an initial collection of coverage-relevant /// spans, each associated with a node in the coverage graph (BCB) and possibly /// other metadata. -pub(crate) fn extract_covspans_and_holes_from_mir( +pub(crate) fn extract_covspans_from_mir( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, basic_coverage_blocks: &CoverageGraph, @@ -29,21 +27,13 @@ pub(crate) fn extract_covspans_and_holes_from_mir( let &ExtractedHirInfo { body_span, .. } = hir_info; let mut covspans = vec![]; - let mut holes = vec![]; for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() { - bcb_to_initial_coverage_spans( - mir_body, - body_span, - bcb, - bcb_data, - &mut covspans, - &mut holes, - ); + bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data, &mut covspans); } // Only add the signature span if we found at least one span in the body. - if !covspans.is_empty() || !holes.is_empty() { + if !covspans.is_empty() { // If there is no usable signature span, add a fake one (before refinement) // to avoid an ugly gap between the body start and the first real span. // FIXME: Find a more principled way to solve this problem. @@ -51,7 +41,7 @@ pub(crate) fn extract_covspans_and_holes_from_mir( covspans.push(SpanFromMir::for_fn_sig(fn_sig_span)); } - ExtractedCovspans { covspans, holes } + ExtractedCovspans { covspans } } // Generate a set of coverage spans from the filtered set of `Statement`s and `Terminator`s of @@ -65,7 +55,6 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( bcb: BasicCoverageBlock, bcb_data: &'a BasicCoverageBlockData, initial_covspans: &mut Vec<SpanFromMir>, - holes: &mut Vec<Hole>, ) { for &bb in &bcb_data.basic_blocks { let data = &mir_body[bb]; @@ -81,13 +70,7 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( let expn_span = filtered_statement_span(statement)?; let (span, visible_macro) = unexpand(expn_span)?; - // A statement that looks like the assignment of a closure expression - // is treated as a "hole" span, to be carved out of other spans. - if is_closure_like(statement) { - holes.push(Hole { span }); - } else { - initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb)); - } + initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb)); Some(()) }; for statement in data.statements.iter() { @@ -105,18 +88,6 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>( } } -fn is_closure_like(statement: &Statement<'_>) -> bool { - match statement.kind { - StatementKind::Assign(box (_, Rvalue::Aggregate(box ref agg_kind, _))) => match agg_kind { - AggregateKind::Closure(_, _) - | AggregateKind::Coroutine(_, _) - | AggregateKind::CoroutineClosure(..) => true, - _ => false, - }, - _ => false, - } -} - /// If the MIR `Statement` has a span contributive to computing coverage spans, /// return it; otherwise return `None`. fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> { @@ -193,7 +164,8 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> { | TerminatorKind::Goto { .. } => None, // Call `func` operand can have a more specific span when part of a chain of calls - | TerminatorKind::Call { ref func, .. } => { + TerminatorKind::Call { ref func, .. } + | TerminatorKind::TailCall { ref func, .. } => { let mut span = terminator.source_info.span; if let mir::Operand::Constant(box constant) = func { if constant.span.lo() > span.lo() { diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index b1016c0867c..ab73a8af317 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -628,6 +628,12 @@ impl WriteInfo { self.add_operand(&arg.node); } } + TerminatorKind::TailCall { func, args, .. } => { + self.add_operand(func); + for arg in args { + self.add_operand(&arg.node); + } + } TerminatorKind::InlineAsm { operands, .. } => { for asm_operand in operands { match asm_operand { diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 2b7d9be6d35..1002746e553 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -1391,11 +1391,15 @@ fn op_to_prop_const<'tcx>( let (prov, offset) = pointer.into_parts(); let alloc_id = prov.alloc_id(); intern_const_alloc_for_constprop(ecx, alloc_id).ok()?; - if matches!(ecx.tcx.global_alloc(alloc_id), GlobalAlloc::Memory(_)) { - // `alloc_id` may point to a static. Codegen will choke on an `Indirect` with anything - // by `GlobalAlloc::Memory`, so do fall through to copying if needed. - // FIXME: find a way to treat this more uniformly - // (probably by fixing codegen) + + // `alloc_id` may point to a static. Codegen will choke on an `Indirect` with anything + // by `GlobalAlloc::Memory`, so do fall through to copying if needed. + // FIXME: find a way to treat this more uniformly (probably by fixing codegen) + if let GlobalAlloc::Memory(alloc) = ecx.tcx.global_alloc(alloc_id) + // Transmuting a constant is just an offset in the allocation. If the alignment of the + // allocation is not enough, fallback to copying into a properly aligned value. + && alloc.inner().align >= op.layout.align.abi + { return Some(ConstValue::Indirect { alloc_id, offset }); } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 5075e072754..fe73715480f 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -383,6 +383,8 @@ impl<'tcx> Inliner<'tcx> { ) -> Option<CallSite<'tcx>> { // Only consider direct calls to functions let terminator = bb_data.terminator(); + + // FIXME(explicit_tail_calls): figure out if we can inline tail calls if let TerminatorKind::Call { ref func, fn_span, .. } = terminator.kind { let func_ty = func.ty(caller_body, self.tcx); if let ty::FnDef(def_id, args) = *func_ty.kind() { @@ -550,6 +552,9 @@ impl<'tcx> Inliner<'tcx> { // inline-asm is detected. LLVM will still possibly do an inline later on // if the no-attribute function ends up with the same instruction set anyway. return Err("Cannot move inline-asm across instruction sets"); + } else if let TerminatorKind::TailCall { .. } = term.kind { + // FIXME(explicit_tail_calls): figure out how exactly functions containing tail calls can be inlined (and if they even should) + return Err("can't inline functions with tail calls"); } else { work_list.extend(term.successors()) } @@ -1038,6 +1043,10 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> { *target = self.map_block(*target); *unwind = self.map_unwind(*unwind); } + TerminatorKind::TailCall { .. } => { + // check_mir_body forbids tail calls + unreachable!() + } TerminatorKind::Call { ref mut target, ref mut unwind, .. } => { if let Some(ref mut tgt) = *target { *tgt = self.map_block(*tgt); diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs index 85a61f95ca3..97ec0cb39de 100644 --- a/compiler/rustc_mir_transform/src/jump_threading.rs +++ b/compiler/rustc_mir_transform/src/jump_threading.rs @@ -596,6 +596,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> { TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::CoroutineDrop => bug!("{term:?} has no terminators"), // Disallowed during optimizations. diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 8d6c00bbedb..82ad8879d17 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -799,6 +799,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> { | TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return + | TerminatorKind::TailCall { .. } | TerminatorKind::Unreachable | TerminatorKind::Drop { .. } | TerminatorKind::Yield { .. } diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs index db2bb60bdac..d928d7cf764 100644 --- a/compiler/rustc_mir_transform/src/mentioned_items.rs +++ b/compiler/rustc_mir_transform/src/mentioned_items.rs @@ -38,7 +38,7 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> { self.super_terminator(terminator, location); let span = || self.body.source_info(location).span; match &terminator.kind { - mir::TerminatorKind::Call { func, .. } => { + mir::TerminatorKind::Call { func, .. } | mir::TerminatorKind::TailCall { func, .. } => { let callee_ty = func.ty(self.body, self.tcx); self.mentioned_items .push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() }); diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index fb52bfa468a..1df5737e859 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -75,6 +75,7 @@ impl RemoveNoopLandingPads { | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Unreachable | TerminatorKind::Call { .. } + | TerminatorKind::TailCall { .. } | TerminatorKind::Assert { .. } | TerminatorKind::Drop { .. } | TerminatorKind::InlineAsm { .. } => false, diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 2018a8fe667..ab5c25c4937 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -400,40 +400,44 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { self.check_edge(location, *target, EdgeKind::Normal); self.check_unwind_edge(location, *unwind); } - TerminatorKind::Call { args, destination, target, unwind, .. } => { - if let Some(target) = target { - self.check_edge(location, *target, EdgeKind::Normal); - } - self.check_unwind_edge(location, *unwind); + TerminatorKind::Call { args, .. } | TerminatorKind::TailCall { args, .. } => { + // FIXME(explicit_tail_calls): refactor this & add tail-call specific checks + if let TerminatorKind::Call { target, unwind, destination, .. } = terminator.kind { + if let Some(target) = target { + self.check_edge(location, target, EdgeKind::Normal); + } + self.check_unwind_edge(location, unwind); + + // The code generation assumes that there are no critical call edges. The assumption + // is used to simplify inserting code that should be executed along the return edge + // from the call. FIXME(tmiasko): Since this is a strictly code generation concern, + // the code generation should be responsible for handling it. + if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized) + && self.is_critical_call_edge(target, unwind) + { + self.fail( + location, + format!( + "encountered critical edge in `Call` terminator {:?}", + terminator.kind, + ), + ); + } - // The code generation assumes that there are no critical call edges. The assumption - // is used to simplify inserting code that should be executed along the return edge - // from the call. FIXME(tmiasko): Since this is a strictly code generation concern, - // the code generation should be responsible for handling it. - if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized) - && self.is_critical_call_edge(*target, *unwind) - { - self.fail( - location, - format!( - "encountered critical edge in `Call` terminator {:?}", - terminator.kind, - ), - ); + // The call destination place and Operand::Move place used as an argument might be + // passed by a reference to the callee. Consequently they cannot be packed. + if is_within_packed(self.tcx, &self.body.local_decls, destination).is_some() { + // This is bad! The callee will expect the memory to be aligned. + self.fail( + location, + format!( + "encountered packed place in `Call` terminator destination: {:?}", + terminator.kind, + ), + ); + } } - // The call destination place and Operand::Move place used as an argument might be - // passed by a reference to the callee. Consequently they cannot be packed. - if is_within_packed(self.tcx, &self.body.local_decls, *destination).is_some() { - // This is bad! The callee will expect the memory to be aligned. - self.fail( - location, - format!( - "encountered packed place in `Call` terminator destination: {:?}", - terminator.kind, - ), - ); - } for arg in args { if let Operand::Move(place) = &arg.node { if is_within_packed(self.tcx, &self.body.local_decls, *place).is_some() { @@ -1498,15 +1502,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } } - TerminatorKind::Call { func, .. } => { + TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => { let func_ty = func.ty(&self.body.local_decls, self.tcx); match func_ty.kind() { ty::FnPtr(..) | ty::FnDef(..) => {} _ => self.fail( location, - format!("encountered non-callable type {func_ty} in `Call` terminator"), + format!( + "encountered non-callable type {func_ty} in `{}` terminator", + terminator.kind.name() + ), ), } + + if let TerminatorKind::TailCall { .. } = terminator.kind { + // FIXME(explicit_tail_calls): implement tail-call specific checks here (such as signature matching, forbidding closures, etc) + } } TerminatorKind::Assert { cond, .. } => { let cond_ty = cond.ty(&self.body.local_decls, self.tcx); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 005a22f642f..bfd505c0672 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -755,7 +755,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { }; match terminator.kind { - mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } => { + mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } + | mir::TerminatorKind::TailCall { ref func, ref args, ref fn_span } => { let callee_ty = func.ty(self.body, tcx); // *Before* monomorphizing, record that we already handled this mention. self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty)); diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs new file mode 100644 index 00000000000..55f602d907b --- /dev/null +++ b/compiler/rustc_next_trait_solver/src/coherence.rs @@ -0,0 +1,469 @@ +use std::fmt::Debug; +use std::ops::ControlFlow; + +use rustc_type_ir::inherent::*; +use rustc_type_ir::visit::{TypeVisitable, TypeVisitableExt, TypeVisitor}; +use rustc_type_ir::{self as ty, InferCtxtLike, Interner}; +use tracing::instrument; + +/// Whether we do the orphan check relative to this crate or to some remote crate. +#[derive(Copy, Clone, Debug)] +pub enum InCrate { + Local { mode: OrphanCheckMode }, + Remote, +} + +#[derive(Copy, Clone, Debug)] +pub enum OrphanCheckMode { + /// Proper orphan check. + Proper, + /// Improper orphan check for backward compatibility. + /// + /// In this mode, type params inside projections are considered to be covered + /// even if the projection may normalize to a type that doesn't actually cover + /// them. This is unsound. See also [#124559] and [#99554]. + /// + /// [#124559]: https://github.com/rust-lang/rust/issues/124559 + /// [#99554]: https://github.com/rust-lang/rust/issues/99554 + Compat, +} + +#[derive(Debug, Copy, Clone)] +pub enum Conflict { + Upstream, + Downstream, +} + +/// Returns whether all impls which would apply to the `trait_ref` +/// e.g. `Ty: Trait<Arg>` are already known in the local crate. +/// +/// This both checks whether any downstream or sibling crates could +/// implement it and whether an upstream crate can add this impl +/// without breaking backwards compatibility. +#[instrument(level = "debug", skip(infcx, lazily_normalize_ty), ret)] +pub fn trait_ref_is_knowable<Infcx, I, E>( + infcx: &Infcx, + trait_ref: ty::TraitRef<I>, + mut lazily_normalize_ty: impl FnMut(I::Ty) -> Result<I::Ty, E>, +) -> Result<Result<(), Conflict>, E> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, + E: Debug, +{ + if orphan_check_trait_ref(infcx, trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok() + { + // A downstream or cousin crate is allowed to implement some + // generic parameters of this trait-ref. + return Ok(Err(Conflict::Downstream)); + } + + if trait_ref_is_local_or_fundamental(infcx.cx(), trait_ref) { + // This is a local or fundamental trait, so future-compatibility + // is no concern. We know that downstream/cousin crates are not + // allowed to implement a generic parameter of this trait ref, + // which means impls could only come from dependencies of this + // crate, which we already know about. + return Ok(Ok(())); + } + + // This is a remote non-fundamental trait, so if another crate + // can be the "final owner" of the generic parameters of this trait-ref, + // they are allowed to implement it future-compatibly. + // + // However, if we are a final owner, then nobody else can be, + // and if we are an intermediate owner, then we don't care + // about future-compatibility, which means that we're OK if + // we are an owner. + if orphan_check_trait_ref( + infcx, + trait_ref, + InCrate::Local { mode: OrphanCheckMode::Proper }, + &mut lazily_normalize_ty, + )? + .is_ok() + { + Ok(Ok(())) + } else { + Ok(Err(Conflict::Upstream)) + } +} + +pub fn trait_ref_is_local_or_fundamental<I: Interner>(tcx: I, trait_ref: ty::TraitRef<I>) -> bool { + trait_ref.def_id.is_local() || tcx.trait_is_fundamental(trait_ref.def_id) +} + +#[derive(Debug, Copy, Clone)] +pub enum IsFirstInputType { + No, + Yes, +} + +impl From<bool> for IsFirstInputType { + fn from(b: bool) -> IsFirstInputType { + match b { + false => IsFirstInputType::No, + true => IsFirstInputType::Yes, + } + } +} + +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = "T: Debug"))] +pub enum OrphanCheckErr<I: Interner, T> { + NonLocalInputType(Vec<(I::Ty, IsFirstInputType)>), + UncoveredTyParams(UncoveredTyParams<I, T>), +} + +#[derive(derivative::Derivative)] +#[derivative(Debug(bound = "T: Debug"))] +pub struct UncoveredTyParams<I: Interner, T> { + pub uncovered: T, + pub local_ty: Option<I::Ty>, +} + +/// Checks whether a trait-ref is potentially implementable by a crate. +/// +/// The current rule is that a trait-ref orphan checks in a crate C: +/// +/// 1. Order the parameters in the trait-ref in generic parameters order +/// - Self first, others linearly (e.g., `<U as Foo<V, W>>` is U < V < W). +/// 2. Of these type parameters, there is at least one type parameter +/// in which, walking the type as a tree, you can reach a type local +/// to C where all types in-between are fundamental types. Call the +/// first such parameter the "local key parameter". +/// - e.g., `Box<LocalType>` is OK, because you can visit LocalType +/// going through `Box`, which is fundamental. +/// - similarly, `FundamentalPair<Vec<()>, Box<LocalType>>` is OK for +/// the same reason. +/// - but (knowing that `Vec<T>` is non-fundamental, and assuming it's +/// not local), `Vec<LocalType>` is bad, because `Vec<->` is between +/// the local type and the type parameter. +/// 3. Before this local type, no generic type parameter of the impl must +/// be reachable through fundamental types. +/// - e.g. `impl<T> Trait<LocalType> for Vec<T>` is fine, as `Vec` is not fundamental. +/// - while `impl<T> Trait<LocalType> for Box<T>` results in an error, as `T` is +/// reachable through the fundamental type `Box`. +/// 4. Every type in the local key parameter not known in C, going +/// through the parameter's type tree, must appear only as a subtree of +/// a type local to C, with only fundamental types between the type +/// local to C and the local key parameter. +/// - e.g., `Vec<LocalType<T>>>` (or equivalently `Box<Vec<LocalType<T>>>`) +/// is bad, because the only local type with `T` as a subtree is +/// `LocalType<T>`, and `Vec<->` is between it and the type parameter. +/// - similarly, `FundamentalPair<LocalType<T>, T>` is bad, because +/// the second occurrence of `T` is not a subtree of *any* local type. +/// - however, `LocalType<Vec<T>>` is OK, because `T` is a subtree of +/// `LocalType<Vec<T>>`, which is local and has no types between it and +/// the type parameter. +/// +/// The orphan rules actually serve several different purposes: +/// +/// 1. They enable link-safety - i.e., 2 mutually-unknowing crates (where +/// every type local to one crate is unknown in the other) can't implement +/// the same trait-ref. This follows because it can be seen that no such +/// type can orphan-check in 2 such crates. +/// +/// To check that a local impl follows the orphan rules, we check it in +/// InCrate::Local mode, using type parameters for the "generic" types. +/// +/// In InCrate::Local mode the orphan check succeeds if the current crate +/// is definitely allowed to implement the given trait (no false positives). +/// +/// 2. They ground negative reasoning for coherence. If a user wants to +/// write both a conditional blanket impl and a specific impl, we need to +/// make sure they do not overlap. For example, if we write +/// ```ignore (illustrative) +/// impl<T> IntoIterator for Vec<T> +/// impl<T: Iterator> IntoIterator for T +/// ``` +/// We need to be able to prove that `Vec<$0>: !Iterator` for every type $0. +/// We can observe that this holds in the current crate, but we need to make +/// sure this will also hold in all unknown crates (both "independent" crates, +/// which we need for link-safety, and also child crates, because we don't want +/// child crates to get error for impl conflicts in a *dependency*). +/// +/// For that, we only allow negative reasoning if, for every assignment to the +/// inference variables, every unknown crate would get an orphan error if they +/// try to implement this trait-ref. To check for this, we use InCrate::Remote +/// mode. That is sound because we already know all the impls from known crates. +/// +/// In InCrate::Remote mode the orphan check succeeds if a foreign crate +/// *could* implement the given trait (no false negatives). +/// +/// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can +/// add "non-blanket" impls without breaking negative reasoning in dependent +/// crates. This is the "rebalancing coherence" (RFC 1023) restriction. +/// +/// For that, we only allow a crate to perform negative reasoning on +/// non-local-non-`#[fundamental]` if there's a local key parameter as per (2). +/// +/// Because we never perform negative reasoning generically (coherence does +/// not involve type parameters), this can be interpreted as doing the full +/// orphan check (using InCrate::Local mode), instantiating non-local known +/// types for all inference variables. +/// +/// This allows for crates to future-compatibly add impls as long as they +/// can't apply to types with a key parameter in a child crate - applying +/// the rules, this basically means that every type parameter in the impl +/// must appear behind a non-fundamental type (because this is not a +/// type-system requirement, crate owners might also go for "semantic +/// future-compatibility" involving things such as sealed traits, but +/// the above requirement is sufficient, and is necessary in "open world" +/// cases). +/// +/// Note that this function is never called for types that have both type +/// parameters and inference variables. +#[instrument(level = "trace", skip(infcx, lazily_normalize_ty), ret)] +pub fn orphan_check_trait_ref<Infcx, I, E: Debug>( + infcx: &Infcx, + trait_ref: ty::TraitRef<I>, + in_crate: InCrate, + lazily_normalize_ty: impl FnMut(I::Ty) -> Result<I::Ty, E>, +) -> Result<Result<(), OrphanCheckErr<I, I::Ty>>, E> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, + E: Debug, +{ + if trait_ref.has_param() { + panic!("orphan check only expects inference variables: {trait_ref:?}"); + } + + let mut checker = OrphanChecker::new(infcx, in_crate, lazily_normalize_ty); + Ok(match trait_ref.visit_with(&mut checker) { + ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)), + ControlFlow::Break(residual) => match residual { + OrphanCheckEarlyExit::NormalizationFailure(err) => return Err(err), + OrphanCheckEarlyExit::UncoveredTyParam(ty) => { + // Does there exist some local type after the `ParamTy`. + checker.search_first_local_ty = true; + let local_ty = match trait_ref.visit_with(&mut checker) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(local_ty)) => Some(local_ty), + _ => None, + }; + Err(OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { + uncovered: ty, + local_ty, + })) + } + OrphanCheckEarlyExit::LocalTy(_) => Ok(()), + }, + }) +} + +struct OrphanChecker<'a, Infcx, I: Interner, F> { + infcx: &'a Infcx, + in_crate: InCrate, + in_self_ty: bool, + lazily_normalize_ty: F, + /// Ignore orphan check failures and exclusively search for the first local type. + search_first_local_ty: bool, + non_local_tys: Vec<(I::Ty, IsFirstInputType)>, +} + +impl<'a, Infcx, I, F, E> OrphanChecker<'a, Infcx, I, F> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, + F: FnOnce(I::Ty) -> Result<I::Ty, E>, +{ + fn new(infcx: &'a Infcx, in_crate: InCrate, lazily_normalize_ty: F) -> Self { + OrphanChecker { + infcx, + in_crate, + in_self_ty: true, + lazily_normalize_ty, + search_first_local_ty: false, + non_local_tys: Vec::new(), + } + } + + fn found_non_local_ty(&mut self, t: I::Ty) -> ControlFlow<OrphanCheckEarlyExit<I, E>> { + self.non_local_tys.push((t, self.in_self_ty.into())); + ControlFlow::Continue(()) + } + + fn found_uncovered_ty_param(&mut self, ty: I::Ty) -> ControlFlow<OrphanCheckEarlyExit<I, E>> { + if self.search_first_local_ty { + return ControlFlow::Continue(()); + } + + ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty)) + } + + fn def_id_is_local(&mut self, def_id: I::DefId) -> bool { + match self.in_crate { + InCrate::Local { .. } => def_id.is_local(), + InCrate::Remote => false, + } + } +} + +enum OrphanCheckEarlyExit<I: Interner, E> { + NormalizationFailure(E), + UncoveredTyParam(I::Ty), + LocalTy(I::Ty), +} + +impl<'a, Infcx, I, F, E> TypeVisitor<I> for OrphanChecker<'a, Infcx, I, F> +where + Infcx: InferCtxtLike<Interner = I>, + I: Interner, + F: FnMut(I::Ty) -> Result<I::Ty, E>, +{ + type Result = ControlFlow<OrphanCheckEarlyExit<I, E>>; + + fn visit_region(&mut self, _r: I::Region) -> Self::Result { + ControlFlow::Continue(()) + } + + fn visit_ty(&mut self, ty: I::Ty) -> Self::Result { + let ty = self.infcx.shallow_resolve(ty); + let ty = match (self.lazily_normalize_ty)(ty) { + Ok(norm_ty) if norm_ty.is_ty_var() => ty, + Ok(norm_ty) => norm_ty, + Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)), + }; + + let result = match ty.kind() { + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Str + | ty::FnDef(..) + | ty::Pat(..) + | ty::FnPtr(_) + | ty::Array(..) + | ty::Slice(..) + | ty::RawPtr(..) + | ty::Never + | ty::Tuple(..) => self.found_non_local_ty(ty), + + ty::Param(..) => panic!("unexpected ty param"), + + ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => { + match self.in_crate { + InCrate::Local { .. } => self.found_uncovered_ty_param(ty), + // The inference variable might be unified with a local + // type in that remote crate. + InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + } + } + + // A rigid alias may normalize to anything. + // * If it references an infer var, placeholder or bound ty, it may + // normalize to that, so we have to treat it as an uncovered ty param. + // * Otherwise it may normalize to any non-type-generic type + // be it local or non-local. + ty::Alias(kind, _) => { + if ty.has_type_flags( + ty::TypeFlags::HAS_TY_PLACEHOLDER + | ty::TypeFlags::HAS_TY_BOUND + | ty::TypeFlags::HAS_TY_INFER, + ) { + match self.in_crate { + InCrate::Local { mode } => match kind { + ty::Projection => { + if let OrphanCheckMode::Compat = mode { + ControlFlow::Continue(()) + } else { + self.found_uncovered_ty_param(ty) + } + } + _ => self.found_uncovered_ty_param(ty), + }, + InCrate::Remote => { + // The inference variable might be unified with a local + // type in that remote crate. + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } + } + } else { + // Regarding *opaque types* specifically, we choose to treat them as non-local, + // even those that appear within the same crate. This seems somewhat surprising + // at first, but makes sense when you consider that opaque types are supposed + // to hide the underlying type *within the same crate*. When an opaque type is + // used from outside the module where it is declared, it should be impossible to + // observe anything about it other than the traits that it implements. + // + // The alternative would be to look at the underlying type to determine whether + // or not the opaque type itself should be considered local. + // + // However, this could make it a breaking change to switch the underlying hidden + // type from a local type to a remote type. This would violate the rule that + // opaque types should be completely opaque apart from the traits that they + // implement, so we don't use this behavior. + // Addendum: Moreover, revealing the underlying type is likely to cause cycle + // errors as we rely on coherence / the specialization graph during typeck. + + self.found_non_local_ty(ty) + } + } + + // For fundamental types, we just look inside of them. + ty::Ref(_, ty, _) => ty.visit_with(self), + ty::Adt(def, args) => { + if self.def_id_is_local(def.def_id()) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else if def.is_fundamental() { + args.visit_with(self) + } else { + self.found_non_local_ty(ty) + } + } + ty::Foreign(def_id) => { + if self.def_id_is_local(def_id) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + ty::Dynamic(tt, ..) => { + let principal = tt.principal().map(|p| p.def_id()); + if principal.is_some_and(|p| self.def_id_is_local(p)) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + ty::Closure(did, ..) | ty::CoroutineClosure(did, ..) | ty::Coroutine(did, ..) => { + if self.def_id_is_local(did) { + ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) + } else { + self.found_non_local_ty(ty) + } + } + // This should only be created when checking whether we have to check whether some + // auto trait impl applies. There will never be multiple impls, so we can just + // act as if it were a local type here. + ty::CoroutineWitness(..) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), + }; + // A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so + // the first type we visit is always the self type. + self.in_self_ty = false; + result + } + + /// All possible values for a constant parameter already exist + /// in the crate defining the trait, so they are always non-local[^1]. + /// + /// Because there's no way to have an impl where the first local + /// generic argument is a constant, we also don't have to fail + /// the orphan check when encountering a parameter or a generic constant. + /// + /// This means that we can completely ignore constants during the orphan check. + /// + /// See `tests/ui/coherence/const-generics-orphan-check-ok.rs` for examples. + /// + /// [^1]: This might not hold for function pointers or trait objects in the future. + /// As these should be quite rare as const arguments and especially rare as impl + /// parameters, allowing uncovered const parameters in impls seems more useful + /// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile. + fn visit_const(&mut self, _c: I::Const) -> Self::Result { + ControlFlow::Continue(()) + } +} diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index 31032dc679a..6a3d58b5906 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -1,4 +1,3 @@ -use std::fmt::Debug; use std::ops::Deref; use rustc_type_ir::fold::TypeFoldable; @@ -32,12 +31,6 @@ pub trait SolverDelegate: // FIXME: Uplift the leak check into this crate. fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution>; - // FIXME: This is only here because elaboration lives in `rustc_infer`! - fn elaborate_supertraits( - cx: Self::Interner, - trait_ref: ty::Binder<Self::Interner, ty::TraitRef<Self::Interner>>, - ) -> impl Iterator<Item = ty::Binder<Self::Interner, ty::TraitRef<Self::Interner>>>; - fn try_const_eval_resolve( &self, param_env: <Self::Interner as Interner>::ParamEnv, @@ -99,14 +92,6 @@ pub trait SolverDelegate: fn reset_opaque_types(&self); - fn trait_ref_is_knowable<E: Debug>( - &self, - trait_ref: ty::TraitRef<Self::Interner>, - lazily_normalize_ty: impl FnMut( - <Self::Interner as Interner>::Ty, - ) -> Result<<Self::Interner as Interner>::Ty, E>, - ) -> Result<bool, E>; - fn fetch_eligible_assoc_item( &self, param_env: <Self::Interner as Interner>::ParamEnv, diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs index 761475d3d6b..0a5b4278058 100644 --- a/compiler/rustc_next_trait_solver/src/lib.rs +++ b/compiler/rustc_next_trait_solver/src/lib.rs @@ -5,6 +5,7 @@ //! So if you got to this crate from the old solver, it's totally normal. pub mod canonicalizer; +pub mod coherence; pub mod delegate; pub mod relate; pub mod resolve; diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 38a4f7dfe25..01dde9ca587 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -2,6 +2,7 @@ pub(super) mod structural_traits; +use rustc_type_ir::elaborate; use rustc_type_ir::fold::TypeFoldable; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; @@ -667,7 +668,7 @@ where // a projection goal. if let Some(principal) = bounds.principal() { let principal_trait_ref = principal.with_self_ty(cx, self_ty); - for (idx, assumption) in D::elaborate_supertraits(cx, principal_trait_ref).enumerate() { + for (idx, assumption) in elaborate::supertraits(cx, principal_trait_ref).enumerate() { candidates.extend(G::probe_and_consider_object_bound_candidate( self, CandidateSource::BuiltinImpl(BuiltinImplSource::Object(idx)), diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 3447b39fa5b..7df14e81ab5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -669,7 +669,9 @@ where let cx = ecx.cx(); let mut requirements = vec![]; requirements.extend( - cx.explicit_super_predicates_of(trait_ref.def_id).iter_instantiated(cx, trait_ref.args), + cx.explicit_super_predicates_of(trait_ref.def_id) + .iter_instantiated(cx, trait_ref.args) + .map(|(pred, _)| pred), ); // FIXME(associated_const_equality): Also add associated consts to diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index d8a3acc655a..c90f8e76163 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -11,6 +11,7 @@ use rustc_type_ir::{self as ty, CanonicalVarValues, InferCtxtLike, Interner}; use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic}; use tracing::{instrument, trace}; +use crate::coherence; use crate::delegate::SolverDelegate; use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::search_graph::SearchGraph; @@ -906,7 +907,8 @@ where ) -> Result<bool, NoSolution> { let delegate = self.delegate; let lazily_normalize_ty = |ty| self.structurally_normalize_ty(param_env, ty); - delegate.trait_ref_is_knowable(trait_ref, lazily_normalize_ty) + coherence::trait_ref_is_knowable(&**delegate, trait_ref, lazily_normalize_ty) + .map(|is_knowable| is_knowable.is_ok()) } pub(super) fn fetch_eligible_assoc_item( diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index b2a59eece0d..4474bbc2351 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -6,7 +6,7 @@ use rustc_type_ir::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; use rustc_type_ir::visit::TypeVisitableExt as _; -use rustc_type_ir::{self as ty, Interner, TraitPredicate, Upcast as _}; +use rustc_type_ir::{self as ty, elaborate, Interner, TraitPredicate, Upcast as _}; use tracing::{instrument, trace}; use crate::delegate::SolverDelegate; @@ -787,7 +787,7 @@ where )); } else if let Some(a_principal) = a_data.principal() { for new_a_principal in - D::elaborate_supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty)).skip(1) + elaborate::supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty)).skip(1) { responses.extend(self.consider_builtin_upcast_to_principal( goal, @@ -862,8 +862,7 @@ where .auto_traits() .into_iter() .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| { - self.cx() - .supertrait_def_ids(principal_def_id) + elaborate::supertrait_def_ids(self.cx(), principal_def_id) .into_iter() .filter(|def_id| self.cx().trait_is_auto(*def_id)) })) diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 58fef9b6c45..a8fe35f45b3 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -282,7 +282,7 @@ impl<'a> Parser<'a> { pub fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> { let mut attrs = ast::AttrVec::new(); loop { - let start_pos: u32 = self.num_bump_calls.try_into().unwrap(); + let start_pos = self.num_bump_calls; // Only try to parse if it is an inner attribute (has `!`). let attr = if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) { Some(self.parse_attribute(InnerAttrPolicy::Permitted)?) @@ -303,7 +303,7 @@ impl<'a> Parser<'a> { None }; if let Some(attr) = attr { - let end_pos: u32 = self.num_bump_calls.try_into().unwrap(); + let end_pos = self.num_bump_calls; // If we are currently capturing tokens, mark the location of this inner attribute. // If capturing ends up creating a `LazyAttrTokenStream`, we will include // this replace range with it, removing the inner attribute from the final @@ -313,7 +313,7 @@ impl<'a> Parser<'a> { // corresponding macro). let range = start_pos..end_pos; if let Capturing::Yes = self.capture_state.capturing { - self.capture_state.inner_attr_ranges.insert(attr.id, (range, vec![])); + self.capture_state.inner_attr_ranges.insert(attr.id, (range, None)); } attrs.push(attr); } else { diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 13a647adfe3..38f18022e3c 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -1,6 +1,6 @@ use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor, TrailingToken}; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; -use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree, AttributesData, DelimSpacing}; +use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree, AttrsTarget, DelimSpacing}; use rustc_ast::tokenstream::{DelimSpan, LazyAttrTokenStream, Spacing, ToAttrTokenStream}; use rustc_ast::{self as ast}; use rustc_ast::{AttrVec, Attribute, HasAttrs, HasTokens}; @@ -8,7 +8,6 @@ use rustc_errors::PResult; use rustc_session::parse::ParseSess; use rustc_span::{sym, Span, DUMMY_SP}; -use std::ops::Range; use std::{iter, mem}; /// A wrapper type to ensure that the parser handles outer attributes correctly. @@ -88,7 +87,6 @@ fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool { // // This also makes `Parser` very cheap to clone, since // there is no intermediate collection buffer to clone. -#[derive(Clone)] struct LazyAttrTokenStreamImpl { start_token: (Token, Spacing), cursor_snapshot: TokenCursor, @@ -146,24 +144,23 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { // start position, we ensure that any replace range which encloses // another replace range will capture the *replaced* tokens for the inner // range, not the original tokens. - for (range, new_tokens) in replace_ranges.into_iter().rev() { + for (range, target) in replace_ranges.into_iter().rev() { assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}"); - // Replace ranges are only allowed to decrease the number of tokens. - assert!( - range.len() >= new_tokens.len(), - "Range {range:?} has greater len than {new_tokens:?}" - ); - - // Replace any removed tokens with `FlatToken::Empty`. - // This keeps the total length of `tokens` constant throughout the - // replacement process, allowing us to use all of the `ReplaceRanges` entries - // without adjusting indices. - let filler = iter::repeat((FlatToken::Empty, Spacing::Alone)) - .take(range.len() - new_tokens.len()); + // Replace the tokens in range with zero or one `FlatToken::AttrsTarget`s, plus + // enough `FlatToken::Empty`s to fill up the rest of the range. This keeps the + // total length of `tokens` constant throughout the replacement process, allowing + // us to use all of the `ReplaceRanges` entries without adjusting indices. + let target_len = target.is_some() as usize; tokens.splice( (range.start as usize)..(range.end as usize), - new_tokens.into_iter().chain(filler), + target + .into_iter() + .map(|target| (FlatToken::AttrsTarget(target), Spacing::Alone)) + .chain( + iter::repeat((FlatToken::Empty, Spacing::Alone)) + .take(range.len() - target_len), + ), ); } make_attr_token_stream(tokens.into_iter(), self.break_last_token) @@ -316,7 +313,7 @@ impl<'a> Parser<'a> { .iter() .cloned() .chain(inner_attr_replace_ranges.iter().cloned()) - .map(|(range, tokens)| ((range.start - start_pos)..(range.end - start_pos), tokens)) + .map(|(range, data)| ((range.start - start_pos)..(range.end - start_pos), data)) .collect() }; @@ -346,18 +343,14 @@ impl<'a> Parser<'a> { && matches!(self.capture_state.capturing, Capturing::Yes) && has_cfg_or_cfg_attr(final_attrs) { - let attr_data = AttributesData { attrs: final_attrs.iter().cloned().collect(), tokens }; + assert!(!self.break_last_token, "Should not have unglued last token with cfg attr"); - // Replace the entire AST node that we just parsed, including attributes, - // with a `FlatToken::AttrTarget`. If this AST node is inside an item - // that has `#[derive]`, then this will allow us to cfg-expand this - // AST node. + // Replace the entire AST node that we just parsed, including attributes, with + // `target`. If this AST node is inside an item that has `#[derive]`, then this will + // allow us to cfg-expand this AST node. let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos }; - let new_tokens = vec![(FlatToken::AttrTarget(attr_data), Spacing::Alone)]; - - assert!(!self.break_last_token, "Should not have unglued last token with cfg attr"); - let range: Range<u32> = (start_pos.try_into().unwrap())..(end_pos.try_into().unwrap()); - self.capture_state.replace_ranges.push((range, new_tokens)); + let target = AttrsTarget { attrs: final_attrs.iter().cloned().collect(), tokens }; + self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target))); self.capture_state.replace_ranges.extend(inner_attr_replace_ranges); } @@ -419,11 +412,11 @@ fn make_attr_token_stream( .expect("Bottom token frame is missing!") .inner .push(AttrTokenTree::Token(token, spacing)), - FlatToken::AttrTarget(data) => stack + FlatToken::AttrsTarget(target) => stack .last_mut() .expect("Bottom token frame is missing!") .inner - .push(AttrTokenTree::Attributes(data)), + .push(AttrTokenTree::AttrsTarget(target)), FlatToken::Empty => {} } token_and_spacing = iter.next(); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 5f16a3e1f37..45ca267fe5d 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -20,7 +20,7 @@ use path::PathStyle; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind}; -use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing}; +use rustc_ast::tokenstream::{AttrsTarget, DelimSpacing, DelimSpan, Spacing}; use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor}; use rustc_ast::util::case::Case; use rustc_ast::{ @@ -203,13 +203,13 @@ struct ClosureSpans { } /// Indicates a range of tokens that should be replaced by -/// the tokens in the provided vector. This is used in two +/// the tokens in the provided `AttrsTarget`. This is used in two /// places during token collection: /// /// 1. During the parsing of an AST node that may have a `#[derive]` /// attribute, we parse a nested AST node that has `#[cfg]` or `#[cfg_attr]` /// In this case, we use a `ReplaceRange` to replace the entire inner AST node -/// with `FlatToken::AttrTarget`, allowing us to perform eager cfg-expansion +/// with `FlatToken::AttrsTarget`, allowing us to perform eager cfg-expansion /// on an `AttrTokenStream`. /// /// 2. When we parse an inner attribute while collecting tokens. We @@ -219,7 +219,7 @@ struct ClosureSpans { /// the first macro inner attribute to invoke a proc-macro). /// When create a `TokenStream`, the inner attributes get inserted /// into the proper place in the token stream. -type ReplaceRange = (Range<u32>, Vec<(FlatToken, Spacing)>); +type ReplaceRange = (Range<u32>, Option<AttrsTarget>); /// Controls how we capture tokens. Capturing can be expensive, /// so we try to avoid performing capturing in cases where @@ -1608,11 +1608,10 @@ enum FlatToken { /// A token - this holds both delimiter (e.g. '{' and '}') /// and non-delimiter tokens Token(Token), - /// Holds the `AttributesData` for an AST node. The - /// `AttributesData` is inserted directly into the - /// constructed `AttrTokenStream` as - /// an `AttrTokenTree::Attributes`. - AttrTarget(AttributesData), + /// Holds the `AttrsTarget` for an AST node. The `AttrsTarget` is inserted + /// directly into the constructed `AttrTokenStream` as an + /// `AttrTokenTree::AttrsTarget`. + AttrsTarget(AttrsTarget), /// A special 'empty' token that is ignored during the conversion /// to an `AttrTokenStream`. This is used to simplify the /// handling of replace ranges. diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 4e0f2792d97..92cf73870ff 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -321,8 +321,14 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> { // The fields are not expanded yet. return; } - let def_ids = fields.iter().map(|field| self.r.local_def_id(field.id).to_def_id()); - self.r.field_def_ids.insert(def_id, self.r.tcx.arena.alloc_from_iter(def_ids)); + let fields = fields + .iter() + .enumerate() + .map(|(i, field)| { + field.ident.unwrap_or_else(|| Ident::from_str_and_span(&format!("{i}"), field.span)) + }) + .collect(); + self.r.field_names.insert(def_id, fields); } fn insert_field_visibilities_local(&mut self, def_id: DefId, fields: &[ast::FieldDef]) { diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 80619c59cc3..a4fdb4a0baf 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -8,6 +8,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_span::hygiene::LocalExpnId; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::Span; +use std::mem; use tracing::debug; pub(crate) fn collect_definitions( @@ -15,8 +16,9 @@ pub(crate) fn collect_definitions( fragment: &AstFragment, expansion: LocalExpnId, ) { - let (parent_def, impl_trait_context) = resolver.invocation_parents[&expansion]; - fragment.visit_with(&mut DefCollector { resolver, parent_def, expansion, impl_trait_context }); + let (parent_def, impl_trait_context, in_attr) = resolver.invocation_parents[&expansion]; + let mut visitor = DefCollector { resolver, parent_def, expansion, impl_trait_context, in_attr }; + fragment.visit_with(&mut visitor); } /// Creates `DefId`s for nodes in the AST. @@ -24,6 +26,7 @@ struct DefCollector<'a, 'b, 'tcx> { resolver: &'a mut Resolver<'b, 'tcx>, parent_def: LocalDefId, impl_trait_context: ImplTraitContext, + in_attr: bool, expansion: LocalExpnId, } @@ -53,7 +56,7 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> { } fn with_parent<F: FnOnce(&mut Self)>(&mut self, parent_def: LocalDefId, f: F) { - let orig_parent_def = std::mem::replace(&mut self.parent_def, parent_def); + let orig_parent_def = mem::replace(&mut self.parent_def, parent_def); f(self); self.parent_def = orig_parent_def; } @@ -63,7 +66,7 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> { impl_trait_context: ImplTraitContext, f: F, ) { - let orig_itc = std::mem::replace(&mut self.impl_trait_context, impl_trait_context); + let orig_itc = mem::replace(&mut self.impl_trait_context, impl_trait_context); f(self); self.impl_trait_context = orig_itc; } @@ -105,8 +108,10 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> { fn visit_macro_invoc(&mut self, id: NodeId) { let id = id.placeholder_to_expn_id(); - let old_parent = - self.resolver.invocation_parents.insert(id, (self.parent_def, self.impl_trait_context)); + let old_parent = self + .resolver + .invocation_parents + .insert(id, (self.parent_def, self.impl_trait_context, self.in_attr)); assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation"); } } @@ -413,4 +418,10 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> { visit::walk_crate(self, krate) } } + + fn visit_attribute(&mut self, attr: &'a Attribute) -> Self::Result { + let orig_in_attr = mem::replace(&mut self.in_attr, true); + visit::walk_attribute(self, attr); + self.in_attr = orig_in_attr; + } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 50a4e03d233..ffd495aa985 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1726,11 +1726,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { )) = binding.kind { let def_id = self.tcx.parent(ctor_def_id); - return self - .field_def_ids(def_id)? - .iter() - .map(|&field_id| self.def_span(field_id)) - .reduce(Span::to); // None for `struct Foo()` + return self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to); // None for `struct Foo()` } None } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index ad4e222f4de..1d37264f96a 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1744,7 +1744,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { ) { self.r.dcx().emit_err(errors::LendingIteratorReportError { lifetime: lifetime.ident.span, - ty: ty.span(), + ty: ty.span, }); } else { self.r.dcx().emit_err(errors::AnonymousLivetimeNonGatReportError { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 764cc350182..941fb6436df 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1532,17 +1532,17 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { if !this.has_private_fields(def_id) { // If the fields of the type are private, we shouldn't be suggesting using // the struct literal syntax at all, as that will cause a subsequent error. - let field_ids = this.r.field_def_ids(def_id); - let (fields, applicability) = match field_ids { - Some(field_ids) => { - let fields = field_ids.iter().map(|&id| this.r.tcx.item_name(id)); - + let fields = this.r.field_idents(def_id); + let has_fields = fields.as_ref().is_some_and(|f| !f.is_empty()); + let (fields, applicability) = match fields { + Some(fields) => { let fields = if let Some(old_fields) = old_fields { fields + .iter() .enumerate() .map(|(idx, new)| (new, old_fields.get(idx))) .map(|(new, old)| { - let new = new.to_ident_string(); + let new = new.name.to_ident_string(); if let Some(Some(old)) = old && new != *old { @@ -1553,17 +1553,17 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { }) .collect::<Vec<String>>() } else { - fields.map(|f| format!("{f}{tail}")).collect::<Vec<String>>() + fields + .iter() + .map(|f| format!("{f}{tail}")) + .collect::<Vec<String>>() }; (fields.join(", "), applicability) } None => ("/* fields */".to_string(), Applicability::HasPlaceholders), }; - let pad = match field_ids { - Some([]) => "", - _ => " ", - }; + let pad = if has_fields { " " } else { "" }; err.span_suggestion( span, format!("use struct {descr} syntax instead"), @@ -1723,12 +1723,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { &args[..], ); // Use spans of the tuple struct definition. - self.r.field_def_ids(def_id).map(|field_ids| { - field_ids - .iter() - .map(|&field_id| self.r.def_span(field_id)) - .collect::<Vec<_>>() - }) + self.r + .field_idents(def_id) + .map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>()) } _ => None, }; @@ -1791,7 +1788,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { (Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_def_id), _) if ns == ValueNS => { let def_id = self.r.tcx.parent(ctor_def_id); err.span_label(self.r.def_span(def_id), format!("`{path_str}` defined here")); - let fields = self.r.field_def_ids(def_id).map_or_else( + let fields = self.r.field_idents(def_id).map_or_else( || "/* fields */".to_string(), |field_ids| vec!["_"; field_ids.len()].join(", "), ); @@ -2017,12 +2014,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { if let Some(Res::Def(DefKind::Struct | DefKind::Union, did)) = resolution.full_res() { - if let Some(field_ids) = self.r.field_def_ids(did) { - if let Some(field_id) = field_ids - .iter() - .find(|&&field_id| ident.name == self.r.tcx.item_name(field_id)) - { - return Some(AssocSuggestion::Field(self.r.def_span(*field_id))); + if let Some(fields) = self.r.field_idents(did) { + if let Some(field) = fields.iter().find(|id| ident.name == id.name) { + return Some(AssocSuggestion::Field(field.span)); } } } @@ -2418,7 +2412,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> { match kind { CtorKind::Const => false, CtorKind::Fn => { - !self.r.field_def_ids(def_id).is_some_and(|field_ids| field_ids.is_empty()) + !self.r.field_idents(def_id).is_some_and(|field_ids| field_ids.is_empty()) } } }; diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 38963ef4ef0..7cb5a3fb2fd 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -991,7 +991,7 @@ pub struct Resolver<'a, 'tcx> { extern_prelude: FxHashMap<Ident, ExternPreludeEntry<'a>>, /// N.B., this is used only for better diagnostics, not name resolution itself. - field_def_ids: LocalDefIdMap<&'tcx [DefId]>, + field_names: LocalDefIdMap<Vec<Ident>>, /// Span of the privacy modifier in fields of an item `DefId` accessible with dot syntax. /// Used for hints during error reporting. @@ -1139,7 +1139,7 @@ pub struct Resolver<'a, 'tcx> { /// When collecting definitions from an AST fragment produced by a macro invocation `ExpnId` /// we know what parent node that fragment should be attached to thanks to this table, /// and how the `impl Trait` fragments were introduced. - invocation_parents: FxHashMap<LocalExpnId, (LocalDefId, ImplTraitContext)>, + invocation_parents: FxHashMap<LocalExpnId, (LocalDefId, ImplTraitContext, bool /*in_attr*/)>, /// Some way to know that we are in a *trait* impl in `visit_assoc_item`. /// FIXME: Replace with a more general AST map (together with some other fields). @@ -1371,7 +1371,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { node_id_to_def_id.insert(CRATE_NODE_ID, crate_feed); let mut invocation_parents = FxHashMap::default(); - invocation_parents.insert(LocalExpnId::ROOT, (CRATE_DEF_ID, ImplTraitContext::Existential)); + invocation_parents + .insert(LocalExpnId::ROOT, (CRATE_DEF_ID, ImplTraitContext::Existential, false)); let mut extern_prelude: FxHashMap<Ident, ExternPreludeEntry<'_>> = tcx .sess @@ -1406,7 +1407,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { prelude: None, extern_prelude, - field_def_ids: Default::default(), + field_names: Default::default(), field_visibility_spans: FxHashMap::default(), determined_imports: Vec::new(), @@ -2127,10 +2128,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } } - fn field_def_ids(&self, def_id: DefId) -> Option<&'tcx [DefId]> { + fn field_idents(&self, def_id: DefId) -> Option<Vec<Ident>> { match def_id.as_local() { - Some(def_id) => self.field_def_ids.get(&def_id).copied(), - None => Some(self.tcx.associated_item_def_ids(def_id)), + Some(def_id) => self.field_names.get(&def_id).cloned(), + None => Some( + self.tcx + .associated_item_def_ids(def_id) + .iter() + .map(|&def_id| { + Ident::new(self.tcx.item_name(def_id), self.tcx.def_span(def_id)) + }) + .collect(), + ), } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 026a2ca1412..cb9bebd33d3 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -297,11 +297,12 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> { .invocation_parents .get(&invoc_id) .or_else(|| self.invocation_parents.get(&eager_expansion_root)) - .map(|&(mod_def_id, _)| mod_def_id) - .filter(|&mod_def_id| { - invoc.fragment_kind == AstFragmentKind::Expr + .filter(|&&(mod_def_id, _, in_attr)| { + in_attr + && invoc.fragment_kind == AstFragmentKind::Expr && self.tcx.def_kind(mod_def_id) == DefKind::Mod - }); + }) + .map(|&(mod_def_id, ..)| mod_def_id); let (ext, res) = self.smart_resolve_macro_path( path, kind, diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 9d5a14b5145..8bfaa112c2f 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -644,6 +644,7 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> { target: target.map(|t| t.as_usize()), unwind: unwind.stable(tables), }, + mir::TerminatorKind::TailCall { func: _, args: _, fn_span: _ } => todo!(), mir::TerminatorKind::Assert { cond, expected, msg, target, unwind } => { TerminatorKind::Assert { cond: cond.stable(tables), diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index c95649e2ffb..ad087620ae0 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -1,3 +1,4 @@ +use crate::infer::at::ToTrace; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::{self, Obligation, ObligationCause, ObligationCtxt, SelectionContext}; @@ -17,6 +18,16 @@ pub use rustc_infer::infer::*; #[extension(pub trait InferCtxtExt<'tcx>)] impl<'tcx> InferCtxt<'tcx> { + fn can_eq<T: ToTrace<'tcx>>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool { + self.probe(|_| { + let ocx = ObligationCtxt::new(self); + let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, a, b) else { + return false; + }; + ocx.select_where_possible().is_empty() + }) + } + fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { let ty = self.resolve_vars_if_possible(ty); diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 50c618bb3bd..37008baca28 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -26,6 +26,7 @@ #![feature(never_type)] #![feature(rustdoc_internals)] #![feature(type_alias_impl_trait)] +#![feature(unwrap_infallible)] #![recursion_limit = "512"] // For rustdoc // tidy-alphabetical-end diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index f98744e906f..f67518a577e 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -8,14 +8,12 @@ use rustc_infer::infer::canonical::{ }; use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::solve::Goal; -use rustc_infer::traits::util::supertraits; use rustc_infer::traits::{ObligationCause, Reveal}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt as _}; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; use rustc_type_ir::solve::{Certainty, NoSolution, SolverMode}; -use crate::traits::coherence::trait_ref_is_knowable; use crate::traits::specialization_graph; #[repr(transparent)] @@ -82,13 +80,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution) } - fn elaborate_supertraits( - interner: TyCtxt<'tcx>, - trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>, - ) -> impl Iterator<Item = ty::Binder<'tcx, ty::TraitRef<'tcx>>> { - supertraits(interner, trait_ref) - } - fn try_const_eval_resolve( &self, param_env: ty::ParamEnv<'tcx>, @@ -200,15 +191,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< let _ = self.take_opaque_types(); } - fn trait_ref_is_knowable<E: std::fmt::Debug>( - &self, - trait_ref: ty::TraitRef<'tcx>, - lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>, - ) -> Result<bool, E> { - trait_ref_is_knowable(&self.0, trait_ref, lazily_normalize_ty) - .map(|is_knowable| is_knowable.is_ok()) - } - fn fetch_eligible_assoc_item( &self, param_env: ty::ParamEnv<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index 9f0d84e7d45..fc390bf318d 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -25,42 +25,14 @@ use rustc_middle::traits::specialization_graph::OverlapMode; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use rustc_middle::ty::{self, Ty, TyCtxt}; +pub use rustc_next_trait_solver::coherence::*; use rustc_span::symbol::sym; use rustc_span::{Span, DUMMY_SP}; use std::fmt::Debug; -use std::ops::ControlFlow; use super::error_reporting::suggest_new_overflow_limit; use super::ObligationCtxt; -/// Whether we do the orphan check relative to this crate or to some remote crate. -#[derive(Copy, Clone, Debug)] -pub enum InCrate { - Local { mode: OrphanCheckMode }, - Remote, -} - -#[derive(Copy, Clone, Debug)] -pub enum OrphanCheckMode { - /// Proper orphan check. - Proper, - /// Improper orphan check for backward compatibility. - /// - /// In this mode, type params inside projections are considered to be covered - /// even if the projection may normalize to a type that doesn't actually cover - /// them. This is unsound. See also [#124559] and [#99554]. - /// - /// [#124559]: https://github.com/rust-lang/rust/issues/124559 - /// [#99554]: https://github.com/rust-lang/rust/issues/99554 - Compat, -} - -#[derive(Debug, Copy, Clone)] -pub enum Conflict { - Upstream, - Downstream, -} - pub struct OverlapResult<'tcx> { pub impl_header: ty::ImplHeader<'tcx>, pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>>, @@ -612,426 +584,6 @@ fn try_prove_negated_where_clause<'tcx>( true } -/// Returns whether all impls which would apply to the `trait_ref` -/// e.g. `Ty: Trait<Arg>` are already known in the local crate. -/// -/// This both checks whether any downstream or sibling crates could -/// implement it and whether an upstream crate can add this impl -/// without breaking backwards compatibility. -#[instrument(level = "debug", skip(infcx, lazily_normalize_ty), ret)] -pub fn trait_ref_is_knowable<'tcx, E: Debug>( - infcx: &InferCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - mut lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>, -) -> Result<Result<(), Conflict>, E> { - if orphan_check_trait_ref(infcx, trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok() - { - // A downstream or cousin crate is allowed to implement some - // generic parameters of this trait-ref. - return Ok(Err(Conflict::Downstream)); - } - - if trait_ref_is_local_or_fundamental(infcx.tcx, trait_ref) { - // This is a local or fundamental trait, so future-compatibility - // is no concern. We know that downstream/cousin crates are not - // allowed to implement a generic parameter of this trait ref, - // which means impls could only come from dependencies of this - // crate, which we already know about. - return Ok(Ok(())); - } - - // This is a remote non-fundamental trait, so if another crate - // can be the "final owner" of the generic parameters of this trait-ref, - // they are allowed to implement it future-compatibly. - // - // However, if we are a final owner, then nobody else can be, - // and if we are an intermediate owner, then we don't care - // about future-compatibility, which means that we're OK if - // we are an owner. - if orphan_check_trait_ref( - infcx, - trait_ref, - InCrate::Local { mode: OrphanCheckMode::Proper }, - &mut lazily_normalize_ty, - )? - .is_ok() - { - Ok(Ok(())) - } else { - Ok(Err(Conflict::Upstream)) - } -} - -pub fn trait_ref_is_local_or_fundamental<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, -) -> bool { - trait_ref.def_id.is_local() || tcx.has_attr(trait_ref.def_id, sym::fundamental) -} - -#[derive(Debug, Copy, Clone)] -pub enum IsFirstInputType { - No, - Yes, -} - -impl From<bool> for IsFirstInputType { - fn from(b: bool) -> IsFirstInputType { - match b { - false => IsFirstInputType::No, - true => IsFirstInputType::Yes, - } - } -} - -#[derive(Debug)] -pub enum OrphanCheckErr<'tcx, T> { - NonLocalInputType(Vec<(Ty<'tcx>, IsFirstInputType)>), - UncoveredTyParams(UncoveredTyParams<'tcx, T>), -} - -#[derive(Debug)] -pub struct UncoveredTyParams<'tcx, T> { - pub uncovered: T, - pub local_ty: Option<Ty<'tcx>>, -} - -/// Checks whether a trait-ref is potentially implementable by a crate. -/// -/// The current rule is that a trait-ref orphan checks in a crate C: -/// -/// 1. Order the parameters in the trait-ref in generic parameters order -/// - Self first, others linearly (e.g., `<U as Foo<V, W>>` is U < V < W). -/// 2. Of these type parameters, there is at least one type parameter -/// in which, walking the type as a tree, you can reach a type local -/// to C where all types in-between are fundamental types. Call the -/// first such parameter the "local key parameter". -/// - e.g., `Box<LocalType>` is OK, because you can visit LocalType -/// going through `Box`, which is fundamental. -/// - similarly, `FundamentalPair<Vec<()>, Box<LocalType>>` is OK for -/// the same reason. -/// - but (knowing that `Vec<T>` is non-fundamental, and assuming it's -/// not local), `Vec<LocalType>` is bad, because `Vec<->` is between -/// the local type and the type parameter. -/// 3. Before this local type, no generic type parameter of the impl must -/// be reachable through fundamental types. -/// - e.g. `impl<T> Trait<LocalType> for Vec<T>` is fine, as `Vec` is not fundamental. -/// - while `impl<T> Trait<LocalType> for Box<T>` results in an error, as `T` is -/// reachable through the fundamental type `Box`. -/// 4. Every type in the local key parameter not known in C, going -/// through the parameter's type tree, must appear only as a subtree of -/// a type local to C, with only fundamental types between the type -/// local to C and the local key parameter. -/// - e.g., `Vec<LocalType<T>>>` (or equivalently `Box<Vec<LocalType<T>>>`) -/// is bad, because the only local type with `T` as a subtree is -/// `LocalType<T>`, and `Vec<->` is between it and the type parameter. -/// - similarly, `FundamentalPair<LocalType<T>, T>` is bad, because -/// the second occurrence of `T` is not a subtree of *any* local type. -/// - however, `LocalType<Vec<T>>` is OK, because `T` is a subtree of -/// `LocalType<Vec<T>>`, which is local and has no types between it and -/// the type parameter. -/// -/// The orphan rules actually serve several different purposes: -/// -/// 1. They enable link-safety - i.e., 2 mutually-unknowing crates (where -/// every type local to one crate is unknown in the other) can't implement -/// the same trait-ref. This follows because it can be seen that no such -/// type can orphan-check in 2 such crates. -/// -/// To check that a local impl follows the orphan rules, we check it in -/// InCrate::Local mode, using type parameters for the "generic" types. -/// -/// In InCrate::Local mode the orphan check succeeds if the current crate -/// is definitely allowed to implement the given trait (no false positives). -/// -/// 2. They ground negative reasoning for coherence. If a user wants to -/// write both a conditional blanket impl and a specific impl, we need to -/// make sure they do not overlap. For example, if we write -/// ```ignore (illustrative) -/// impl<T> IntoIterator for Vec<T> -/// impl<T: Iterator> IntoIterator for T -/// ``` -/// We need to be able to prove that `Vec<$0>: !Iterator` for every type $0. -/// We can observe that this holds in the current crate, but we need to make -/// sure this will also hold in all unknown crates (both "independent" crates, -/// which we need for link-safety, and also child crates, because we don't want -/// child crates to get error for impl conflicts in a *dependency*). -/// -/// For that, we only allow negative reasoning if, for every assignment to the -/// inference variables, every unknown crate would get an orphan error if they -/// try to implement this trait-ref. To check for this, we use InCrate::Remote -/// mode. That is sound because we already know all the impls from known crates. -/// -/// In InCrate::Remote mode the orphan check succeeds if a foreign crate -/// *could* implement the given trait (no false negatives). -/// -/// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can -/// add "non-blanket" impls without breaking negative reasoning in dependent -/// crates. This is the "rebalancing coherence" (RFC 1023) restriction. -/// -/// For that, we only allow a crate to perform negative reasoning on -/// non-local-non-`#[fundamental]` if there's a local key parameter as per (2). -/// -/// Because we never perform negative reasoning generically (coherence does -/// not involve type parameters), this can be interpreted as doing the full -/// orphan check (using InCrate::Local mode), instantiating non-local known -/// types for all inference variables. -/// -/// This allows for crates to future-compatibly add impls as long as they -/// can't apply to types with a key parameter in a child crate - applying -/// the rules, this basically means that every type parameter in the impl -/// must appear behind a non-fundamental type (because this is not a -/// type-system requirement, crate owners might also go for "semantic -/// future-compatibility" involving things such as sealed traits, but -/// the above requirement is sufficient, and is necessary in "open world" -/// cases). -/// -/// Note that this function is never called for types that have both type -/// parameters and inference variables. -#[instrument(level = "trace", skip(infcx, lazily_normalize_ty), ret)] -pub fn orphan_check_trait_ref<'tcx, E: Debug>( - infcx: &InferCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - in_crate: InCrate, - lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>, -) -> Result<Result<(), OrphanCheckErr<'tcx, Ty<'tcx>>>, E> { - if trait_ref.has_param() { - bug!("orphan check only expects inference variables: {trait_ref:?}"); - } - - let mut checker = OrphanChecker::new(infcx, in_crate, lazily_normalize_ty); - Ok(match trait_ref.visit_with(&mut checker) { - ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)), - ControlFlow::Break(residual) => match residual { - OrphanCheckEarlyExit::NormalizationFailure(err) => return Err(err), - OrphanCheckEarlyExit::UncoveredTyParam(ty) => { - // Does there exist some local type after the `ParamTy`. - checker.search_first_local_ty = true; - let local_ty = match trait_ref.visit_with(&mut checker).break_value() { - Some(OrphanCheckEarlyExit::LocalTy(local_ty)) => Some(local_ty), - _ => None, - }; - Err(OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { - uncovered: ty, - local_ty, - })) - } - OrphanCheckEarlyExit::LocalTy(_) => Ok(()), - }, - }) -} - -struct OrphanChecker<'a, 'tcx, F> { - infcx: &'a InferCtxt<'tcx>, - in_crate: InCrate, - in_self_ty: bool, - lazily_normalize_ty: F, - /// Ignore orphan check failures and exclusively search for the first local type. - search_first_local_ty: bool, - non_local_tys: Vec<(Ty<'tcx>, IsFirstInputType)>, -} - -impl<'a, 'tcx, F, E> OrphanChecker<'a, 'tcx, F> -where - F: FnOnce(Ty<'tcx>) -> Result<Ty<'tcx>, E>, -{ - fn new(infcx: &'a InferCtxt<'tcx>, in_crate: InCrate, lazily_normalize_ty: F) -> Self { - OrphanChecker { - infcx, - in_crate, - in_self_ty: true, - lazily_normalize_ty, - search_first_local_ty: false, - non_local_tys: Vec::new(), - } - } - - fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> { - self.non_local_tys.push((t, self.in_self_ty.into())); - ControlFlow::Continue(()) - } - - fn found_uncovered_ty_param( - &mut self, - ty: Ty<'tcx>, - ) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> { - if self.search_first_local_ty { - return ControlFlow::Continue(()); - } - - ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty)) - } - - fn def_id_is_local(&mut self, def_id: DefId) -> bool { - match self.in_crate { - InCrate::Local { .. } => def_id.is_local(), - InCrate::Remote => false, - } - } -} - -enum OrphanCheckEarlyExit<'tcx, E> { - NormalizationFailure(E), - UncoveredTyParam(Ty<'tcx>), - LocalTy(Ty<'tcx>), -} - -impl<'a, 'tcx, F, E> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'a, 'tcx, F> -where - F: FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>, -{ - type Result = ControlFlow<OrphanCheckEarlyExit<'tcx, E>>; - - fn visit_region(&mut self, _r: ty::Region<'tcx>) -> Self::Result { - ControlFlow::Continue(()) - } - - fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - let ty = self.infcx.shallow_resolve(ty); - let ty = match (self.lazily_normalize_ty)(ty) { - Ok(norm_ty) if norm_ty.is_ty_var() => ty, - Ok(norm_ty) => norm_ty, - Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)), - }; - - let result = match *ty.kind() { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Str - | ty::FnDef(..) - | ty::Pat(..) - | ty::FnPtr(_) - | ty::Array(..) - | ty::Slice(..) - | ty::RawPtr(..) - | ty::Never - | ty::Tuple(..) => self.found_non_local_ty(ty), - - ty::Param(..) => bug!("unexpected ty param"), - - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => { - match self.in_crate { - InCrate::Local { .. } => self.found_uncovered_ty_param(ty), - // The inference variable might be unified with a local - // type in that remote crate. - InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), - } - } - - // A rigid alias may normalize to anything. - // * If it references an infer var, placeholder or bound ty, it may - // normalize to that, so we have to treat it as an uncovered ty param. - // * Otherwise it may normalize to any non-type-generic type - // be it local or non-local. - ty::Alias(kind, _) => { - if ty.has_type_flags( - ty::TypeFlags::HAS_TY_PLACEHOLDER - | ty::TypeFlags::HAS_TY_BOUND - | ty::TypeFlags::HAS_TY_INFER, - ) { - match self.in_crate { - InCrate::Local { mode } => match kind { - ty::Projection if let OrphanCheckMode::Compat = mode => { - ControlFlow::Continue(()) - } - _ => self.found_uncovered_ty_param(ty), - }, - InCrate::Remote => { - // The inference variable might be unified with a local - // type in that remote crate. - ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) - } - } - } else { - // Regarding *opaque types* specifically, we choose to treat them as non-local, - // even those that appear within the same crate. This seems somewhat surprising - // at first, but makes sense when you consider that opaque types are supposed - // to hide the underlying type *within the same crate*. When an opaque type is - // used from outside the module where it is declared, it should be impossible to - // observe anything about it other than the traits that it implements. - // - // The alternative would be to look at the underlying type to determine whether - // or not the opaque type itself should be considered local. - // - // However, this could make it a breaking change to switch the underlying hidden - // type from a local type to a remote type. This would violate the rule that - // opaque types should be completely opaque apart from the traits that they - // implement, so we don't use this behavior. - // Addendum: Moreover, revealing the underlying type is likely to cause cycle - // errors as we rely on coherence / the specialization graph during typeck. - - self.found_non_local_ty(ty) - } - } - - // For fundamental types, we just look inside of them. - ty::Ref(_, ty, _) => ty.visit_with(self), - ty::Adt(def, args) => { - if self.def_id_is_local(def.did()) { - ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) - } else if def.is_fundamental() { - args.visit_with(self) - } else { - self.found_non_local_ty(ty) - } - } - ty::Foreign(def_id) => { - if self.def_id_is_local(def_id) { - ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) - } else { - self.found_non_local_ty(ty) - } - } - ty::Dynamic(tt, ..) => { - let principal = tt.principal().map(|p| p.def_id()); - if principal.is_some_and(|p| self.def_id_is_local(p)) { - ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) - } else { - self.found_non_local_ty(ty) - } - } - ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), - ty::Closure(did, ..) | ty::CoroutineClosure(did, ..) | ty::Coroutine(did, ..) => { - if self.def_id_is_local(did) { - ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)) - } else { - self.found_non_local_ty(ty) - } - } - // This should only be created when checking whether we have to check whether some - // auto trait impl applies. There will never be multiple impls, so we can just - // act as if it were a local type here. - ty::CoroutineWitness(..) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)), - }; - // A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so - // the first type we visit is always the self type. - self.in_self_ty = false; - result - } - - /// All possible values for a constant parameter already exist - /// in the crate defining the trait, so they are always non-local[^1]. - /// - /// Because there's no way to have an impl where the first local - /// generic argument is a constant, we also don't have to fail - /// the orphan check when encountering a parameter or a generic constant. - /// - /// This means that we can completely ignore constants during the orphan check. - /// - /// See `tests/ui/coherence/const-generics-orphan-check-ok.rs` for examples. - /// - /// [^1]: This might not hold for function pointers or trait objects in the future. - /// As these should be quite rare as const arguments and especially rare as impl - /// parameters, allowing uncovered const parameters in impls seems more useful - /// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile. - fn visit_const(&mut self, _c: ty::Const<'tcx>) -> Self::Result { - ControlFlow::Continue(()) - } -} - /// Compute the `intercrate_ambiguity_causes` for the new solver using /// "proof trees". /// diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 32c8a454b40..94b28426f43 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -1,5 +1,10 @@ use super::{ObligationCauseCode, PredicateObligation}; +use crate::errors::{ + EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented, +}; use crate::infer::error_reporting::TypeErrCtxt; +use crate::infer::InferCtxtExt; +use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt; use rustc_ast::AttrArgs; use rustc_ast::AttrArgsEq; use rustc_ast::AttrKind; @@ -21,12 +26,6 @@ use rustc_span::Span; use std::iter; use std::path::PathBuf; -use crate::errors::{ - EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented, -}; - -use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt; - /// The symbols which are always allowed in a format string static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[ kw::SelfUpper, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index a0a8e5963f1..52edffce614 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -3472,6 +3472,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) }); } + // Suppress `compare_type_predicate_entailment` errors for RPITITs, since they + // should be implied by the parent method. + ObligationCauseCode::CompareImplItem { trait_item_def_id, .. } + if tcx.is_impl_trait_in_trait(trait_item_def_id) => {} ObligationCauseCode::CompareImplItem { trait_item_def_id, kind, .. } => { let item_name = tcx.item_name(trait_item_def_id); let msg = format!( diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 17b6dd2bc58..5461f9e65af 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -1073,7 +1073,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // mismatched, then we have a totally different error to report. if self.enter_forall(found_args, |found_args| { self.enter_forall(expected_args, |expected_args| { - !self.can_sub(obligation.param_env, expected_args, found_args) + !self.can_eq(obligation.param_env, expected_args, found_args) }) }) { return None; diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 5b2c8fb1950..1c6993bdd37 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -203,7 +203,7 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span tcx.associated_items(trait_def_id) .in_definition_order() .filter(|item| item.kind == ty::AssocKind::Type) - .flat_map(|item| tcx.explicit_item_bounds(item.def_id).instantiate_identity_iter_copied()) + .flat_map(|item| tcx.explicit_item_bounds(item.def_id).iter_identity_copied()) .filter_map(|c| predicate_references_self(tcx, c)) .collect() } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 68cc04bc8e6..cc082ad98aa 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -18,7 +18,7 @@ use super::{ TraitQueryMode, }; -use crate::infer::{InferCtxt, InferOk, TypeFreshener}; +use crate::infer::{InferCtxt, InferCtxtExt, InferOk, TypeFreshener}; use crate::solve::InferCtxtSelectExt as _; use crate::traits::error_reporting::TypeErrCtxtExt; use crate::traits::normalize::normalize_with_depth; @@ -1523,7 +1523,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // bound regions. let trait_ref = predicate.skip_binder().trait_ref; - coherence::trait_ref_is_knowable::<!>(self.infcx, trait_ref, |ty| Ok(ty)).unwrap() + coherence::trait_ref_is_knowable(self.infcx, trait_ref, |ty| Ok::<_, !>(ty)).into_ok() } /// Returns `true` if the global caches can be used. diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs index 686f2f04ad9..5e91320f897 100644 --- a/compiler/rustc_ty_utils/src/opaque_types.rs +++ b/compiler/rustc_ty_utils/src/opaque_types.rs @@ -169,10 +169,8 @@ impl<'tcx> OpaqueTypeCollector<'tcx> { // Collect opaque types nested within the associated type bounds of this opaque type. // We use identity args here, because we already know that the opaque type uses // only generic parameters, and thus instantiating would not give us more information. - for (pred, span) in self - .tcx - .explicit_item_bounds(alias_ty.def_id) - .instantiate_identity_iter_copied() + for (pred, span) in + self.tcx.explicit_item_bounds(alias_ty.def_id).iter_identity_copied() { trace!(?pred); self.visit_spanned(span, pred); diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs index 97a1b94263e..eb6cb369974 100644 --- a/compiler/rustc_ty_utils/src/sig_types.rs +++ b/compiler/rustc_ty_utils/src/sig_types.rs @@ -62,7 +62,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>( } } DefKind::OpaqueTy => { - for (pred, span) in tcx.explicit_item_bounds(item).instantiate_identity_iter_copied() { + for (pred, span) in tcx.explicit_item_bounds(item).iter_identity_copied() { try_visit!(visitor.visit(span, pred)); } } diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index 491ef34430c..2531219baec 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -448,7 +448,7 @@ where /// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity), /// but on an iterator of `TypeFoldable` values. - pub fn instantiate_identity_iter(self) -> Iter::IntoIter { + pub fn iter_identity(self) -> Iter::IntoIter { self.value.into_iter() } } @@ -515,9 +515,7 @@ where /// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity), /// but on an iterator of values that deref to a `TypeFoldable`. - pub fn instantiate_identity_iter_copied( - self, - ) -> impl Iterator<Item = <Iter::Item as Deref>::Target> { + pub fn iter_identity_copied(self) -> impl Iterator<Item = <Iter::Item as Deref>::Target> { self.value.into_iter().map(|v| *v) } } diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs new file mode 100644 index 00000000000..0def7d12f74 --- /dev/null +++ b/compiler/rustc_type_ir/src/elaborate.rs @@ -0,0 +1,305 @@ +use std::marker::PhantomData; + +use smallvec::smallvec; + +use crate::data_structures::HashSet; +use crate::outlives::{push_outlives_components, Component}; +use crate::{self as ty, Interner}; +use crate::{inherent::*, Upcast as _}; + +/// "Elaboration" is the process of identifying all the predicates that +/// are implied by a source predicate. Currently, this basically means +/// walking the "supertraits" and other similar assumptions. For example, +/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd` +/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that +/// `T: Foo`, then we know that `T: 'static`. +pub struct Elaborator<I: Interner, O> { + cx: I, + stack: Vec<O>, + visited: HashSet<ty::Binder<I, ty::PredicateKind<I>>>, + mode: Filter, +} + +enum Filter { + All, + OnlySelf, +} + +/// Describes how to elaborate an obligation into a sub-obligation. +pub trait Elaboratable<I: Interner> { + fn predicate(&self) -> I::Predicate; + + // Makes a new `Self` but with a different clause that comes from elaboration. + fn child(&self, clause: I::Clause) -> Self; + + // Makes a new `Self` but with a different clause and a different cause + // code (if `Self` has one, such as [`PredicateObligation`]). + fn child_with_derived_cause( + &self, + clause: I::Clause, + span: I::Span, + parent_trait_pred: ty::Binder<I, ty::TraitPredicate<I>>, + index: usize, + ) -> Self; +} + +pub fn elaborate<I: Interner, O: Elaboratable<I>>( + cx: I, + obligations: impl IntoIterator<Item = O>, +) -> Elaborator<I, O> { + let mut elaborator = + Elaborator { cx, stack: Vec::new(), visited: HashSet::default(), mode: Filter::All }; + elaborator.extend_deduped(obligations); + elaborator +} + +impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> { + fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = O>) { + // Only keep those bounds that we haven't already seen. + // This is necessary to prevent infinite recursion in some + // cases. One common case is when people define + // `trait Sized: Sized { }` rather than `trait Sized { }`. + self.stack.extend( + obligations.into_iter().filter(|o| { + self.visited.insert(self.cx.anonymize_bound_vars(o.predicate().kind())) + }), + ); + } + + /// Filter to only the supertraits of trait predicates, i.e. only the predicates + /// that have `Self` as their self type, instead of all implied predicates. + pub fn filter_only_self(mut self) -> Self { + self.mode = Filter::OnlySelf; + self + } + + fn elaborate(&mut self, elaboratable: &O) { + let cx = self.cx; + + // We only elaborate clauses. + let Some(clause) = elaboratable.predicate().as_clause() else { + return; + }; + + let bound_clause = clause.kind(); + match bound_clause.skip_binder() { + ty::ClauseKind::Trait(data) => { + // Negative trait bounds do not imply any supertrait bounds + if data.polarity != ty::PredicatePolarity::Positive { + return; + } + + let map_to_child_clause = + |(index, (clause, span)): (usize, (I::Clause, I::Span))| { + elaboratable.child_with_derived_cause( + clause.instantiate_supertrait(cx, bound_clause.rebind(data.trait_ref)), + span, + bound_clause.rebind(data), + index, + ) + }; + + // Get predicates implied by the trait, or only super predicates if we only care about self predicates. + match self.mode { + Filter::All => self.extend_deduped( + cx.explicit_implied_predicates_of(data.def_id()) + .iter_identity() + .enumerate() + .map(map_to_child_clause), + ), + Filter::OnlySelf => self.extend_deduped( + cx.explicit_super_predicates_of(data.def_id()) + .iter_identity() + .enumerate() + .map(map_to_child_clause), + ), + }; + } + ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => { + // We know that `T: 'a` for some type `T`. We can + // often elaborate this. For example, if we know that + // `[U]: 'a`, that implies that `U: 'a`. Similarly, if + // we know `&'a U: 'b`, then we know that `'a: 'b` and + // `U: 'b`. + // + // We can basically ignore bound regions here. So for + // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to + // `'a: 'b`. + + // Ignore `for<'a> T: 'a` -- we might in the future + // consider this as evidence that `T: 'static`, but + // I'm a bit wary of such constructions and so for now + // I want to be conservative. --nmatsakis + if r_min.is_bound() { + return; + } + + let mut components = smallvec![]; + push_outlives_components(cx, ty_max, &mut components); + self.extend_deduped( + components + .into_iter() + .filter_map(|component| elaborate_component_to_clause(cx, component, r_min)) + .map(|clause| elaboratable.child(bound_clause.rebind(clause).upcast(cx))), + ); + } + ty::ClauseKind::RegionOutlives(..) => { + // Nothing to elaborate from `'a: 'b`. + } + ty::ClauseKind::WellFormed(..) => { + // Currently, we do not elaborate WF predicates, + // although we easily could. + } + ty::ClauseKind::Projection(..) => { + // Nothing to elaborate in a projection predicate. + } + ty::ClauseKind::ConstEvaluatable(..) => { + // Currently, we do not elaborate const-evaluatable + // predicates. + } + ty::ClauseKind::ConstArgHasType(..) => { + // Nothing to elaborate + } + } + } +} + +fn elaborate_component_to_clause<I: Interner>( + cx: I, + component: Component<I>, + outlives_region: I::Region, +) -> Option<ty::ClauseKind<I>> { + match component { + Component::Region(r) => { + if r.is_bound() { + None + } else { + Some(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r, outlives_region))) + } + } + + Component::Param(p) => { + let ty = Ty::new_param(cx, p); + Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, outlives_region))) + } + + Component::Placeholder(p) => { + let ty = Ty::new_placeholder(cx, p); + Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, outlives_region))) + } + + Component::UnresolvedInferenceVariable(_) => None, + + Component::Alias(alias_ty) => { + // We might end up here if we have `Foo<<Bar as Baz>::Assoc>: 'a`. + // With this, we can deduce that `<Bar as Baz>::Assoc: 'a`. + Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate( + alias_ty.to_ty(cx), + outlives_region, + ))) + } + + Component::EscapingAlias(_) => { + // We might be able to do more here, but we don't + // want to deal with escaping vars right now. + None + } + } +} + +impl<I: Interner, O: Elaboratable<I>> Iterator for Elaborator<I, O> { + type Item = O; + + fn size_hint(&self) -> (usize, Option<usize>) { + (self.stack.len(), None) + } + + fn next(&mut self) -> Option<Self::Item> { + // Extract next item from top-most stack frame, if any. + if let Some(obligation) = self.stack.pop() { + self.elaborate(&obligation); + Some(obligation) + } else { + None + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Supertrait iterator +/////////////////////////////////////////////////////////////////////////// + +/// Computes the def-ids of the transitive supertraits of `trait_def_id`. This (intentionally) +/// does not compute the full elaborated super-predicates but just the set of def-ids. It is used +/// to identify which traits may define a given associated type to help avoid cycle errors, +/// and to make size estimates for vtable layout computation. +pub fn supertrait_def_ids<I: Interner>( + cx: I, + trait_def_id: I::DefId, +) -> impl Iterator<Item = I::DefId> { + let mut set = HashSet::default(); + let mut stack = vec![trait_def_id]; + + set.insert(trait_def_id); + + std::iter::from_fn(move || { + let trait_def_id = stack.pop()?; + + for (predicate, _) in cx.explicit_super_predicates_of(trait_def_id).iter_identity() { + if let ty::ClauseKind::Trait(data) = predicate.kind().skip_binder() { + if set.insert(data.def_id()) { + stack.push(data.def_id()); + } + } + } + + Some(trait_def_id) + }) +} + +pub fn supertraits<I: Interner>( + tcx: I, + trait_ref: ty::Binder<I, ty::TraitRef<I>>, +) -> FilterToTraits<I, Elaborator<I, I::Clause>> { + elaborate(tcx, [trait_ref.upcast(tcx)]).filter_only_self().filter_to_traits() +} + +pub fn transitive_bounds<I: Interner>( + tcx: I, + trait_refs: impl Iterator<Item = ty::Binder<I, ty::TraitRef<I>>>, +) -> FilterToTraits<I, Elaborator<I, I::Clause>> { + elaborate(tcx, trait_refs.map(|trait_ref| trait_ref.upcast(tcx))) + .filter_only_self() + .filter_to_traits() +} + +impl<I: Interner> Elaborator<I, I::Clause> { + fn filter_to_traits(self) -> FilterToTraits<I, Self> { + FilterToTraits { _cx: PhantomData, base_iterator: self } + } +} + +/// A filter around an iterator of predicates that makes it yield up +/// just trait references. +pub struct FilterToTraits<I: Interner, It: Iterator<Item = I::Clause>> { + _cx: PhantomData<I>, + base_iterator: It, +} + +impl<I: Interner, It: Iterator<Item = I::Clause>> Iterator for FilterToTraits<I, It> { + type Item = ty::Binder<I, ty::TraitRef<I>>; + + fn next(&mut self) -> Option<ty::Binder<I, ty::TraitRef<I>>> { + while let Some(pred) = self.base_iterator.next() { + if let Some(data) = pred.as_trait_clause() { + return Some(data.map_bound(|t| t.trait_ref)); + } + } + None + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let (_, upper) = self.base_iterator.size_hint(); + (0, upper) + } +} diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index 2c80ee0a73d..1d0b2345b80 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -3,7 +3,7 @@ use crate::relate::Relate; use crate::solve::{Goal, NoSolution, SolverMode}; use crate::{self as ty, Interner}; -pub trait InferCtxtLike { +pub trait InferCtxtLike: Sized { type Interner: Interner; fn cx(&self) -> Self::Interner; @@ -73,6 +73,11 @@ pub trait InferCtxtLike { rhs: T, ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>; + fn shallow_resolve( + &self, + ty: <Self::Interner as Interner>::Ty, + ) -> <Self::Interner as Interner>::Ty; + fn resolve_vars_if_possible<T>(&self, value: T) -> T where T: TypeFoldable<Self::Interner>; diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 68c2575258d..de86a8536f7 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -9,6 +9,7 @@ use std::hash::Hash; use rustc_ast_ir::Mutability; use crate::data_structures::HashSet; +use crate::elaborate::Elaboratable; use crate::fold::{TypeFoldable, TypeSuperFoldable}; use crate::relate::Relate; use crate::solve::{CacheData, CanonicalInput, QueryResult, Reveal}; @@ -40,6 +41,10 @@ pub trait Ty<I: Interner<Ty = Self>>: fn new_var(interner: I, var: ty::TyVid) -> Self; + fn new_param(interner: I, param: I::ParamTy) -> Self; + + fn new_placeholder(interner: I, param: I::PlaceholderTy) -> Self; + fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundTy) -> Self; fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self; @@ -429,6 +434,8 @@ pub trait Predicate<I: Interner<Predicate = Self>>: + UpcastFrom<I, ty::OutlivesPredicate<I, I::Region>> + IntoKind<Kind = ty::Binder<I, ty::PredicateKind<I>>> { + fn as_clause(self) -> Option<I::Clause>; + fn is_coinductive(self, interner: I) -> bool; // FIXME: Eventually uplift the impl out of rustc and make this defaulted. @@ -441,35 +448,35 @@ pub trait Clause<I: Interner<Clause = Self>>: + Hash + Eq + TypeFoldable<I> - // FIXME: Remove these, uplift the `Upcast` impls. + + UpcastFrom<I, ty::Binder<I, ty::ClauseKind<I>>> + UpcastFrom<I, ty::TraitRef<I>> + UpcastFrom<I, ty::Binder<I, ty::TraitRef<I>>> + UpcastFrom<I, ty::ProjectionPredicate<I>> + UpcastFrom<I, ty::Binder<I, ty::ProjectionPredicate<I>>> + IntoKind<Kind = ty::Binder<I, ty::ClauseKind<I>>> + + Elaboratable<I> { fn as_trait_clause(self) -> Option<ty::Binder<I, ty::TraitPredicate<I>>> { self.kind() - .map_bound(|clause| { - if let ty::ClauseKind::Trait(t) = clause { - Some(t) - } else { - None - } - }) + .map_bound(|clause| if let ty::ClauseKind::Trait(t) = clause { Some(t) } else { None }) .transpose() } + fn as_projection_clause(self) -> Option<ty::Binder<I, ty::ProjectionPredicate<I>>> { self.kind() - .map_bound(|clause| { - if let ty::ClauseKind::Projection(p) = clause { - Some(p) - } else { - None - } - }) + .map_bound( + |clause| { + if let ty::ClauseKind::Projection(p) = clause { Some(p) } else { None } + }, + ) .transpose() } + + /// Performs a instantiation suitable for going from a + /// poly-trait-ref to supertraits that must hold if that + /// poly-trait-ref holds. This is slightly different from a normal + /// instantiation in terms of what happens with bound regions. + fn instantiate_supertrait(self, tcx: I, trait_ref: ty::Binder<I, ty::TraitRef<I>>) -> Self; } /// Common capabilities of placeholder kinds @@ -514,6 +521,8 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq { fn all_field_tys(self, interner: I) -> ty::EarlyBinder<I, impl IntoIterator<Item = I::Ty>>; fn sized_constraint(self, interner: I) -> Option<ty::EarlyBinder<I, I::Ty>>; + + fn is_fundamental(self) -> bool; } pub trait ParamEnv<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> { @@ -558,6 +567,8 @@ pub trait EvaluationCache<I: Interner> { } pub trait DefId<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> { + fn is_local(self) -> bool; + fn as_local(self) -> Option<I::LocalDefId>; } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 6e013768c3e..fdd1553d389 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -32,7 +32,7 @@ pub trait Interner: { type DefId: DefId<Self>; type LocalDefId: Copy + Debug + Hash + Eq + Into<Self::DefId> + TypeFoldable<Self>; - type Span: Copy + Debug + Hash + Eq; + type Span: Copy + Debug + Hash + Eq + TypeFoldable<Self>; type GenericArgs: GenericArgs<Self>; type GenericArgsSlice: Copy + Debug + Hash + Eq + SliceLike<Item = Self::GenericArg>; @@ -213,7 +213,12 @@ pub trait Interner: fn explicit_super_predicates_of( self, def_id: Self::DefId, - ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>; + ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>; + + fn explicit_implied_predicates_of( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>; fn has_target_features(self, def_id: Self::DefId) -> bool; @@ -246,10 +251,9 @@ pub trait Interner: fn trait_is_object_safe(self, trait_def_id: Self::DefId) -> bool; - fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool; + fn trait_is_fundamental(self, def_id: Self::DefId) -> bool; - fn supertrait_def_ids(self, trait_def_id: Self::DefId) - -> impl IntoIterator<Item = Self::DefId>; + fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool; fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed; @@ -268,6 +272,11 @@ pub trait Interner: param_env: Self::ParamEnv, placeholder: Self::PlaceholderConst, ) -> Self::Ty; + + fn anonymize_bound_vars<T: TypeFoldable<Self>>( + self, + binder: ty::Binder<Self, T>, + ) -> ty::Binder<Self, T>; } /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter` diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index 2a909b06baf..b14a65fc779 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -20,6 +20,7 @@ pub mod visit; #[cfg(feature = "nightly")] pub mod codec; pub mod data_structures; +pub mod elaborate; pub mod error; pub mod fast_reject; pub mod fold; diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 1ec095a46f7..65bcb241e4a 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -2374,7 +2374,7 @@ impl dyn Error + Send { let err: Box<dyn Error> = self; <dyn Error>::downcast(err).map_err(|s| unsafe { // Reapply the `Send` marker. - Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send)) + mem::transmute::<Box<dyn Error>, Box<dyn Error + Send>>(s) }) } } @@ -2387,8 +2387,8 @@ impl dyn Error + Send + Sync { pub fn downcast<T: Error + 'static>(self: Box<Self>) -> Result<Box<T>, Box<Self>> { let err: Box<dyn Error> = self; <dyn Error>::downcast(err).map_err(|s| unsafe { - // Reapply the `Send + Sync` marker. - Box::from_raw(Box::into_raw(s) as *mut (dyn Error + Send + Sync)) + // Reapply the `Send + Sync` markers. + mem::transmute::<Box<dyn Error>, Box<dyn Error + Send + Sync>>(s) }) } } diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 56620cf890d..ba1f38dcc3e 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -1796,18 +1796,18 @@ fn test_ord_absence() { } fn map_debug<K: Debug>(mut map: BTreeMap<K, ()>) { - format!("{map:?}"); - format!("{:?}", map.iter()); - format!("{:?}", map.iter_mut()); - format!("{:?}", map.keys()); - format!("{:?}", map.values()); - format!("{:?}", map.values_mut()); + let _ = format!("{map:?}"); + let _ = format!("{:?}", map.iter()); + let _ = format!("{:?}", map.iter_mut()); + let _ = format!("{:?}", map.keys()); + let _ = format!("{:?}", map.values()); + let _ = format!("{:?}", map.values_mut()); if true { - format!("{:?}", map.into_iter()); + let _ = format!("{:?}", map.into_iter()); } else if true { - format!("{:?}", map.into_keys()); + let _ = format!("{:?}", map.into_keys()); } else { - format!("{:?}", map.into_values()); + let _ = format!("{:?}", map.into_values()); } } diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index 688ce57e9da..48bf7674138 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -705,9 +705,9 @@ fn test_ord_absence() { } fn set_debug<K: Debug>(set: BTreeSet<K>) { - format!("{set:?}"); - format!("{:?}", set.iter()); - format!("{:?}", set.into_iter()); + let _ = format!("{set:?}"); + let _ = format!("{:?}", set.iter()); + let _ = format!("{:?}", set.into_iter()); } fn set_clone<K: Clone>(mut set: BTreeSet<K>) { diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 01510a61405..077483a174b 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -1495,6 +1495,14 @@ impl<'a, T, A: Allocator> Cursor<'a, T, A> { pub fn back(&self) -> Option<&'a T> { self.list.back() } + + /// Provides a reference to the cursor's parent list. + #[must_use] + #[inline(always)] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_list(&self) -> &'a LinkedList<T, A> { + self.list + } } impl<'a, T, A: Allocator> CursorMut<'a, T, A> { @@ -1605,6 +1613,18 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> { pub fn as_cursor(&self) -> Cursor<'_, T, A> { Cursor { list: self.list, current: self.current, index: self.index } } + + /// Provides a read-only reference to the cursor's parent list. + /// + /// The lifetime of the returned reference is bound to that of the + /// `CursorMut`, which means it cannot outlive the `CursorMut` and that the + /// `CursorMut` is frozen for the lifetime of the reference. + #[must_use] + #[inline(always)] + #[unstable(feature = "linked_list_cursors", issue = "58533")] + pub fn as_list(&self) -> &LinkedList<T, A> { + self.list + } } // Now the list editing operations diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 61d05afccfd..a07f250d7d8 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -164,6 +164,20 @@ impl<T, A: Allocator> VecDeque<T, A> { self.buf.ptr() } + /// Appends an element to the buffer. + /// + /// # Safety + /// + /// May only be called if `deque.len() < deque.capacity()` + #[inline] + unsafe fn push_unchecked(&mut self, element: T) { + // SAFETY: Because of the precondition, it's guaranteed that there is space + // in the logical array after the last element. + unsafe { self.buffer_write(self.to_physical_idx(self.len), element) }; + // This can't overflow because `deque.len() < deque.capacity() <= usize::MAX`. + self.len += 1; + } + /// Moves an element out of the buffer #[inline] unsafe fn buffer_read(&mut self, off: usize) -> T { @@ -2911,6 +2925,14 @@ impl<T, A: Allocator> Extend<T> for VecDeque<T, A> { fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } + + #[inline] + unsafe fn extend_one_unchecked(&mut self, item: T) { + // SAFETY: Our preconditions ensure the space has been reserved, and `extend_reserve` is implemented correctly. + unsafe { + self.push_unchecked(item); + } + } } #[stable(feature = "extend_ref", since = "1.2.0")] @@ -2928,6 +2950,14 @@ impl<'a, T: 'a + Copy, A: Allocator> Extend<&'a T> for VecDeque<T, A> { fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } + + #[inline] + unsafe fn extend_one_unchecked(&mut self, &item: &'a T) { + // SAFETY: Our preconditions ensure the space has been reserved, and `extend_reserve` is implemented correctly. + unsafe { + self.push_unchecked(item); + } + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs index dccf40ccb38..6a89abc3ef9 100644 --- a/library/alloc/src/collections/vec_deque/spec_extend.rs +++ b/library/alloc/src/collections/vec_deque/spec_extend.rs @@ -21,21 +21,12 @@ where // self.push_back(item); // } - // May only be called if `deque.len() < deque.capacity()` - unsafe fn push_unchecked<T, A: Allocator>(deque: &mut VecDeque<T, A>, element: T) { - // SAFETY: Because of the precondition, it's guaranteed that there is space - // in the logical array after the last element. - unsafe { deque.buffer_write(deque.to_physical_idx(deque.len), element) }; - // This can't overflow because `deque.len() < deque.capacity() <= usize::MAX`. - deque.len += 1; - } - while let Some(element) = iter.next() { let (lower, _) = iter.size_hint(); self.reserve(lower.saturating_add(1)); // SAFETY: We just reserved space for at least one element. - unsafe { push_unchecked(self, element) }; + unsafe { self.push_unchecked(element) }; // Inner loop to avoid repeatedly calling `reserve`. while self.len < self.capacity() { @@ -43,7 +34,7 @@ where return; }; // SAFETY: The loop condition guarantees that `self.len() < self.capacity()`. - unsafe { push_unchecked(self, element) }; + unsafe { self.push_unchecked(element) }; } } } diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs index ae44cab8131..c6bba619ae6 100644 --- a/library/alloc/src/fmt.rs +++ b/library/alloc/src/fmt.rs @@ -12,6 +12,7 @@ //! Some examples of the [`format!`] extension are: //! //! ``` +//! # #![allow(unused_must_use)] //! format!("Hello"); // => "Hello" //! format!("Hello, {}!", "world"); // => "Hello, world!" //! format!("The number is {}", 1); // => "The number is 1" @@ -50,6 +51,7 @@ //! the iterator advances. This leads to behavior like this: //! //! ``` +//! # #![allow(unused_must_use)] //! format!("{1} {} {0} {}", 1, 2); // => "2 1 1 2" //! ``` //! @@ -77,6 +79,7 @@ //! For example, the following [`format!`] expressions all use named arguments: //! //! ``` +//! # #![allow(unused_must_use)] //! format!("{argument}", argument = "test"); // => "test" //! format!("{name} {}", 1, name = 2); // => "2 1" //! format!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" @@ -86,6 +89,7 @@ //! reference a variable with that name in the current scope. //! //! ``` +//! # #![allow(unused_must_use)] //! let argument = 2 + 2; //! format!("{argument}"); // => "4" //! diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 703538d0f35..a7715740cbd 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -124,6 +124,7 @@ #![feature(error_generic_member_access)] #![feature(exact_size_is_empty)] #![feature(extend_one)] +#![feature(extend_one_unchecked)] #![feature(fmt_internals)] #![feature(fn_traits)] #![feature(hasher_prefixfree_extras)] @@ -256,6 +257,7 @@ pub mod vec; #[unstable(feature = "liballoc_internals", issue = "none", reason = "implementation detail")] pub mod __export { pub use core::format_args; + pub use core::hint::must_use; } #[cfg(test)] diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index d5ca5c4ed27..8c6a367869c 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -111,6 +111,7 @@ macro_rules! vec { /// # Examples /// /// ``` +/// # #![allow(unused_must_use)] /// format!("test"); // => "test" /// format!("hello {}", "world!"); // => "hello world!" /// format!("x = {}, y = {val}", 10, val = 30); // => "x = 10, y = 30" @@ -119,10 +120,13 @@ macro_rules! vec { /// ``` #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] +#[allow_internal_unstable(hint_must_use, liballoc_internals)] #[cfg_attr(not(test), rustc_diagnostic_item = "format_macro")] macro_rules! format { - ($($arg:tt)*) => {{ - let res = $crate::fmt::format($crate::__export::format_args!($($arg)*)); - res - }} + ($($arg:tt)*) => { + $crate::__export::must_use({ + let res = $crate::fmt::format($crate::__export::format_args!($($arg)*)); + res + }) + } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 90672164cb9..1983ea8281a 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -197,11 +197,7 @@ macro_rules! acquire { /// /// Sharing some immutable data between threads: /// -// Note that we **do not** run these tests here. The windows builders get super -// unhappy if a thread outlives the main thread and then exits at the same time -// (something deadlocks) so we just avoid this entirely by not running these -// tests. -/// ```no_run +/// ``` /// use std::sync::Arc; /// use std::thread; /// @@ -220,7 +216,7 @@ macro_rules! acquire { /// /// [`AtomicUsize`]: core::sync::atomic::AtomicUsize "sync::atomic::AtomicUsize" /// -/// ```no_run +/// ``` /// use std::sync::Arc; /// use std::sync::atomic::{AtomicUsize, Ordering}; /// use std::thread; diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index f1706e31bb8..6e9b017ad75 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -3048,6 +3048,16 @@ impl<T, A: Allocator> Extend<T> for Vec<T, A> { fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } + + #[inline] + unsafe fn extend_one_unchecked(&mut self, item: T) { + // SAFETY: Our preconditions ensure the space has been reserved, and `extend_reserve` is implemented correctly. + unsafe { + let len = self.len(); + ptr::write(self.as_mut_ptr().add(len), item); + self.set_len(len + 1); + } + } } impl<T, A: Allocator> Vec<T, A> { @@ -3244,6 +3254,16 @@ impl<'a, T: Copy + 'a, A: Allocator> Extend<&'a T> for Vec<T, A> { fn extend_reserve(&mut self, additional: usize) { self.reserve(additional); } + + #[inline] + unsafe fn extend_one_unchecked(&mut self, &item: &'a T) { + // SAFETY: Our preconditions ensure the space has been reserved, and `extend_reserve` is implemented correctly. + unsafe { + let len = self.len(); + ptr::write(self.as_mut_ptr().add(len), item); + self.set_len(len + 1); + } + } } /// Implements comparison of vectors, [lexicographically](Ord#lexicographical-comparison). diff --git a/library/alloc/tests/fmt.rs b/library/alloc/tests/fmt.rs index 379e09ab69a..ce24a40f4c0 100644 --- a/library/alloc/tests/fmt.rs +++ b/library/alloc/tests/fmt.rs @@ -217,19 +217,19 @@ fn test_format_macro_interface() { // make sure that format! doesn't move out of local variables let a = Box::new(3); - format!("{a}"); - format!("{a}"); + let _ = format!("{a}"); + let _ = format!("{a}"); // make sure that format! doesn't cause spurious unused-unsafe warnings when // it's inside of an outer unsafe block unsafe { let a: isize = ::std::mem::transmute(3_usize); - format!("{a}"); + let _ = format!("{a}"); } // test that trailing commas are acceptable - format!("{}", "test",); - format!("{foo}", foo = "test",); + let _ = format!("{}", "test",); + let _ = format!("{foo}", foo = "test",); } // Basic test to make sure that we can invoke the `write!` macro with an diff --git a/library/core/src/any.rs b/library/core/src/any.rs index eab11ae288a..59f3b6841d5 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -673,7 +673,7 @@ impl hash::Hash for TypeId { #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Debug for TypeId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - f.debug_tuple("TypeId").field(&self.as_u128()).finish() + write!(f, "TypeId({:#034x})", self.as_u128()) } } diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 6d1f10f5211..88adc378477 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -10,8 +10,6 @@ #![allow(non_camel_case_types)] use crate::fmt; -use crate::marker::PhantomData; -use crate::ops::{Deref, DerefMut}; #[doc(no_inline)] #[stable(feature = "core_c_str", since = "1.64.0")] @@ -28,6 +26,20 @@ pub use self::c_str::CStr; #[unstable(feature = "c_str_module", issue = "112134")] pub mod c_str; +#[unstable( + feature = "c_variadic", + issue = "44930", + reason = "the `c_variadic` feature has not been properly tested on all supported platforms" +)] +pub use self::va_list::{VaList, VaListImpl}; + +#[unstable( + feature = "c_variadic", + issue = "44930", + reason = "the `c_variadic` feature has not been properly tested on all supported platforms" +)] +pub mod va_list; + macro_rules! type_alias { { $Docfile:tt, $Alias:ident = $Real:ty; @@ -205,404 +217,6 @@ impl fmt::Debug for c_void { } } -/// Basic implementation of a `va_list`. -// The name is WIP, using `VaListImpl` for now. -#[cfg(any( - all( - not(target_arch = "aarch64"), - not(target_arch = "powerpc"), - not(target_arch = "s390x"), - not(target_arch = "x86_64") - ), - all(target_arch = "aarch64", target_vendor = "apple"), - target_family = "wasm", - target_os = "uefi", - windows, -))] -#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - ptr: *mut c_void, - - // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to - // the region of the function it's defined in - _marker: PhantomData<&'f mut &'f c_void>, -} - -#[cfg(any( - all( - not(target_arch = "aarch64"), - not(target_arch = "powerpc"), - not(target_arch = "s390x"), - not(target_arch = "x86_64") - ), - all(target_arch = "aarch64", target_vendor = "apple"), - target_family = "wasm", - target_os = "uefi", - windows, -))] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> fmt::Debug for VaListImpl<'f> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "va_list* {:p}", self.ptr) - } -} - -/// AArch64 ABI implementation of a `va_list`. See the -/// [AArch64 Procedure Call Standard] for more details. -/// -/// [AArch64 Procedure Call Standard]: -/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf -#[cfg(all( - target_arch = "aarch64", - not(target_vendor = "apple"), - not(target_os = "uefi"), - not(windows), -))] -#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - stack: *mut c_void, - gr_top: *mut c_void, - vr_top: *mut c_void, - gr_offs: i32, - vr_offs: i32, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// PowerPC ABI implementation of a `va_list`. -#[cfg(all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)))] -#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - gpr: u8, - fpr: u8, - reserved: u16, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// s390x ABI implementation of a `va_list`. -#[cfg(target_arch = "s390x")] -#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - gpr: i64, - fpr: i64, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// x86_64 ABI implementation of a `va_list`. -#[cfg(all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)))] -#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -#[lang = "va_list"] -pub struct VaListImpl<'f> { - gp_offset: i32, - fp_offset: i32, - overflow_arg_area: *mut c_void, - reg_save_area: *mut c_void, - _marker: PhantomData<&'f mut &'f c_void>, -} - -/// A wrapper for a `va_list` -#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 -#[derive(Debug)] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -pub struct VaList<'a, 'f: 'a> { - #[cfg(any( - all( - not(target_arch = "aarch64"), - not(target_arch = "powerpc"), - not(target_arch = "s390x"), - not(target_arch = "x86_64") - ), - all(target_arch = "aarch64", target_vendor = "apple"), - target_family = "wasm", - target_os = "uefi", - windows, - ))] - inner: VaListImpl<'f>, - - #[cfg(all( - any( - target_arch = "aarch64", - target_arch = "powerpc", - target_arch = "s390x", - target_arch = "x86_64" - ), - any(not(target_arch = "aarch64"), not(target_vendor = "apple")), - not(target_family = "wasm"), - not(target_os = "uefi"), - not(windows), - ))] - inner: &'a mut VaListImpl<'f>, - - _marker: PhantomData<&'a mut VaListImpl<'f>>, -} - -#[cfg(any( - all( - not(target_arch = "aarch64"), - not(target_arch = "powerpc"), - not(target_arch = "s390x"), - not(target_arch = "x86_64") - ), - all(target_arch = "aarch64", target_vendor = "apple"), - target_family = "wasm", - target_os = "uefi", - windows, -))] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> VaListImpl<'f> { - /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: VaListImpl { ..*self }, _marker: PhantomData } - } -} - -#[cfg(all( - any( - target_arch = "aarch64", - target_arch = "powerpc", - target_arch = "s390x", - target_arch = "x86_64" - ), - any(not(target_arch = "aarch64"), not(target_vendor = "apple")), - not(target_family = "wasm"), - not(target_os = "uefi"), - not(windows), -))] -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> VaListImpl<'f> { - /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. - #[inline] - pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { - VaList { inner: self, _marker: PhantomData } - } -} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'a, 'f: 'a> Deref for VaList<'a, 'f> { - type Target = VaListImpl<'f>; - - #[inline] - fn deref(&self) -> &VaListImpl<'f> { - &self.inner - } -} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { - #[inline] - fn deref_mut(&mut self) -> &mut VaListImpl<'f> { - &mut self.inner - } -} - -// The VaArgSafe trait needs to be used in public interfaces, however, the trait -// itself must not be allowed to be used outside this module. Allowing users to -// implement the trait for a new type (thereby allowing the va_arg intrinsic to -// be used on a new type) is likely to cause undefined behavior. -// -// FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface -// but also ensure it cannot be used elsewhere, the trait needs to be public -// within a private module. Once RFC 2145 has been implemented look into -// improving this. -mod sealed_trait { - /// Trait which permits the allowed types to be used with [super::VaListImpl::arg]. - #[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" - )] - pub unsafe trait VaArgSafe {} -} - -macro_rules! impl_va_arg_safe { - ($($t:ty),+) => { - $( - #[unstable(feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930")] - unsafe impl sealed_trait::VaArgSafe for $t {} - )+ - } -} - -impl_va_arg_safe! {i8, i16, i32, i64, usize} -impl_va_arg_safe! {u8, u16, u32, u64, isize} -impl_va_arg_safe! {f64} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -unsafe impl<T> sealed_trait::VaArgSafe for *mut T {} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -unsafe impl<T> sealed_trait::VaArgSafe for *const T {} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> VaListImpl<'f> { - /// Advance to the next arg. - #[inline] - pub unsafe fn arg<T: sealed_trait::VaArgSafe>(&mut self) -> T { - // SAFETY: the caller must uphold the safety contract for `va_arg`. - unsafe { va_arg(self) } - } - - /// Copies the `va_list` at the current location. - pub unsafe fn with_copy<F, R>(&self, f: F) -> R - where - F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R, - { - let mut ap = self.clone(); - let ret = f(ap.as_va_list()); - // SAFETY: the caller must uphold the safety contract for `va_end`. - unsafe { - va_end(&mut ap); - } - ret - } -} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> Clone for VaListImpl<'f> { - #[inline] - fn clone(&self) -> Self { - let mut dest = crate::mem::MaybeUninit::uninit(); - // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal - unsafe { - va_copy(dest.as_mut_ptr(), self); - dest.assume_init() - } - } -} - -#[unstable( - feature = "c_variadic", - reason = "the `c_variadic` feature has not been properly tested on \ - all supported platforms", - issue = "44930" -)] -impl<'f> Drop for VaListImpl<'f> { - fn drop(&mut self) { - // FIXME: this should call `va_end`, but there's no clean way to - // guarantee that `drop` always gets inlined into its caller, - // so the `va_end` would get directly called from the same function as - // the corresponding `va_copy`. `man va_end` states that C requires this, - // and LLVM basically follows the C semantics, so we need to make sure - // that `va_end` is always called from the same function as `va_copy`. - // For more details, see https://github.com/rust-lang/rust/pull/59625 - // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic. - // - // This works for now, since `va_end` is a no-op on all current LLVM targets. - } -} - -extern "rust-intrinsic" { - /// Destroy the arglist `ap` after initialization with `va_start` or - /// `va_copy`. - #[rustc_nounwind] - fn va_end(ap: &mut VaListImpl<'_>); - - /// Copies the current location of arglist `src` to the arglist `dst`. - #[rustc_nounwind] - fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); - - /// Loads an argument of type `T` from the `va_list` `ap` and increment the - /// argument `ap` points to. - #[rustc_nounwind] - fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaListImpl<'_>) -> T; -} - // Link the MSVC default lib #[cfg(all(windows, target_env = "msvc"))] #[link( diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs new file mode 100644 index 00000000000..6a2e8b67d0c --- /dev/null +++ b/library/core/src/ffi/va_list.rs @@ -0,0 +1,301 @@ +//! C's "variable arguments" +//! +//! Better known as "varargs". + +use crate::ffi::c_void; + +#[allow(unused_imports)] +use crate::fmt; +use crate::marker::PhantomData; +use crate::ops::{Deref, DerefMut}; + +/// Basic implementation of a `va_list`. +// The name is WIP, using `VaListImpl` for now. +#[cfg(any( + all( + not(target_arch = "aarch64"), + not(target_arch = "powerpc"), + not(target_arch = "s390x"), + not(target_arch = "x86_64") + ), + all(target_arch = "aarch64", target_vendor = "apple"), + target_family = "wasm", + target_os = "uefi", + windows, +))] +#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 +#[lang = "va_list"] +pub struct VaListImpl<'f> { + ptr: *mut c_void, + + // Invariant over `'f`, so each `VaListImpl<'f>` object is tied to + // the region of the function it's defined in + _marker: PhantomData<&'f mut &'f c_void>, +} + +#[cfg(any( + all( + not(target_arch = "aarch64"), + not(target_arch = "powerpc"), + not(target_arch = "s390x"), + not(target_arch = "x86_64") + ), + all(target_arch = "aarch64", target_vendor = "apple"), + target_family = "wasm", + target_os = "uefi", + windows, +))] +impl<'f> fmt::Debug for VaListImpl<'f> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "va_list* {:p}", self.ptr) + } +} + +/// AArch64 ABI implementation of a `va_list`. See the +/// [AArch64 Procedure Call Standard] for more details. +/// +/// [AArch64 Procedure Call Standard]: +/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf +#[cfg(all( + target_arch = "aarch64", + not(target_vendor = "apple"), + not(target_os = "uefi"), + not(windows), +))] +#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 +#[derive(Debug)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + stack: *mut c_void, + gr_top: *mut c_void, + vr_top: *mut c_void, + gr_offs: i32, + vr_offs: i32, + _marker: PhantomData<&'f mut &'f c_void>, +} + +/// PowerPC ABI implementation of a `va_list`. +#[cfg(all(target_arch = "powerpc", not(target_os = "uefi"), not(windows)))] +#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 +#[derive(Debug)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + gpr: u8, + fpr: u8, + reserved: u16, + overflow_arg_area: *mut c_void, + reg_save_area: *mut c_void, + _marker: PhantomData<&'f mut &'f c_void>, +} + +/// s390x ABI implementation of a `va_list`. +#[cfg(target_arch = "s390x")] +#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 +#[derive(Debug)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + gpr: i64, + fpr: i64, + overflow_arg_area: *mut c_void, + reg_save_area: *mut c_void, + _marker: PhantomData<&'f mut &'f c_void>, +} + +/// x86_64 ABI implementation of a `va_list`. +#[cfg(all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)))] +#[cfg_attr(not(doc), repr(C))] // work around https://github.com/rust-lang/rust/issues/66401 +#[derive(Debug)] +#[lang = "va_list"] +pub struct VaListImpl<'f> { + gp_offset: i32, + fp_offset: i32, + overflow_arg_area: *mut c_void, + reg_save_area: *mut c_void, + _marker: PhantomData<&'f mut &'f c_void>, +} + +/// A wrapper for a `va_list` +#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/90435 +#[derive(Debug)] +pub struct VaList<'a, 'f: 'a> { + #[cfg(any( + all( + not(target_arch = "aarch64"), + not(target_arch = "powerpc"), + not(target_arch = "s390x"), + not(target_arch = "x86_64") + ), + all(target_arch = "aarch64", target_vendor = "apple"), + target_family = "wasm", + target_os = "uefi", + windows, + ))] + inner: VaListImpl<'f>, + + #[cfg(all( + any( + target_arch = "aarch64", + target_arch = "powerpc", + target_arch = "s390x", + target_arch = "x86_64" + ), + any(not(target_arch = "aarch64"), not(target_vendor = "apple")), + not(target_family = "wasm"), + not(target_os = "uefi"), + not(windows), + ))] + inner: &'a mut VaListImpl<'f>, + + _marker: PhantomData<&'a mut VaListImpl<'f>>, +} + +#[cfg(any( + all( + not(target_arch = "aarch64"), + not(target_arch = "powerpc"), + not(target_arch = "s390x"), + not(target_arch = "x86_64") + ), + all(target_arch = "aarch64", target_vendor = "apple"), + target_family = "wasm", + target_os = "uefi", + windows, +))] +impl<'f> VaListImpl<'f> { + /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. + #[inline] + pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { + VaList { inner: VaListImpl { ..*self }, _marker: PhantomData } + } +} + +#[cfg(all( + any( + target_arch = "aarch64", + target_arch = "powerpc", + target_arch = "s390x", + target_arch = "x86_64" + ), + any(not(target_arch = "aarch64"), not(target_vendor = "apple")), + not(target_family = "wasm"), + not(target_os = "uefi"), + not(windows), +))] +impl<'f> VaListImpl<'f> { + /// Convert a `VaListImpl` into a `VaList` that is binary-compatible with C's `va_list`. + #[inline] + pub fn as_va_list<'a>(&'a mut self) -> VaList<'a, 'f> { + VaList { inner: self, _marker: PhantomData } + } +} + +impl<'a, 'f: 'a> Deref for VaList<'a, 'f> { + type Target = VaListImpl<'f>; + + #[inline] + fn deref(&self) -> &VaListImpl<'f> { + &self.inner + } +} + +impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> { + #[inline] + fn deref_mut(&mut self) -> &mut VaListImpl<'f> { + &mut self.inner + } +} + +// The VaArgSafe trait needs to be used in public interfaces, however, the trait +// itself must not be allowed to be used outside this module. Allowing users to +// implement the trait for a new type (thereby allowing the va_arg intrinsic to +// be used on a new type) is likely to cause undefined behavior. +// +// FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface +// but also ensure it cannot be used elsewhere, the trait needs to be public +// within a private module. Once RFC 2145 has been implemented look into +// improving this. +mod sealed_trait { + /// Trait which permits the allowed types to be used with [super::VaListImpl::arg]. + pub unsafe trait VaArgSafe {} +} + +macro_rules! impl_va_arg_safe { + ($($t:ty),+) => { + $( + unsafe impl sealed_trait::VaArgSafe for $t {} + )+ + } +} + +impl_va_arg_safe! {i8, i16, i32, i64, usize} +impl_va_arg_safe! {u8, u16, u32, u64, isize} +impl_va_arg_safe! {f64} + +unsafe impl<T> sealed_trait::VaArgSafe for *mut T {} +unsafe impl<T> sealed_trait::VaArgSafe for *const T {} + +impl<'f> VaListImpl<'f> { + /// Advance to the next arg. + #[inline] + pub unsafe fn arg<T: sealed_trait::VaArgSafe>(&mut self) -> T { + // SAFETY: the caller must uphold the safety contract for `va_arg`. + unsafe { va_arg(self) } + } + + /// Copies the `va_list` at the current location. + pub unsafe fn with_copy<F, R>(&self, f: F) -> R + where + F: for<'copy> FnOnce(VaList<'copy, 'f>) -> R, + { + let mut ap = self.clone(); + let ret = f(ap.as_va_list()); + // SAFETY: the caller must uphold the safety contract for `va_end`. + unsafe { + va_end(&mut ap); + } + ret + } +} + +impl<'f> Clone for VaListImpl<'f> { + #[inline] + fn clone(&self) -> Self { + let mut dest = crate::mem::MaybeUninit::uninit(); + // SAFETY: we write to the `MaybeUninit`, thus it is initialized and `assume_init` is legal + unsafe { + va_copy(dest.as_mut_ptr(), self); + dest.assume_init() + } + } +} + +impl<'f> Drop for VaListImpl<'f> { + fn drop(&mut self) { + // FIXME: this should call `va_end`, but there's no clean way to + // guarantee that `drop` always gets inlined into its caller, + // so the `va_end` would get directly called from the same function as + // the corresponding `va_copy`. `man va_end` states that C requires this, + // and LLVM basically follows the C semantics, so we need to make sure + // that `va_end` is always called from the same function as `va_copy`. + // For more details, see https://github.com/rust-lang/rust/pull/59625 + // and https://llvm.org/docs/LangRef.html#llvm-va-end-intrinsic. + // + // This works for now, since `va_end` is a no-op on all current LLVM targets. + } +} + +extern "rust-intrinsic" { + /// Destroy the arglist `ap` after initialization with `va_start` or + /// `va_copy`. + #[rustc_nounwind] + fn va_end(ap: &mut VaListImpl<'_>); + + /// Copies the current location of arglist `src` to the arglist `dst`. + #[rustc_nounwind] + fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>); + + /// Loads an argument of type `T` from the `va_list` `ap` and increment the + /// argument `ap` points to. + #[rustc_nounwind] + fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaListImpl<'_>) -> T; +} diff --git a/library/core/src/iter/traits/collect.rs b/library/core/src/iter/traits/collect.rs index 61a45789013..86660f2e375 100644 --- a/library/core/src/iter/traits/collect.rs +++ b/library/core/src/iter/traits/collect.rs @@ -1,3 +1,5 @@ +use super::TrustedLen; + /// Conversion from an [`Iterator`]. /// /// By implementing `FromIterator` for a type, you define how it will be @@ -460,6 +462,27 @@ pub trait Extend<A> { fn extend_reserve(&mut self, additional: usize) { let _ = additional; } + + /// Extends a collection with one element, without checking there is enough capacity for it. + /// + /// # Safety + /// + /// **For callers:** This must only be called when we know the collection has enough capacity + /// to contain the new item, for example because we previously called `extend_reserve`. + /// + /// **For implementors:** For a collection to unsafely rely on this method's safety precondition (that is, + /// invoke UB if they are violated), it must implement `extend_reserve` correctly. In other words, + /// callers may assume that if they `extend_reserve`ed enough space they can call this method. + + // This method is for internal usage only. It is only on the trait because of specialization's limitations. + #[unstable(feature = "extend_one_unchecked", issue = "none")] + #[doc(hidden)] + unsafe fn extend_one_unchecked(&mut self, item: A) + where + Self: Sized, + { + self.extend_one(item); + } } #[stable(feature = "extend_for_unit", since = "1.28.0")] @@ -499,33 +522,102 @@ where fn extend<T: IntoIterator<Item = (A, B)>>(&mut self, into_iter: T) { let (a, b) = self; let iter = into_iter.into_iter(); + SpecTupleExtend::extend(iter, a, b); + } + + fn extend_one(&mut self, item: (A, B)) { + self.0.extend_one(item.0); + self.1.extend_one(item.1); + } + + fn extend_reserve(&mut self, additional: usize) { + self.0.extend_reserve(additional); + self.1.extend_reserve(additional); + } + + unsafe fn extend_one_unchecked(&mut self, item: (A, B)) { + // SAFETY: Those are our safety preconditions, and we correctly forward `extend_reserve`. + unsafe { + self.0.extend_one_unchecked(item.0); + self.1.extend_one_unchecked(item.1); + } + } +} + +fn default_extend_tuple<A, B, ExtendA, ExtendB>( + iter: impl Iterator<Item = (A, B)>, + a: &mut ExtendA, + b: &mut ExtendB, +) where + ExtendA: Extend<A>, + ExtendB: Extend<B>, +{ + fn extend<'a, A, B>( + a: &'a mut impl Extend<A>, + b: &'a mut impl Extend<B>, + ) -> impl FnMut((), (A, B)) + 'a { + move |(), (t, u)| { + a.extend_one(t); + b.extend_one(u); + } + } + + let (lower_bound, _) = iter.size_hint(); + if lower_bound > 0 { + a.extend_reserve(lower_bound); + b.extend_reserve(lower_bound); + } + + iter.fold((), extend(a, b)); +} + +trait SpecTupleExtend<A, B> { + fn extend(self, a: &mut A, b: &mut B); +} +impl<A, B, ExtendA, ExtendB, Iter> SpecTupleExtend<ExtendA, ExtendB> for Iter +where + ExtendA: Extend<A>, + ExtendB: Extend<B>, + Iter: Iterator<Item = (A, B)>, +{ + default fn extend(self, a: &mut ExtendA, b: &mut ExtendB) { + default_extend_tuple(self, a, b); + } +} + +impl<A, B, ExtendA, ExtendB, Iter> SpecTupleExtend<ExtendA, ExtendB> for Iter +where + ExtendA: Extend<A>, + ExtendB: Extend<B>, + Iter: TrustedLen<Item = (A, B)>, +{ + fn extend(self, a: &mut ExtendA, b: &mut ExtendB) { fn extend<'a, A, B>( a: &'a mut impl Extend<A>, b: &'a mut impl Extend<B>, ) -> impl FnMut((), (A, B)) + 'a { - move |(), (t, u)| { - a.extend_one(t); - b.extend_one(u); + // SAFETY: We reserve enough space for the `size_hint`, and the iterator is `TrustedLen` + // so its `size_hint` is exact. + move |(), (t, u)| unsafe { + a.extend_one_unchecked(t); + b.extend_one_unchecked(u); } } - let (lower_bound, _) = iter.size_hint(); + let (lower_bound, upper_bound) = self.size_hint(); + + if upper_bound.is_none() { + // We cannot reserve more than `usize::MAX` items, and this is likely to go out of memory anyway. + default_extend_tuple(self, a, b); + return; + } + if lower_bound > 0 { a.extend_reserve(lower_bound); b.extend_reserve(lower_bound); } - iter.fold((), extend(a, b)); - } - - fn extend_one(&mut self, item: (A, B)) { - self.0.extend_one(item.0); - self.1.extend_one(item.1); - } - - fn extend_reserve(&mut self, additional: usize) { - self.0.extend_reserve(additional); - self.1.extend_reserve(additional); + self.fold((), extend(a, b)); } } diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 9054ade2d79..dd4b6e82343 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -1266,6 +1266,20 @@ impl<T> SizedTypeProperties for T {} /// // ^^^ error[E0616]: field `private` of struct `Struct` is private /// ``` /// +/// Only [`Sized`] fields are supported, but the container may be unsized: +/// ``` +/// # use core::mem; +/// #[repr(C)] +/// pub struct Struct { +/// a: u8, +/// b: [u8], +/// } +/// +/// assert_eq!(mem::offset_of!(Struct, a), 0); // OK +/// // assert_eq!(mem::offset_of!(Struct, b), 1); +/// // ^^^ error[E0277]: doesn't have a size known at compile-time +/// ``` +/// /// Note that type layout is, in general, [subject to change and /// platform-specific](https://doc.rust-lang.org/reference/type-layout.html). If /// layout stability is required, consider using an [explicit `repr` attribute]. diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index 3c58b0af9c2..e74300d6c2f 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -507,8 +507,8 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # // FIXME(f16_f128): remove when `extendhfsf2` and `truncsfhf2` are available - /// # #[cfg(target_os = "linux")] { + /// # // FIXME(f16_f128): extendhfsf2, truncsfhf2, __gnu_h2f_ieee, __gnu_f2h_ieee missing for many platforms + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let x = 2.0_f16; /// let abs_difference = (x.recip() - (1.0 / x)).abs(); @@ -528,8 +528,8 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # // FIXME(f16_f128): remove when `extendhfsf2` and `truncsfhf2` are available - /// # #[cfg(target_os = "linux")] { + /// # // FIXME(f16_f128): extendhfsf2, truncsfhf2, __gnu_h2f_ieee, __gnu_f2h_ieee missing for many platforms + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let angle = std::f16::consts::PI; /// @@ -551,8 +551,8 @@ impl f16 { /// /// ``` /// #![feature(f16)] - /// # // FIXME(f16_f128): remove when `extendhfsf2` and `truncsfhf2` are available - /// # #[cfg(target_os = "linux")] { + /// # // FIXME(f16_f128): extendhfsf2, truncsfhf2, __gnu_h2f_ieee, __gnu_f2h_ieee missing for many platforms + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// let angle = 180.0f16; /// @@ -870,6 +870,8 @@ impl f16 { /// /// ``` /// #![feature(f16)] + /// # // FIXME(f16_f128): extendhfsf2, truncsfhf2, __gnu_h2f_ieee, __gnu_f2h_ieee missing for many platforms + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { /// /// struct GoodBoy { /// name: &'static str, @@ -897,6 +899,7 @@ impl f16 { /// .zip([-5.0, 0.1, 10.0, 99.0, f16::INFINITY, f16::NAN].iter()) /// .for_each(|(a, b)| assert_eq!(a.to_bits(), b.to_bits())) /// } + /// # } /// ``` #[inline] #[must_use] diff --git a/library/core/tests/fmt/builders.rs b/library/core/tests/fmt/builders.rs index 487ce46be28..2bdc334b7c0 100644 --- a/library/core/tests/fmt/builders.rs +++ b/library/core/tests/fmt/builders.rs @@ -441,7 +441,7 @@ mod debug_map { } } - format!("{Foo:?}"); + let _ = format!("{Foo:?}"); } #[test] @@ -455,7 +455,7 @@ mod debug_map { } } - format!("{Foo:?}"); + let _ = format!("{Foo:?}"); } #[test] @@ -469,7 +469,7 @@ mod debug_map { } } - format!("{Foo:?}"); + let _ = format!("{Foo:?}"); } } diff --git a/library/std/build.rs b/library/std/build.rs index 55388648a14..c542ba81eed 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -99,7 +99,9 @@ fn main() { // the compiler-builtins update. <https://github.com/rust-lang/rust/issues/123885> ("x86" | "x86_64", _) => false, // Missing `__gnu_h2f_ieee` and `__gnu_f2h_ieee` - ("powerpc" | "powerpc64" | "powerpc64le", _) => false, + ("powerpc" | "powerpc64", _) => false, + // Missing `__gnu_h2f_ieee` and `__gnu_f2h_ieee` + ("mips" | "mips32r6" | "mips64" | "mips64r6", _) => false, // Missing `__extendhfsf` and `__truncsfhf` ("riscv32" | "riscv64", _) => false, // Most OSs are missing `__extendhfsf` and `__truncsfhf` diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 8c7fc4cb2e4..a1f83029d27 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -175,6 +175,11 @@ impl Drop for OwnedFd { // the file descriptor was closed or not, and if we retried (for // something like EINTR), we might close another valid file descriptor // opened after we closed ours. + // However, this is usually justified, as some of the major Unices + // do make sure to always close the FD, even when `close()` is interrupted, + // and the scenario is rare to begin with. + // Helpful link to an epic discussion by POSIX workgroup: + // http://austingroupbugs.net/view.php?id=529 #[cfg(not(target_os = "hermit"))] { #[cfg(unix)] diff --git a/library/std/src/path.rs b/library/std/src/path.rs index 8d565e26a16..d5121a554bf 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -3192,15 +3192,19 @@ impl Hash for Path { let bytes = &bytes[prefix_len..]; let mut component_start = 0; - let mut bytes_hashed = 0; + // track some extra state to avoid prefix collisions. + // ["foo", "bar"] and ["foobar"], will have the same payload bytes + // but result in different chunk_bits + let mut chunk_bits: usize = 0; for i in 0..bytes.len() { let is_sep = if verbatim { is_verbatim_sep(bytes[i]) } else { is_sep_byte(bytes[i]) }; if is_sep { if i > component_start { let to_hash = &bytes[component_start..i]; + chunk_bits = chunk_bits.wrapping_add(to_hash.len()); + chunk_bits = chunk_bits.rotate_right(2); h.write(to_hash); - bytes_hashed += to_hash.len(); } // skip over separator and optionally a following CurDir item @@ -3221,11 +3225,12 @@ impl Hash for Path { if component_start < bytes.len() { let to_hash = &bytes[component_start..]; + chunk_bits = chunk_bits.wrapping_add(to_hash.len()); + chunk_bits = chunk_bits.rotate_right(2); h.write(to_hash); - bytes_hashed += to_hash.len(); } - h.write_usize(bytes_hashed); + h.write_usize(chunk_bits); } } diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs index bb6126e4941..3ade4fb892f 100644 --- a/library/std/src/path/tests.rs +++ b/library/std/src/path/tests.rs @@ -1619,6 +1619,20 @@ pub fn test_compare() { relative_from: Some("") ); + tc!("foo//", "foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo///", "foo", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + tc!("foo/.", "foo", eq: true, starts_with: true, @@ -1633,6 +1647,20 @@ pub fn test_compare() { relative_from: Some("") ); + tc!("foo/.//bar", "foo/bar", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + + tc!("foo//./bar", "foo/bar", + eq: true, + starts_with: true, + ends_with: true, + relative_from: Some("") + ); + tc!("foo/bar", "foo", eq: false, starts_with: true, @@ -1640,6 +1668,13 @@ pub fn test_compare() { relative_from: Some("bar") ); + tc!("foo/bar", "foobar", + eq: false, + starts_with: false, + ends_with: false, + relative_from: None + ); + tc!("foo/bar/baz", "foo/bar", eq: false, starts_with: true, diff --git a/library/std/src/sync/once_lock.rs b/library/std/src/sync/once_lock.rs index f52b9e52c54..fe243550606 100644 --- a/library/std/src/sync/once_lock.rs +++ b/library/std/src/sync/once_lock.rs @@ -80,14 +80,21 @@ use crate::sync::Once; /// static LIST: OnceList<u32> = OnceList::new(); /// static COUNTER: AtomicU32 = AtomicU32::new(0); /// -/// let vec = (0..thread::available_parallelism().unwrap().get()).map(|_| thread::spawn(|| { -/// while let i @ 0..=1000 = COUNTER.fetch_add(1, Ordering::Relaxed) { -/// LIST.push(i); +/// # const LEN: u32 = if cfg!(miri) { 50 } else { 1000 }; +/// # /* +/// const LEN: u32 = 1000; +/// # */ +/// thread::scope(|s| { +/// for _ in 0..thread::available_parallelism().unwrap().get() { +/// s.spawn(|| { +/// while let i @ 0..LEN = COUNTER.fetch_add(1, Ordering::Relaxed) { +/// LIST.push(i); +/// } +/// }); /// } -/// })).collect::<Vec<thread::JoinHandle<_>>>(); -/// vec.into_iter().for_each(|handle| handle.join().unwrap()); +/// }); /// -/// for i in 0..=1000 { +/// for i in 0..LEN { /// assert!(LIST.contains(&i)); /// } /// diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index 9c24742cff1..c095127da14 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -54,7 +54,6 @@ dependencies = [ "junction", "libc", "object", - "once_cell", "opener", "pretty_assertions", "semver", diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 32dd3efa7a6..df7b5b88193 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -49,7 +49,6 @@ home = "0.5" ignore = "0.4" libc = "0.2" object = { version = "0.32", default-features = false, features = ["archive", "coff", "read_core", "unaligned"] } -once_cell = "1.19" opener = "0.5" semver = "1.0" serde = "1.0" diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs index 479af4af666..a4be6bc56c5 100644 --- a/src/bootstrap/src/core/build_steps/clean.rs +++ b/src/bootstrap/src/core/build_steps/clean.rs @@ -85,7 +85,7 @@ macro_rules! clean_crate_tree { // NOTE: doesn't use `run_cargo` because we don't want to save a stamp file, // and doesn't use `stream_cargo` to avoid passing `--message-format` which `clean` doesn't accept. - builder.run(cargo); + cargo.run(builder); } } )+ } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 01b25224de4..11a7a404535 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -27,7 +27,7 @@ use crate::core::builder::crate_description; use crate::core::builder::Cargo; use crate::core::builder::{Builder, Kind, PathSet, RunConfig, ShouldRun, Step, TaskPath}; use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection}; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::command; use crate::utils::helpers::{ exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, symlink_dir, t, up_to_date, }; @@ -773,20 +773,19 @@ impl Step for StartupObjects { let src_file = &src_dir.join(file.to_string() + ".rs"); let dst_file = &dst_dir.join(file.to_string() + ".o"); if !up_to_date(src_file, dst_file) { - let mut cmd = BootstrapCommand::new(&builder.initial_rustc); + let mut cmd = command(&builder.initial_rustc); cmd.env("RUSTC_BOOTSTRAP", "1"); if !builder.local_rebuild { // a local_rebuild compiler already has stage1 features cmd.arg("--cfg").arg("bootstrap"); } - builder.run( - cmd.arg("--target") - .arg(target.rustc_target_arg()) - .arg("--emit=obj") - .arg("-o") - .arg(dst_file) - .arg(src_file), - ); + cmd.arg("--target") + .arg(target.rustc_target_arg()) + .arg("--emit=obj") + .arg("-o") + .arg(dst_file) + .arg(src_file) + .run(builder); } let target = sysroot_dir.join((*file).to_string() + ".o"); @@ -1488,10 +1487,10 @@ pub fn compiler_file( if builder.config.dry_run() { return PathBuf::new(); } - let mut cmd = BootstrapCommand::new(compiler); + let mut cmd = command(compiler); cmd.args(builder.cflags(target, GitRepo::Rustc, c)); cmd.arg(format!("-print-file-name={file}")); - let out = builder.run(cmd.capture_stdout()).stdout(); + let out = cmd.capture_stdout().run(builder).stdout(); PathBuf::from(out.trim()) } @@ -1836,9 +1835,8 @@ impl Step for Assemble { let llvm::LlvmResult { llvm_config, .. } = builder.ensure(llvm::Llvm { target: target_compiler.host }); if !builder.config.dry_run() && builder.config.llvm_tools_enabled { - let llvm_bin_dir = builder - .run(BootstrapCommand::new(llvm_config).capture_stdout().arg("--bindir")) - .stdout(); + let llvm_bin_dir = + command(llvm_config).capture_stdout().arg("--bindir").run(builder).stdout(); let llvm_bin_dir = Path::new(llvm_bin_dir.trim()); // Since we've already built the LLVM tools, install them to the sysroot. @@ -2163,7 +2161,7 @@ pub fn strip_debug(builder: &Builder<'_>, target: TargetSelection, path: &Path) } let previous_mtime = FileTime::from_last_modification_time(&path.metadata().unwrap()); - builder.run(BootstrapCommand::new("strip").capture().arg("--strip-debug").arg(path)); + command("strip").capture().arg("--strip-debug").arg(path).run(builder); // After running `strip`, we have to set the file modification time to what it was before, // otherwise we risk Cargo invalidating its fingerprint and rebuilding the world next time diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 3dc871b1ca9..7bc5405e92f 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -25,7 +25,7 @@ use crate::core::build_steps::tool::{self, Tool}; use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; use crate::utils::channel::{self, Info}; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::{command, BootstrapCommand}; use crate::utils::helpers::{ exe, is_dylib, move_file, t, target_supports_cranelift_backend, timeit, }; @@ -180,9 +180,9 @@ fn make_win_dist( } //Ask gcc where it keeps its stuff - let mut cmd = BootstrapCommand::new(builder.cc(target)); + let mut cmd = command(builder.cc(target)); cmd.arg("-print-search-dirs"); - let gcc_out = builder.run(cmd.capture_stdout()).stdout(); + let gcc_out = cmd.capture_stdout().run(builder).stdout(); let mut bin_path: Vec<_> = env::split_paths(&env::var_os("PATH").unwrap_or_default()).collect(); let mut lib_path = Vec::new(); @@ -1023,7 +1023,7 @@ impl Step for PlainSourceTarball { } // Vendor all Cargo dependencies - let mut cmd = BootstrapCommand::new(&builder.initial_cargo); + let mut cmd = command(&builder.initial_cargo); cmd.arg("vendor") .arg("--versioned-dirs") .arg("--sync") @@ -1061,7 +1061,7 @@ impl Step for PlainSourceTarball { } let config = if !builder.config.dry_run() { - builder.run(cmd.capture()).stdout() + cmd.capture().run(builder).stdout() } else { String::new() }; @@ -1599,14 +1599,14 @@ impl Step for Extended { let _ = fs::remove_dir_all(&pkg); let pkgbuild = |component: &str| { - let mut cmd = BootstrapCommand::new("pkgbuild"); + let mut cmd = command("pkgbuild"); cmd.arg("--identifier") .arg(format!("org.rust-lang.{}", component)) .arg("--scripts") .arg(pkg.join(component)) .arg("--nopayload") .arg(pkg.join(component).with_extension("pkg")); - builder.run(cmd); + cmd.run(builder); }; let prepare = |name: &str| { @@ -1636,7 +1636,7 @@ impl Step for Extended { builder.create_dir(&pkg.join("res")); builder.create(&pkg.join("res/LICENSE.txt"), &license); builder.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), 0o644); - let mut cmd = BootstrapCommand::new("productbuild"); + let mut cmd = command("productbuild"); cmd.arg("--distribution") .arg(xform(&etc.join("pkg/Distribution.xml"))) .arg("--resources") @@ -1649,7 +1649,7 @@ impl Step for Extended { .arg("--package-path") .arg(&pkg); let _time = timeit(builder); - builder.run(cmd); + cmd.run(builder); } if target.is_windows() { @@ -1703,168 +1703,159 @@ impl Step for Extended { let light = wix.join("bin/light.exe"); let heat_flags = ["-nologo", "-gg", "-sfrag", "-srd", "-sreg"]; - builder.run( - BootstrapCommand::new(&heat) + command(&heat) + .current_dir(&exe) + .arg("dir") + .arg("rustc") + .args(heat_flags) + .arg("-cg") + .arg("RustcGroup") + .arg("-dr") + .arg("Rustc") + .arg("-var") + .arg("var.RustcDir") + .arg("-out") + .arg(exe.join("RustcGroup.wxs")) + .run(builder); + if built_tools.contains("rust-docs") { + command(&heat) .current_dir(&exe) .arg("dir") - .arg("rustc") + .arg("rust-docs") .args(heat_flags) .arg("-cg") - .arg("RustcGroup") + .arg("DocsGroup") .arg("-dr") - .arg("Rustc") + .arg("Docs") .arg("-var") - .arg("var.RustcDir") + .arg("var.DocsDir") .arg("-out") - .arg(exe.join("RustcGroup.wxs")), - ); - if built_tools.contains("rust-docs") { - builder.run( - BootstrapCommand::new(&heat) - .current_dir(&exe) - .arg("dir") - .arg("rust-docs") - .args(heat_flags) - .arg("-cg") - .arg("DocsGroup") - .arg("-dr") - .arg("Docs") - .arg("-var") - .arg("var.DocsDir") - .arg("-out") - .arg(exe.join("DocsGroup.wxs")) - .arg("-t") - .arg(etc.join("msi/squash-components.xsl")), - ); + .arg(exe.join("DocsGroup.wxs")) + .arg("-t") + .arg(etc.join("msi/squash-components.xsl")) + .run(builder); } - builder.run( - BootstrapCommand::new(&heat) + command(&heat) + .current_dir(&exe) + .arg("dir") + .arg("cargo") + .args(heat_flags) + .arg("-cg") + .arg("CargoGroup") + .arg("-dr") + .arg("Cargo") + .arg("-var") + .arg("var.CargoDir") + .arg("-out") + .arg(exe.join("CargoGroup.wxs")) + .arg("-t") + .arg(etc.join("msi/remove-duplicates.xsl")) + .run(builder); + command(&heat) + .current_dir(&exe) + .arg("dir") + .arg("rust-std") + .args(heat_flags) + .arg("-cg") + .arg("StdGroup") + .arg("-dr") + .arg("Std") + .arg("-var") + .arg("var.StdDir") + .arg("-out") + .arg(exe.join("StdGroup.wxs")) + .run(builder); + if built_tools.contains("rust-analyzer") { + command(&heat) .current_dir(&exe) .arg("dir") - .arg("cargo") + .arg("rust-analyzer") .args(heat_flags) .arg("-cg") - .arg("CargoGroup") + .arg("RustAnalyzerGroup") .arg("-dr") - .arg("Cargo") + .arg("RustAnalyzer") .arg("-var") - .arg("var.CargoDir") + .arg("var.RustAnalyzerDir") .arg("-out") - .arg(exe.join("CargoGroup.wxs")) + .arg(exe.join("RustAnalyzerGroup.wxs")) .arg("-t") - .arg(etc.join("msi/remove-duplicates.xsl")), - ); - builder.run( - BootstrapCommand::new(&heat) + .arg(etc.join("msi/remove-duplicates.xsl")) + .run(builder); + } + if built_tools.contains("clippy") { + command(&heat) .current_dir(&exe) .arg("dir") - .arg("rust-std") + .arg("clippy") .args(heat_flags) .arg("-cg") - .arg("StdGroup") + .arg("ClippyGroup") .arg("-dr") - .arg("Std") + .arg("Clippy") .arg("-var") - .arg("var.StdDir") + .arg("var.ClippyDir") .arg("-out") - .arg(exe.join("StdGroup.wxs")), - ); - if built_tools.contains("rust-analyzer") { - builder.run( - BootstrapCommand::new(&heat) - .current_dir(&exe) - .arg("dir") - .arg("rust-analyzer") - .args(heat_flags) - .arg("-cg") - .arg("RustAnalyzerGroup") - .arg("-dr") - .arg("RustAnalyzer") - .arg("-var") - .arg("var.RustAnalyzerDir") - .arg("-out") - .arg(exe.join("RustAnalyzerGroup.wxs")) - .arg("-t") - .arg(etc.join("msi/remove-duplicates.xsl")), - ); - } - if built_tools.contains("clippy") { - builder.run( - BootstrapCommand::new(&heat) - .current_dir(&exe) - .arg("dir") - .arg("clippy") - .args(heat_flags) - .arg("-cg") - .arg("ClippyGroup") - .arg("-dr") - .arg("Clippy") - .arg("-var") - .arg("var.ClippyDir") - .arg("-out") - .arg(exe.join("ClippyGroup.wxs")) - .arg("-t") - .arg(etc.join("msi/remove-duplicates.xsl")), - ); + .arg(exe.join("ClippyGroup.wxs")) + .arg("-t") + .arg(etc.join("msi/remove-duplicates.xsl")) + .run(builder); } if built_tools.contains("miri") { - builder.run( - BootstrapCommand::new(&heat) - .current_dir(&exe) - .arg("dir") - .arg("miri") - .args(heat_flags) - .arg("-cg") - .arg("MiriGroup") - .arg("-dr") - .arg("Miri") - .arg("-var") - .arg("var.MiriDir") - .arg("-out") - .arg(exe.join("MiriGroup.wxs")) - .arg("-t") - .arg(etc.join("msi/remove-duplicates.xsl")), - ); - } - builder.run( - BootstrapCommand::new(&heat) + command(&heat) .current_dir(&exe) .arg("dir") - .arg("rust-analysis") + .arg("miri") .args(heat_flags) .arg("-cg") - .arg("AnalysisGroup") + .arg("MiriGroup") .arg("-dr") - .arg("Analysis") + .arg("Miri") .arg("-var") - .arg("var.AnalysisDir") + .arg("var.MiriDir") .arg("-out") - .arg(exe.join("AnalysisGroup.wxs")) + .arg(exe.join("MiriGroup.wxs")) .arg("-t") - .arg(etc.join("msi/remove-duplicates.xsl")), - ); + .arg(etc.join("msi/remove-duplicates.xsl")) + .run(builder); + } + command(&heat) + .current_dir(&exe) + .arg("dir") + .arg("rust-analysis") + .args(heat_flags) + .arg("-cg") + .arg("AnalysisGroup") + .arg("-dr") + .arg("Analysis") + .arg("-var") + .arg("var.AnalysisDir") + .arg("-out") + .arg(exe.join("AnalysisGroup.wxs")) + .arg("-t") + .arg(etc.join("msi/remove-duplicates.xsl")) + .run(builder); if target.ends_with("windows-gnu") { - builder.run( - BootstrapCommand::new(&heat) - .current_dir(&exe) - .arg("dir") - .arg("rust-mingw") - .args(heat_flags) - .arg("-cg") - .arg("GccGroup") - .arg("-dr") - .arg("Gcc") - .arg("-var") - .arg("var.GccDir") - .arg("-out") - .arg(exe.join("GccGroup.wxs")), - ); + command(&heat) + .current_dir(&exe) + .arg("dir") + .arg("rust-mingw") + .args(heat_flags) + .arg("-cg") + .arg("GccGroup") + .arg("-dr") + .arg("Gcc") + .arg("-var") + .arg("var.GccDir") + .arg("-out") + .arg(exe.join("GccGroup.wxs")) + .run(builder); } let candle = |input: &Path| { let output = exe.join(input.file_stem().unwrap()).with_extension("wixobj"); let arch = if target.contains("x86_64") { "x64" } else { "x86" }; - let mut cmd = BootstrapCommand::new(&candle); + let mut cmd = command(&candle); cmd.current_dir(&exe) .arg("-nologo") .arg("-dRustcDir=rustc") @@ -1893,7 +1884,7 @@ impl Step for Extended { if target.ends_with("windows-gnu") { cmd.arg("-dGccDir=rust-mingw"); } - builder.run(cmd); + cmd.run(builder); }; candle(&xform(&etc.join("msi/rust.wxs"))); candle(&etc.join("msi/ui.wxs")); @@ -1925,7 +1916,7 @@ impl Step for Extended { builder.info(&format!("building `msi` installer with {light:?}")); let filename = format!("{}-{}.msi", pkgname(builder, "rust"), target.triple); - let mut cmd = BootstrapCommand::new(&light); + let mut cmd = command(&light); cmd.arg("-nologo") .arg("-ext") .arg("WixUIExtension") @@ -1962,7 +1953,7 @@ impl Step for Extended { cmd.arg("-sice:ICE57"); let _time = timeit(builder); - builder.run(cmd); + cmd.run(builder); if !builder.config.dry_run() { t!(move_file(exe.join(&filename), distdir(builder).join(&filename))); @@ -2078,13 +2069,13 @@ fn maybe_install_llvm( } else if let llvm::LlvmBuildStatus::AlreadyBuilt(llvm::LlvmResult { llvm_config, .. }) = llvm::prebuilt_llvm_config(builder, target) { - let mut cmd = BootstrapCommand::new(llvm_config); + let mut cmd = command(llvm_config); cmd.arg("--libfiles"); builder.verbose(|| println!("running {cmd:?}")); let files = if builder.config.dry_run() { "".into() } else { - builder.run(cmd.capture_stdout()).stdout() + cmd.capture_stdout().run(builder).stdout() }; let build_llvm_out = &builder.llvm_out(builder.config.build); let target_llvm_out = &builder.llvm_out(target); diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 823e842693e..4b35d6c5d4c 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -154,7 +154,7 @@ impl<P: Step> Step for RustbookSrc<P> { builder.info(&format!("Rustbook ({target}) - {name}")); let _ = fs::remove_dir_all(&out); - builder.run(rustbook_cmd.arg("build").arg(&src).arg("-d").arg(&out)); + rustbook_cmd.arg("build").arg(&src).arg("-d").arg(&out).run(builder); for lang in &self.languages { let out = out.join(lang); @@ -162,10 +162,15 @@ impl<P: Step> Step for RustbookSrc<P> { builder.info(&format!("Rustbook ({target}) - {name} - {lang}")); let _ = fs::remove_dir_all(&out); - let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook); - builder.run( - rustbook_cmd.arg("build").arg(&src).arg("-d").arg(&out).arg("-l").arg(lang), - ); + builder + .tool_cmd(Tool::Rustbook) + .arg("build") + .arg(&src) + .arg("-d") + .arg(&out) + .arg("-l") + .arg(lang) + .run(builder); } } @@ -301,7 +306,7 @@ fn invoke_rustdoc( cmd.arg("-Z").arg("unstable-options").arg("--disable-minification"); } - builder.run(cmd); + cmd.run(builder); } #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -395,7 +400,7 @@ impl Step for Standalone { } else { cmd.arg("--markdown-css").arg("rust.css"); } - builder.run(cmd); + cmd.run(builder); } // We open doc/index.html as the default if invoked as `x.py doc --open` @@ -494,7 +499,7 @@ impl Step for Releases { cmd.arg("--disable-minification"); } - builder.run(cmd); + cmd.run(builder); } // We open doc/RELEASES.html as the default if invoked as `x.py doc --open RELEASES.md` @@ -738,7 +743,7 @@ fn doc_std( format!("library{} in {} format", crate_description(requested_crates), format.as_str()); let _guard = builder.msg_doc(compiler, description, target); - builder.run(cargo.into_cmd()); + cargo.into_cmd().run(builder); builder.cp_link_r(&out_dir, out); } @@ -863,7 +868,7 @@ impl Step for Rustc { let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc"); symlink_dir_force(&builder.config, &out, &proc_macro_out_dir); - builder.run(cargo.into_cmd()); + cargo.into_cmd().run(builder); if !builder.config.dry_run() { // Sanity check on linked compiler crates @@ -996,7 +1001,7 @@ macro_rules! tool_doc { symlink_dir_force(&builder.config, &out, &proc_macro_out_dir); let _guard = builder.msg_doc(compiler, stringify!($tool).to_lowercase(), target); - builder.run(cargo.into_cmd()); + cargo.into_cmd().run(builder); if !builder.config.dry_run() { // Sanity check on linked doc directories @@ -1075,12 +1080,7 @@ impl Step for ErrorIndex { builder.info(&format!("Documenting error index ({})", self.target)); let out = builder.doc_out(self.target); t!(fs::create_dir_all(&out)); - let mut index = tool::ErrorIndex::command(builder); - index.arg("html"); - index.arg(out); - index.arg(&builder.version); - - builder.run(index); + tool::ErrorIndex::command(builder).arg("html").arg(out).arg(&builder.version).run(builder); } } @@ -1116,7 +1116,7 @@ impl Step for UnstableBookGen { cmd.arg(builder.src.join("src")); cmd.arg(out); - builder.run(cmd); + cmd.run(builder); } } @@ -1211,7 +1211,7 @@ impl Step for RustcBook { self.compiler.host, self.target, ); - builder.run(cmd); + cmd.run(builder); drop(doc_generator_guard); // Run rustbook/mdbook to generate the HTML pages. diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs index 66286a52813..d3ac2bae1e8 100644 --- a/src/bootstrap/src/core/build_steps/format.rs +++ b/src/bootstrap/src/core/build_steps/format.rs @@ -1,7 +1,7 @@ //! Runs rustfmt on the repository. use crate::core::builder::Builder; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::command; use crate::utils::helpers::{self, program_out_of_date, t}; use build_helper::ci::CiEnv; use build_helper::git::get_git_modified_files; @@ -54,13 +54,13 @@ fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl F fn get_rustfmt_version(build: &Builder<'_>) -> Option<(String, PathBuf)> { let stamp_file = build.out.join("rustfmt.stamp"); - let mut cmd = BootstrapCommand::new(match build.initial_rustfmt() { + let mut cmd = command(match build.initial_rustfmt() { Some(p) => p, None => return None, }); cmd.arg("--version"); - let output = build.run(cmd.capture().allow_failure()); + let output = cmd.capture().allow_failure().run(build); if output.is_failure() { return None; } @@ -160,29 +160,25 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) { } } let git_available = - build.run(helpers::git(None).capture().allow_failure().arg("--version")).is_success(); + helpers::git(None).capture().allow_failure().arg("--version").run(build).is_success(); let mut adjective = None; if git_available { - let in_working_tree = build - .run( - helpers::git(Some(&build.src)) - .capture() - .allow_failure() - .arg("rev-parse") - .arg("--is-inside-work-tree"), - ) + let in_working_tree = helpers::git(Some(&build.src)) + .capture() + .allow_failure() + .arg("rev-parse") + .arg("--is-inside-work-tree") + .run(build) .is_success(); if in_working_tree { - let untracked_paths_output = build - .run( - helpers::git(Some(&build.src)) - .capture_stdout() - .arg("status") - .arg("--porcelain") - .arg("-z") - .arg("--untracked-files=normal"), - ) + let untracked_paths_output = helpers::git(Some(&build.src)) + .capture_stdout() + .arg("status") + .arg("--porcelain") + .arg("-z") + .arg("--untracked-files=normal") + .run(build) .stdout(); let untracked_paths: Vec<_> = untracked_paths_output .split_terminator('\0') diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs index 7ee1aca2abc..d3e9d6d7875 100644 --- a/src/bootstrap/src/core/build_steps/install.rs +++ b/src/bootstrap/src/core/build_steps/install.rs @@ -10,7 +10,7 @@ use std::path::{Component, Path, PathBuf}; use crate::core::build_steps::dist; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::{Config, TargetSelection}; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::command; use crate::utils::helpers::t; use crate::utils::tarball::GeneratedTarball; use crate::{Compiler, Kind}; @@ -102,7 +102,7 @@ fn install_sh( let empty_dir = builder.out.join("tmp/empty_dir"); t!(fs::create_dir_all(&empty_dir)); - let mut cmd = BootstrapCommand::new(SHELL); + let mut cmd = command(SHELL); cmd.current_dir(&empty_dir) .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh"))) .arg(format!("--prefix={}", prepare_dir(&destdir_env, prefix))) @@ -113,7 +113,7 @@ fn install_sh( .arg(format!("--libdir={}", prepare_dir(&destdir_env, libdir))) .arg(format!("--mandir={}", prepare_dir(&destdir_env, mandir))) .arg("--disable-ldconfig"); - builder.run(cmd); + cmd.run(builder); t!(fs::remove_dir_all(&empty_dir)); } diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 4f1c1f87d1c..872823506f8 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -24,7 +24,7 @@ use crate::utils::helpers::{ }; use crate::{generate_smart_stamp_hash, CLang, GitRepo, Kind}; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::command; use build_helper::ci::CiEnv; use build_helper::git::get_git_merge_base; @@ -478,9 +478,8 @@ impl Step for Llvm { let LlvmResult { llvm_config, .. } = builder.ensure(Llvm { target: builder.config.build }); if !builder.config.dry_run() { - let llvm_bindir = builder - .run(BootstrapCommand::new(&llvm_config).capture_stdout().arg("--bindir")) - .stdout(); + let llvm_bindir = + command(&llvm_config).capture_stdout().arg("--bindir").run(builder).stdout(); let host_bin = Path::new(llvm_bindir.trim()); cfg.define( "LLVM_TABLEGEN", @@ -530,8 +529,8 @@ impl Step for Llvm { // Helper to find the name of LLVM's shared library on darwin and linux. let find_llvm_lib_name = |extension| { - let cmd = BootstrapCommand::new(&res.llvm_config); - let version = builder.run(cmd.capture_stdout().arg("--version")).stdout(); + let version = + command(&res.llvm_config).capture_stdout().arg("--version").run(builder).stdout(); let major = version.split('.').next().unwrap(); match &llvm_version_suffix { @@ -587,8 +586,7 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) { return; } - let cmd = BootstrapCommand::new(llvm_config); - let version = builder.run(cmd.capture_stdout().arg("--version")).stdout(); + let version = command(llvm_config).capture_stdout().arg("--version").run(builder).stdout(); let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok()); if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) { if major >= 17 { @@ -755,7 +753,7 @@ fn configure_cmake( } if builder.config.llvm_clang_cl.is_some() { - cflags.push(&format!(" --target={target}")); + cflags.push(format!(" --target={target}")); } cfg.define("CMAKE_C_FLAGS", cflags); let mut cxxflags: OsString = builder @@ -774,7 +772,7 @@ fn configure_cmake( cxxflags.push(s); } if builder.config.llvm_clang_cl.is_some() { - cxxflags.push(&format!(" --target={target}")); + cxxflags.push(format!(" --target={target}")); } cfg.define("CMAKE_CXX_FLAGS", cxxflags); if let Some(ar) = builder.ar(target) { @@ -915,7 +913,7 @@ impl Step for Lld { // Find clang's runtime library directory and push that as a search path to the // cmake linker flags. let clang_rt_dir = get_clang_cl_resource_dir(clang_cl_path); - ldflags.push_all(&format!("/libpath:{}", clang_rt_dir.display())); + ldflags.push_all(format!("/libpath:{}", clang_rt_dir.display())); } } diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs index f41b5fe10f1..5b83080a326 100644 --- a/src/bootstrap/src/core/build_steps/perf.rs +++ b/src/bootstrap/src/core/build_steps/perf.rs @@ -31,5 +31,5 @@ Consider setting `rust.debuginfo-level = 1` in `config.toml`."#); .env("PERF_COLLECTOR", collector) .env("PERF_RESULT_DIR", profile_results_dir) .args(args); - builder.run(&mut cmd); + cmd.run(builder); } diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs index 316a03a2171..3a2d3f67522 100644 --- a/src/bootstrap/src/core/build_steps/run.rs +++ b/src/bootstrap/src/core/build_steps/run.rs @@ -11,7 +11,7 @@ use crate::core::build_steps::tool::{self, SourceType, Tool}; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::flags::get_completion; use crate::core::config::TargetSelection; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::command; use crate::Mode; #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)] @@ -40,8 +40,7 @@ impl Step for BuildManifest { panic!("\n\nfailed to specify `dist.upload-addr` in `config.toml`\n\n") }); - let today = - builder.run(BootstrapCommand::new("date").capture_stdout().arg("+%Y-%m-%d")).stdout(); + let today = command("date").capture_stdout().arg("+%Y-%m-%d").run(builder).stdout(); cmd.arg(sign); cmd.arg(distdir(builder)); @@ -50,7 +49,7 @@ impl Step for BuildManifest { cmd.arg(&builder.config.channel); builder.create_dir(&distdir(builder)); - builder.run(cmd); + cmd.run(builder); } } @@ -72,7 +71,7 @@ impl Step for BumpStage0 { fn run(self, builder: &Builder<'_>) -> Self::Output { let mut cmd = builder.tool_cmd(Tool::BumpStage0); cmd.args(builder.config.args()); - builder.run(cmd); + cmd.run(builder); } } @@ -94,7 +93,7 @@ impl Step for ReplaceVersionPlaceholder { fn run(self, builder: &Builder<'_>) -> Self::Output { let mut cmd = builder.tool_cmd(Tool::ReplaceVersionPlaceholder); cmd.arg(&builder.src); - builder.run(cmd); + cmd.run(builder); } } @@ -158,7 +157,7 @@ impl Step for Miri { // after another --, so this must be at the end. miri.args(builder.config.args()); - builder.run(miri.into_cmd()); + miri.into_cmd().run(builder); } } @@ -188,7 +187,7 @@ impl Step for CollectLicenseMetadata { let mut cmd = builder.tool_cmd(Tool::CollectLicenseMetadata); cmd.env("REUSE_EXE", reuse); cmd.env("DEST", &dest); - builder.run(cmd); + cmd.run(builder); dest } @@ -218,7 +217,7 @@ impl Step for GenerateCopyright { let mut cmd = builder.tool_cmd(Tool::GenerateCopyright); cmd.env("LICENSE_METADATA", &license_metadata); cmd.env("DEST", &dest); - builder.run(cmd); + cmd.run(builder); dest } @@ -242,7 +241,7 @@ impl Step for GenerateWindowsSys { fn run(self, builder: &Builder<'_>) { let mut cmd = builder.tool_cmd(Tool::GenerateWindowsSys); cmd.arg(&builder.src); - builder.run(cmd); + cmd.run(builder); } } diff --git a/src/bootstrap/src/core/build_steps/suggest.rs b/src/bootstrap/src/core/build_steps/suggest.rs index 5412b3c807b..2d4409d27c6 100644 --- a/src/bootstrap/src/core/build_steps/suggest.rs +++ b/src/bootstrap/src/core/build_steps/suggest.rs @@ -13,13 +13,11 @@ use crate::core::builder::Builder; pub fn suggest(builder: &Builder<'_>, run: bool) { let git_config = builder.config.git_config(); let suggestions = builder - .run( - builder - .tool_cmd(Tool::SuggestTests) - .capture_stdout() - .env("SUGGEST_TESTS_GIT_REPOSITORY", git_config.git_repository) - .env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch), - ) + .tool_cmd(Tool::SuggestTests) + .capture_stdout() + .env("SUGGEST_TESTS_GIT_REPOSITORY", git_config.git_repository) + .env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch) + .run(builder) .stdout(); let suggestions = suggestions diff --git a/src/bootstrap/src/core/build_steps/synthetic_targets.rs b/src/bootstrap/src/core/build_steps/synthetic_targets.rs index a115ba2d8b2..021680302cd 100644 --- a/src/bootstrap/src/core/build_steps/synthetic_targets.rs +++ b/src/bootstrap/src/core/build_steps/synthetic_targets.rs @@ -9,7 +9,7 @@ use crate::core::builder::{Builder, ShouldRun, Step}; use crate::core::config::TargetSelection; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::command; use crate::Compiler; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -56,7 +56,7 @@ fn create_synthetic_target( return TargetSelection::create_synthetic(&name, path.to_str().unwrap()); } - let mut cmd = BootstrapCommand::new(builder.rustc(compiler)); + let mut cmd = command(builder.rustc(compiler)); cmd.arg("--target").arg(base.rustc_target_arg()); cmd.args(["-Zunstable-options", "--print", "target-spec-json"]); @@ -64,7 +64,7 @@ fn create_synthetic_target( // we cannot use nightly features. So `RUSTC_BOOTSTRAP` is needed here. cmd.env("RUSTC_BOOTSTRAP", "1"); - let output = builder.run(cmd.capture()).stdout(); + let output = cmd.capture().run(builder).stdout(); let mut spec: serde_json::Value = serde_json::from_slice(output.as_bytes()).unwrap(); let spec_map = spec.as_object_mut().unwrap(); diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index dc53bd3cfa7..7f4c4bd53df 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -26,7 +26,7 @@ use crate::core::builder::{Builder, Compiler, Kind, RunConfig, ShouldRun, Step}; use crate::core::config::flags::get_completion; use crate::core::config::flags::Subcommand; use crate::core::config::TargetSelection; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::{command, BootstrapCommand}; use crate::utils::helpers::{ self, add_link_lib_path, add_rustdoc_cargo_linker_args, dylib_path, dylib_path_var, linker_args, linker_flags, t, target_supports_cranelift_backend, up_to_date, LldThreads, @@ -155,7 +155,7 @@ You can skip linkcheck with --skip src/tools/linkchecker" let _guard = builder.msg(Kind::Test, compiler.stage, "Linkcheck", bootstrap_host, bootstrap_host); let _time = helpers::timeit(builder); - builder.run(linkchecker.delay_failure().arg(builder.out.join(host.triple).join("doc"))); + linkchecker.delay_failure().arg(builder.out.join(host.triple).join("doc")).run(builder); } fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -212,9 +212,11 @@ impl Step for HtmlCheck { builder, )); - builder.run( - builder.tool_cmd(Tool::HtmlChecker).delay_failure().arg(builder.doc_out(self.target)), - ); + builder + .tool_cmd(Tool::HtmlChecker) + .delay_failure() + .arg(builder.doc_out(self.target)) + .run(builder); } } @@ -259,7 +261,7 @@ impl Step for Cargotest { .env("RUSTC", builder.rustc(compiler)) .env("RUSTDOC", builder.rustdoc(compiler)); add_rustdoc_cargo_linker_args(&mut cmd, builder, compiler.host, LldThreads::No); - builder.run(cmd.delay_failure()); + cmd.delay_failure().run(builder); } } @@ -460,7 +462,7 @@ impl Miri { let mut cargo = BootstrapCommand::from(cargo); let _guard = builder.msg(Kind::Build, compiler.stage, "miri sysroot", compiler.host, target); - builder.run(&mut cargo); + cargo.run(builder); // # Determine where Miri put its sysroot. // To this end, we run `cargo miri setup --print-sysroot` and capture the output. @@ -473,7 +475,7 @@ impl Miri { String::new() } else { builder.verbose(|| println!("running: {cargo:?}")); - let stdout = builder.run(cargo.capture_stdout()).stdout(); + let stdout = cargo.capture_stdout().run(builder).stdout(); // Output is "<sysroot>\n". let sysroot = stdout.trim_end(); builder.verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}")); @@ -561,7 +563,7 @@ impl Step for Miri { { let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "miri", host, target); let _time = helpers::timeit(builder); - builder.run(&mut cargo); + cargo.run(builder); } // Run it again for mir-opt-level 4 to catch some miscompilations. @@ -583,7 +585,7 @@ impl Step for Miri { target, ); let _time = helpers::timeit(builder); - builder.run(cargo); + cargo.run(builder); } } } @@ -648,11 +650,11 @@ impl Step for CargoMiri { // Finally, pass test-args and run everything. cargo.arg("--").args(builder.config.test_args()); - let cargo = BootstrapCommand::from(cargo); + let mut cargo = BootstrapCommand::from(cargo); { let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "cargo-miri", host, target); let _time = helpers::timeit(builder); - builder.run(cargo); + cargo.run(builder); } } } @@ -753,7 +755,7 @@ impl Step for Clippy { let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "clippy", host, host); // Clippy reports errors if it blessed the outputs - if builder.run(cargo.allow_failure()).is_success() { + if cargo.allow_failure().run(builder).is_success() { // The tests succeeded; nothing to do. return; } @@ -806,7 +808,7 @@ impl Step for RustdocTheme { .env("RUSTC_BOOTSTRAP", "1"); cmd.args(linker_args(builder, self.compiler.host, LldThreads::No)); - builder.run(cmd.delay_failure()); + cmd.delay_failure().run(builder); } } @@ -832,7 +834,7 @@ impl Step for RustdocJSStd { fn run(self, builder: &Builder<'_>) { let nodejs = builder.config.nodejs.as_ref().expect("need nodejs to run rustdoc-js-std tests"); - let mut command = BootstrapCommand::new(nodejs); + let mut command = command(nodejs); command .arg(builder.src.join("src/tools/rustdoc-js/tester.js")) .arg("--crate-name") @@ -866,7 +868,7 @@ impl Step for RustdocJSStd { builder.config.build, self.target, ); - builder.run(command); + command.run(builder); } } @@ -908,12 +910,12 @@ fn get_browser_ui_test_version_inner( npm: &Path, global: bool, ) -> Option<String> { - let mut command = BootstrapCommand::new(npm).capture(); + let mut command = command(npm).capture(); command.arg("list").arg("--parseable").arg("--long").arg("--depth=0"); if global { command.arg("--global"); } - let lines = builder.run(command.allow_failure()).stdout(); + let lines = command.allow_failure().run(builder).stdout(); lines .lines() .find_map(|l| l.split(':').nth(1)?.strip_prefix("browser-ui-test@")) @@ -1085,7 +1087,7 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to } builder.info("tidy check"); - builder.run(cmd.delay_failure()); + cmd.delay_failure().run(builder); builder.info("x.py completions check"); let [bash, zsh, fish, powershell] = ["x.py.sh", "x.py.zsh", "x.py.fish", "x.py.ps1"] @@ -1292,7 +1294,7 @@ impl Step for RunMakeSupport { &[], ); - builder.run(cargo.into_cmd()); + cargo.into_cmd().run(builder); let lib_name = "librun_make_support.rlib"; let lib = builder.tools_dir(self.compiler).join(lib_name); @@ -1804,14 +1806,20 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the } let lldb_exe = builder.config.lldb.clone().unwrap_or_else(|| PathBuf::from("lldb")); - let lldb_version = builder - .run(BootstrapCommand::new(&lldb_exe).capture().allow_failure().arg("--version")) + let lldb_version = command(&lldb_exe) + .capture() + .allow_failure() + .arg("--version") + .run(builder) .stdout_if_ok() .and_then(|v| if v.trim().is_empty() { None } else { Some(v) }); if let Some(ref vers) = lldb_version { cmd.arg("--lldb-version").arg(vers); - let lldb_python_dir = builder - .run(BootstrapCommand::new(&lldb_exe).allow_failure().capture_stdout().arg("-P")) + let lldb_python_dir = command(&lldb_exe) + .allow_failure() + .capture_stdout() + .arg("-P") + .run(builder) .stdout_if_ok() .map(|p| p.lines().next().expect("lldb Python dir not found").to_string()); if let Some(ref dir) = lldb_python_dir { @@ -1869,11 +1877,10 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the let llvm::LlvmResult { llvm_config, .. } = builder.ensure(llvm::Llvm { target: builder.config.build }); if !builder.config.dry_run() { - let llvm_version = builder - .run(BootstrapCommand::new(&llvm_config).capture_stdout().arg("--version")) - .stdout(); + let llvm_version = + builder.run(command(&llvm_config).capture_stdout().arg("--version")).stdout(); let llvm_components = builder - .run(BootstrapCommand::new(&llvm_config).capture_stdout().arg("--components")) + .run(command(&llvm_config).capture_stdout().arg("--components")) .stdout(); // Remove trailing newline from llvm-config output. cmd.arg("--llvm-version") @@ -1893,9 +1900,8 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the // separate compilations. We can add LLVM's library path to the // platform-specific environment variable as a workaround. if !builder.config.dry_run() && suite.ends_with("fulldeps") { - let llvm_libdir = builder - .run(BootstrapCommand::new(&llvm_config).capture_stdout().arg("--libdir")) - .stdout(); + let llvm_libdir = + builder.run(command(&llvm_config).capture_stdout().arg("--libdir")).stdout(); add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cmd); } @@ -2169,9 +2175,11 @@ impl BookTest { compiler.host, ); let _time = helpers::timeit(builder); - let cmd = rustbook_cmd.delay_failure(); - let toolstate = - if builder.run(cmd).is_success() { ToolState::TestPass } else { ToolState::TestFail }; + let toolstate = if rustbook_cmd.delay_failure().run(builder).is_success() { + ToolState::TestPass + } else { + ToolState::TestFail + }; builder.save_toolstate(self.name, toolstate); } @@ -2300,7 +2308,7 @@ impl Step for ErrorIndex { let guard = builder.msg(Kind::Test, compiler.stage, "error-index", compiler.host, compiler.host); let _time = helpers::timeit(builder); - builder.run(tool.capture()); + tool.capture().run(builder); drop(guard); // The tests themselves need to link to std, so make sure it is // available. @@ -2333,7 +2341,7 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) -> if !builder.config.verbose_tests { cmd = cmd.capture(); } - builder.run(cmd).is_success() + cmd.run(builder).is_success() } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -2359,7 +2367,7 @@ impl Step for RustcGuide { let src = builder.src.join(relative_path); let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook).delay_failure(); rustbook_cmd.arg("linkcheck").arg(&src); - let toolstate = if builder.run(rustbook_cmd).is_success() { + let toolstate = if rustbook_cmd.run(builder).is_success() { ToolState::TestPass } else { ToolState::TestFail @@ -2863,19 +2871,19 @@ impl Step for RemoteCopyLibs { // Spawn the emulator and wait for it to come online let tool = builder.tool_exe(Tool::RemoteTestClient); - let mut cmd = BootstrapCommand::new(&tool); + let mut cmd = command(&tool); cmd.arg("spawn-emulator").arg(target.triple).arg(&server).arg(builder.tempdir()); if let Some(rootfs) = builder.qemu_rootfs(target) { cmd.arg(rootfs); } - builder.run(cmd); + cmd.run(builder); // Push all our dylibs to the emulator for f in t!(builder.sysroot_libdir(compiler, target).read_dir()) { let f = t!(f); let name = f.file_name().into_string().unwrap(); if helpers::is_dylib(&name) { - builder.run(BootstrapCommand::new(&tool).arg("push").arg(f.path())); + builder.run(command(&tool).arg("push").arg(f.path())); } } } @@ -2906,22 +2914,20 @@ impl Step for Distcheck { builder.ensure(dist::PlainSourceTarball); builder.ensure(dist::Src); - let mut cmd = BootstrapCommand::new("tar"); + let mut cmd = command("tar"); cmd.arg("-xf") .arg(builder.ensure(dist::PlainSourceTarball).tarball()) .arg("--strip-components=1") .current_dir(&dir); - builder.run(cmd); + cmd.run(builder); builder.run( - BootstrapCommand::new("./configure") + command("./configure") .args(&builder.config.configure_args) .arg("--enable-vendor") .current_dir(&dir), ); builder.run( - BootstrapCommand::new(helpers::make(&builder.config.build.triple)) - .arg("check") - .current_dir(&dir), + command(helpers::make(&builder.config.build.triple)).arg("check").current_dir(&dir), ); // Now make sure that rust-src has all of libstd's dependencies @@ -2930,16 +2936,16 @@ impl Step for Distcheck { let _ = fs::remove_dir_all(&dir); t!(fs::create_dir_all(&dir)); - let mut cmd = BootstrapCommand::new("tar"); + let mut cmd = command("tar"); cmd.arg("-xf") .arg(builder.ensure(dist::Src).tarball()) .arg("--strip-components=1") .current_dir(&dir); - builder.run(cmd); + cmd.run(builder); let toml = dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml"); builder.run( - BootstrapCommand::new(&builder.initial_cargo) + command(&builder.initial_cargo) // Will read the libstd Cargo.toml // which uses the unstable `public-dependency` feature. .env("RUSTC_BOOTSTRAP", "1") @@ -2968,7 +2974,7 @@ impl Step for Bootstrap { // Some tests require cargo submodule to be present. builder.build.update_submodule(Path::new("src/tools/cargo")); - let mut check_bootstrap = BootstrapCommand::new(builder.python()); + let mut check_bootstrap = command(builder.python()); check_bootstrap .args(["-m", "unittest", "bootstrap_test.py"]) .env("BUILD_DIR", &builder.out) @@ -2976,9 +2982,9 @@ impl Step for Bootstrap { .current_dir(builder.src.join("src/bootstrap/")); // NOTE: we intentionally don't pass test_args here because the args for unittest and cargo test are mutually incompatible. // Use `python -m unittest` manually if you want to pass arguments. - builder.run(check_bootstrap.delay_failure()); + check_bootstrap.delay_failure().run(builder); - let mut cmd = BootstrapCommand::new(&builder.initial_cargo); + let mut cmd = command(&builder.initial_cargo); cmd.arg("test") .args(["--features", "bootstrap-self-test"]) .current_dir(builder.src.join("src/bootstrap")) @@ -3053,7 +3059,7 @@ impl Step for TierCheck { self.compiler.host, self.compiler.host, ); - builder.run(BootstrapCommand::from(cargo).delay_failure()); + BootstrapCommand::from(cargo).delay_failure().run(builder); } } @@ -3129,7 +3135,7 @@ impl Step for RustInstaller { return; } - let mut cmd = BootstrapCommand::new(builder.src.join("src/tools/rust-installer/test.sh")); + let mut cmd = command(builder.src.join("src/tools/rust-installer/test.sh")); let tmpdir = testdir(builder, compiler.host).join("rust-installer"); let _ = std::fs::remove_dir_all(&tmpdir); let _ = std::fs::create_dir_all(&tmpdir); @@ -3138,7 +3144,7 @@ impl Step for RustInstaller { cmd.env("CARGO", &builder.initial_cargo); cmd.env("RUSTC", &builder.initial_rustc); cmd.env("TMP_DIR", &tmpdir); - builder.run(cmd.delay_failure()); + cmd.delay_failure().run(builder); } fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { @@ -3332,7 +3338,7 @@ impl Step for CodegenCranelift { .arg("testsuite.extended_sysroot"); cargo.args(builder.config.test_args()); - builder.run(cargo.into_cmd()); + cargo.into_cmd().run(builder); } } @@ -3457,6 +3463,6 @@ impl Step for CodegenGCC { .arg("--std-tests"); cargo.args(builder.config.test_args()); - builder.run(cargo.into_cmd()); + cargo.into_cmd().run(builder); } } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 5fb282db90a..ad92a01bce7 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -8,7 +8,7 @@ use crate::core::builder; use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; use crate::utils::channel::GitInfo; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::{command, BootstrapCommand}; use crate::utils::helpers::{add_dylib_path, exe, t}; use crate::Compiler; use crate::Mode; @@ -438,7 +438,7 @@ impl ErrorIndex { // for rustc_private and libLLVM.so, and `sysroot_lib` for libstd, etc. let host = builder.config.build; let compiler = builder.compiler_for(builder.top_stage, host, host); - let mut cmd = BootstrapCommand::new(builder.ensure(ErrorIndex { compiler })); + let mut cmd = command(builder.ensure(ErrorIndex { compiler })); let mut dylib_paths = builder.rustc_lib_paths(compiler); dylib_paths.push(PathBuf::from(&builder.sysroot_libdir(compiler, compiler.host))); add_dylib_path(dylib_paths, &mut cmd); @@ -602,7 +602,7 @@ impl Step for Rustdoc { &self.compiler.host, &target, ); - builder.run(cargo.into_cmd()); + cargo.into_cmd().run(builder); // Cargo adds a number of paths to the dylib search path on windows, which results in // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool" @@ -857,7 +857,7 @@ impl Step for LlvmBitcodeLinker { &self.extra_features, ); - builder.run(cargo.into_cmd()); + cargo.into_cmd().run(builder); let tool_out = builder .cargo_out(self.compiler, Mode::ToolRustc, self.target) @@ -912,21 +912,20 @@ impl Step for LibcxxVersionTool { } let compiler = builder.cxx(self.target).unwrap(); - let mut cmd = BootstrapCommand::new(compiler); + let mut cmd = command(compiler); cmd.arg("-o") .arg(&executable) .arg(builder.src.join("src/tools/libcxx-version/main.cpp")); - builder.run(cmd); + cmd.run(builder); if !executable.exists() { panic!("Something went wrong. {} is not present", executable.display()); } } - let version_output = - builder.run(BootstrapCommand::new(executable).capture_stdout()).stdout(); + let version_output = command(executable).capture_stdout().run(builder).stdout(); let version_str = version_output.split_once("version:").unwrap().1; let version = version_str.trim().parse::<usize>().unwrap(); @@ -1050,7 +1049,7 @@ impl<'a> Builder<'a> { /// Gets a `BootstrapCommand` which is ready to run `tool` in `stage` built for /// `host`. pub fn tool_cmd(&self, tool: Tool) -> BootstrapCommand { - let mut cmd = BootstrapCommand::new(self.tool_exe(tool)); + let mut cmd = command(self.tool_exe(tool)); let compiler = self.compiler(0, self.config.build); let host = &compiler.host; // Prepares the `cmd` provided to be able to run the `compiler` provided. diff --git a/src/bootstrap/src/core/build_steps/vendor.rs b/src/bootstrap/src/core/build_steps/vendor.rs index 0b999a24a1f..62342ee4792 100644 --- a/src/bootstrap/src/core/build_steps/vendor.rs +++ b/src/bootstrap/src/core/build_steps/vendor.rs @@ -1,5 +1,5 @@ use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::command; use std::path::{Path, PathBuf}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] @@ -27,7 +27,7 @@ impl Step for Vendor { } fn run(self, builder: &Builder<'_>) -> Self::Output { - let mut cmd = BootstrapCommand::new(&builder.initial_cargo); + let mut cmd = command(&builder.initial_cargo); cmd.arg("vendor"); if self.versioned_dirs { @@ -59,6 +59,6 @@ impl Step for Vendor { cmd.current_dir(self.root_dir); - builder.run(cmd); + cmd.run(builder); } } diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs index 4da912994c3..65cc1fa7478 100644 --- a/src/bootstrap/src/core/builder.rs +++ b/src/bootstrap/src/core/builder.rs @@ -8,6 +8,7 @@ use std::fs; use std::hash::Hash; use std::ops::Deref; use std::path::{Path, PathBuf}; +use std::sync::LazyLock; use std::time::{Duration, Instant}; use crate::core::build_steps::tool::{self, SourceType}; @@ -23,12 +24,10 @@ use crate::utils::helpers::{check_cfg_arg, libdir, linker_flags, t, LldThreads}; use crate::EXTRA_CHECK_CFGS; use crate::{Build, CLang, Crate, DocTests, GitRepo, Mode}; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::{command, BootstrapCommand}; pub use crate::Compiler; use clap::ValueEnum; -// FIXME: replace with std::lazy after it gets stabilized and reaches beta -use once_cell::sync::Lazy; #[cfg(test)] mod tests; @@ -498,7 +497,7 @@ impl StepDescription { enum ReallyDefault<'a> { Bool(bool), - Lazy(Lazy<bool, Box<dyn Fn() -> bool + 'a>>), + Lazy(LazyLock<bool, Box<dyn Fn() -> bool + 'a>>), } pub struct ShouldRun<'a> { @@ -529,7 +528,7 @@ impl<'a> ShouldRun<'a> { } pub fn lazy_default_condition(mut self, lazy_cond: Box<dyn Fn() -> bool + 'a>) -> Self { - self.is_really_default = ReallyDefault::Lazy(Lazy::new(lazy_cond)); + self.is_really_default = ReallyDefault::Lazy(LazyLock::new(lazy_cond)); self } @@ -1254,7 +1253,7 @@ impl<'a> Builder<'a> { if run_compiler.stage == 0 { // `ensure(Clippy { stage: 0 })` *builds* clippy with stage0, it doesn't use the beta clippy. let cargo_clippy = self.build.config.download_clippy(); - let mut cmd = BootstrapCommand::new(cargo_clippy); + let mut cmd = command(cargo_clippy); cmd.env("CARGO", &self.initial_cargo); return cmd; } @@ -1273,7 +1272,7 @@ impl<'a> Builder<'a> { let mut dylib_path = helpers::dylib_path(); dylib_path.insert(0, self.sysroot(run_compiler).join("lib")); - let mut cmd = BootstrapCommand::new(cargo_clippy); + let mut cmd = command(cargo_clippy); cmd.env(helpers::dylib_path_var(), env::join_paths(&dylib_path).unwrap()); cmd.env("CARGO", &self.initial_cargo); cmd @@ -1295,7 +1294,7 @@ impl<'a> Builder<'a> { extra_features: Vec::new(), }); // Invoke cargo-miri, make sure it can find miri and cargo. - let mut cmd = BootstrapCommand::new(cargo_miri); + let mut cmd = command(cargo_miri); cmd.env("MIRI", &miri); cmd.env("CARGO", &self.initial_cargo); // Need to add the `run_compiler` libs. Those are the libs produces *by* `build_compiler`, @@ -1311,7 +1310,7 @@ impl<'a> Builder<'a> { } pub fn rustdoc_cmd(&self, compiler: Compiler) -> BootstrapCommand { - let mut cmd = BootstrapCommand::new(self.bootstrap_out.join("rustdoc")); + let mut cmd = command(self.bootstrap_out.join("rustdoc")); cmd.env("RUSTC_STAGE", compiler.stage.to_string()) .env("RUSTC_SYSROOT", self.sysroot(compiler)) // Note that this is *not* the sysroot_libdir because rustdoc must be linked @@ -1365,7 +1364,7 @@ impl<'a> Builder<'a> { cargo = self.cargo_miri_cmd(compiler); cargo.arg("miri").arg(subcmd); } else { - cargo = BootstrapCommand::new(&self.initial_cargo); + cargo = command(&self.initial_cargo); cargo.arg(cmd); } @@ -1918,9 +1917,8 @@ impl<'a> Builder<'a> { // platform-specific environment variable as a workaround. if mode == Mode::ToolRustc || mode == Mode::Codegen { if let Some(llvm_config) = self.llvm_config(target) { - let llvm_libdir = self - .run(BootstrapCommand::new(llvm_config).capture_stdout().arg("--libdir")) - .stdout(); + let llvm_libdir = + command(llvm_config).capture_stdout().arg("--libdir").run(self).stdout(); add_link_lib_path(vec![llvm_libdir.trim().into()], &mut cargo); } } @@ -2527,7 +2525,7 @@ impl Cargo { if let Some(target_linker) = builder.linker(target) { let target = crate::envify(&target.triple); - self.command.env(&format!("CARGO_TARGET_{target}_LINKER"), target_linker); + self.command.env(format!("CARGO_TARGET_{target}_LINKER"), target_linker); } // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not // `linker_args` here. diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index c35398e2eb7..a7f4bb0cf14 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -11,7 +11,7 @@ use std::{ use build_helper::ci::CiEnv; use xz2::bufread::XzDecoder; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::{command, BootstrapCommand}; use crate::utils::helpers::hex_encode; use crate::utils::helpers::{check_run, exe, move_file, program_out_of_date}; use crate::{t, Config}; @@ -212,7 +212,7 @@ impl Config { fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) { println!("downloading {url}"); // Try curl. If that fails and we are on windows, fallback to PowerShell. - let mut curl = BootstrapCommand::new("curl"); + let mut curl = command("curl"); curl.args([ "-y", "30", diff --git a/src/bootstrap/src/core/metadata.rs b/src/bootstrap/src/core/metadata.rs index 49d17107125..b18da844014 100644 --- a/src/bootstrap/src/core/metadata.rs +++ b/src/bootstrap/src/core/metadata.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use serde_derive::Deserialize; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::command; use crate::{t, Build, Crate}; /// For more information, see the output of @@ -70,7 +70,7 @@ pub fn build(build: &mut Build) { /// particular crate (e.g., `x build sysroot` to build library/sysroot). fn workspace_members(build: &Build) -> Vec<Package> { let collect_metadata = |manifest_path| { - let mut cargo = BootstrapCommand::new(&build.initial_cargo); + let mut cargo = command(&build.initial_cargo); cargo // Will read the libstd Cargo.toml // which uses the unstable `public-dependency` feature. @@ -81,7 +81,7 @@ fn workspace_members(build: &Build) -> Vec<Package> { .arg("--no-deps") .arg("--manifest-path") .arg(build.src.join(manifest_path)); - let metadata_output = build.run(cargo.capture_stdout().run_always()).stdout(); + let metadata_output = cargo.capture_stdout().run_always().run(build).stdout(); let Output { packages, .. } = t!(serde_json::from_str(&metadata_output)); packages }; diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs index 78862ccb8cd..9995da3a1e5 100644 --- a/src/bootstrap/src/core/sanity.rs +++ b/src/bootstrap/src/core/sanity.rs @@ -23,7 +23,7 @@ use std::collections::HashSet; use crate::builder::Kind; use crate::core::config::Target; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::command; use crate::Build; pub struct Finder { @@ -209,9 +209,7 @@ than building it. #[cfg(not(feature = "bootstrap-self-test"))] let stage0_supported_target_list: HashSet<String> = crate::utils::helpers::output( - &mut BootstrapCommand::new(&build.config.initial_rustc) - .args(["--print", "target-list"]) - .command, + &mut command(&build.config.initial_rustc).args(["--print", "target-list"]).command, ) .lines() .map(|s| s.to_string()) @@ -354,8 +352,7 @@ than building it. // There are three builds of cmake on windows: MSVC, MinGW, and // Cygwin. The Cygwin build does not have generators for Visual // Studio, so detect that here and error. - let out = - build.run(BootstrapCommand::new("cmake").capture_stdout().arg("--help")).stdout(); + let out = command("cmake").capture_stdout().arg("--help").run(build).stdout(); if !out.contains("Visual Studio") { panic!( " diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index ffdd9bc885a..f16afb29796 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -41,7 +41,7 @@ use crate::core::builder::Kind; use crate::core::config::{flags, LldMode}; use crate::core::config::{DryRun, Target}; use crate::core::config::{LlvmLibunwind, TargetSelection}; -use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, CommandOutput}; +use crate::utils::exec::{command, BehaviorOnFailure, BootstrapCommand, CommandOutput}; use crate::utils::helpers::{self, dir_is_empty, exe, libdir, mtime, output, symlink_dir}; mod core; @@ -510,9 +510,10 @@ impl Build { } println!("Updating submodule {}", relative_path.display()); - self.run( - helpers::git(Some(&self.src)).args(["submodule", "-q", "sync"]).arg(relative_path), - ); + helpers::git(Some(&self.src)) + .args(["submodule", "-q", "sync"]) + .arg(relative_path) + .run(self); // Try passing `--progress` to start, then run git again without if that fails. let update = |progress: bool| { @@ -548,23 +549,25 @@ impl Build { }; // NOTE: doesn't use `try_run` because this shouldn't print an error if it fails. if !update(true).command.status().map_or(false, |status| status.success()) { - self.run(update(false)); + update(false).run(self); } // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error). // diff-index reports the modifications through the exit status - let has_local_modifications = self - .run(submodule_git().allow_failure().args(["diff-index", "--quiet", "HEAD"])) + let has_local_modifications = submodule_git() + .allow_failure() + .args(["diff-index", "--quiet", "HEAD"]) + .run(self) .is_failure(); if has_local_modifications { - self.run(submodule_git().args(["stash", "push"])); + submodule_git().args(["stash", "push"]).run(self); } - self.run(submodule_git().args(["reset", "-q", "--hard"])); - self.run(submodule_git().args(["clean", "-qdfx"])); + submodule_git().args(["reset", "-q", "--hard"]).run(self); + submodule_git().args(["clean", "-qdfx"]).run(self); if has_local_modifications { - self.run(submodule_git().args(["stash", "pop"])); + submodule_git().args(["stash", "pop"]).run(self); } } @@ -575,14 +578,12 @@ impl Build { if !self.config.submodules(self.rust_info()) { return; } - let output = self - .run( - helpers::git(Some(&self.src)) - .capture() - .args(["config", "--file"]) - .arg(self.config.src.join(".gitmodules")) - .args(["--get-regexp", "path"]), - ) + let output = helpers::git(Some(&self.src)) + .capture() + .args(["config", "--file"]) + .arg(self.config.src.join(".gitmodules")) + .args(["--get-regexp", "path"]) + .run(self) .stdout(); for line in output.lines() { // Look for `submodule.$name.path = $path` @@ -860,16 +861,14 @@ impl Build { if let Some(s) = target_config.and_then(|c| c.llvm_filecheck.as_ref()) { s.to_path_buf() } else if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { - let llvm_bindir = - self.run(BootstrapCommand::new(s).capture_stdout().arg("--bindir")).stdout(); + let llvm_bindir = command(s).capture_stdout().arg("--bindir").run(self).stdout(); let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", target)); if filecheck.exists() { filecheck } else { // On Fedora the system LLVM installs FileCheck in the // llvm subdirectory of the libdir. - let llvm_libdir = - self.run(BootstrapCommand::new(s).capture_stdout().arg("--libdir")).stdout(); + let llvm_libdir = command(s).capture_stdout().arg("--libdir").run(self).stdout(); let lib_filecheck = Path::new(llvm_libdir.trim()).join("llvm").join(exe("FileCheck", target)); if lib_filecheck.exists() { @@ -935,8 +934,7 @@ impl Build { /// Execute a command and return its output. /// This method should be used for all command executions in bootstrap. - fn run<C: AsMut<BootstrapCommand>>(&self, mut command: C) -> CommandOutput { - let command = command.as_mut(); + fn run(&self, command: &mut BootstrapCommand) -> CommandOutput { if self.config.dry_run() && !command.run_always { return CommandOutput::default(); } @@ -1496,18 +1494,17 @@ impl Build { // Figure out how many merge commits happened since we branched off master. // That's our beta number! // (Note that we use a `..` range, not the `...` symmetric difference.) - self.run( - helpers::git(Some(&self.src)) - .capture() - .arg("rev-list") - .arg("--count") - .arg("--merges") - .arg(format!( - "refs/remotes/origin/{}..HEAD", - self.config.stage0_metadata.config.nightly_branch - )), - ) - .stdout() + helpers::git(Some(&self.src)) + .capture() + .arg("rev-list") + .arg("--count") + .arg("--merges") + .arg(format!( + "refs/remotes/origin/{}..HEAD", + self.config.stage0_metadata.config.nightly_branch + )) + .run(self) + .stdout() }); let n = count.trim().parse().unwrap(); self.prerelease_version.set(Some(n)); diff --git a/src/bootstrap/src/utils/cache.rs b/src/bootstrap/src/utils/cache.rs index e18dcbb47be..d60b54dc703 100644 --- a/src/bootstrap/src/utils/cache.rs +++ b/src/bootstrap/src/utils/cache.rs @@ -9,10 +9,7 @@ use std::marker::PhantomData; use std::mem; use std::ops::Deref; use std::path::PathBuf; -use std::sync::Mutex; - -// FIXME: replace with std::lazy after it gets stabilized and reaches beta -use once_cell::sync::Lazy; +use std::sync::{LazyLock, Mutex}; use crate::core::builder::Step; @@ -196,7 +193,7 @@ impl Interner { } } -pub static INTERNER: Lazy<Interner> = Lazy::new(Interner::default); +pub static INTERNER: LazyLock<Interner> = LazyLock::new(Interner::default); /// This is essentially a `HashMap` which allows storing any type in its input and /// any type in its output. It is a write-once cache; values are never evicted, diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs index b80df545202..d6fa7fc0bbe 100644 --- a/src/bootstrap/src/utils/cc_detect.rs +++ b/src/bootstrap/src/utils/cc_detect.rs @@ -26,7 +26,7 @@ use std::path::{Path, PathBuf}; use std::{env, iter}; use crate::core::config::TargetSelection; -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::{command, BootstrapCommand}; use crate::{Build, CLang, GitRepo}; // The `cc` crate doesn't provide a way to obtain a path to the detected archiver, @@ -183,14 +183,14 @@ fn default_compiler( } let cmd = BootstrapCommand::from(c.to_command()); - let output = build.run(cmd.capture_stdout().arg("--version")).stdout(); + let output = cmd.capture_stdout().arg("--version").run(build).stdout(); let i = output.find(" 4.")?; match output[i + 3..].chars().next().unwrap() { '0'..='6' => {} _ => return None, } let alternative = format!("e{gnu_compiler}"); - if build.run(BootstrapCommand::new(&alternative).capture()).is_success() { + if command(&alternative).capture().run(build).is_success() { Some(PathBuf::from(alternative)) } else { None diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index 086d10c6351..ba963f52dc2 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -1,3 +1,4 @@ +use crate::Build; use std::ffi::OsStr; use std::path::Path; use std::process::{Command, CommandArgs, CommandEnvs, ExitStatus, Output, Stdio}; @@ -108,14 +109,17 @@ impl BootstrapCommand { self } + #[must_use] pub fn delay_failure(self) -> Self { Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self } } + #[must_use] pub fn fail_fast(self) -> Self { Self { failure_behavior: BehaviorOnFailure::Exit, ..self } } + #[must_use] pub fn allow_failure(self) -> Self { Self { failure_behavior: BehaviorOnFailure::Ignore, ..self } } @@ -126,21 +130,20 @@ impl BootstrapCommand { } /// Capture all output of the command, do not print it. + #[must_use] pub fn capture(self) -> Self { Self { stdout: OutputMode::Capture, stderr: OutputMode::Capture, ..self } } /// Capture stdout of the command, do not print it. + #[must_use] pub fn capture_stdout(self) -> Self { Self { stdout: OutputMode::Capture, ..self } } -} -/// This implementation exists to make it possible to pass both [BootstrapCommand] and -/// `&mut BootstrapCommand` to `Build.run()`. -impl AsMut<BootstrapCommand> for BootstrapCommand { - fn as_mut(&mut self) -> &mut BootstrapCommand { - self + /// Run the command, returning its output. + pub fn run(&mut self, builder: &Build) -> CommandOutput { + builder.run(self) } } @@ -164,6 +167,13 @@ enum CommandStatus { DidNotStart, } +/// Create a new BootstrapCommand. This is a helper function to make command creation +/// shorter than `BootstrapCommand::new`. +#[must_use] +pub fn command<S: AsRef<OsStr>>(program: S) -> BootstrapCommand { + BootstrapCommand::new(program) +} + /// Represents the output of an executed process. #[allow(unused)] pub struct CommandOutput { @@ -173,10 +183,12 @@ pub struct CommandOutput { } impl CommandOutput { + #[must_use] pub fn did_not_start() -> Self { Self { status: CommandStatus::DidNotStart, stdout: vec![], stderr: vec![] } } + #[must_use] pub fn is_success(&self) -> bool { match self.status { CommandStatus::Finished(status) => status.success(), @@ -184,10 +196,12 @@ impl CommandOutput { } } + #[must_use] pub fn is_failure(&self) -> bool { !self.is_success() } + #[must_use] pub fn status(&self) -> Option<ExitStatus> { match self.status { CommandStatus::Finished(status) => Some(status), @@ -195,14 +209,17 @@ impl CommandOutput { } } + #[must_use] pub fn stdout(&self) -> String { String::from_utf8(self.stdout.clone()).expect("Cannot parse process stdout as UTF-8") } + #[must_use] pub fn stdout_if_ok(&self) -> Option<String> { if self.is_success() { Some(self.stdout()) } else { None } } + #[must_use] pub fn stderr(&self) -> String { String::from_utf8(self.stderr.clone()).expect("Cannot parse process stderr as UTF-8") } diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index 2575b7939b4..5dd3ba96786 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -47,7 +47,7 @@ macro_rules! t { } }; } -use crate::utils::exec::BootstrapCommand; +use crate::utils::exec::{command, BootstrapCommand}; pub use t; pub fn exe(name: &str, target: TargetSelection) -> String { @@ -369,9 +369,9 @@ fn lld_flag_no_threads(builder: &Builder<'_>, lld_mode: LldMode, is_windows: boo let (windows_flag, other_flag) = LLD_NO_THREADS.get_or_init(|| { let newer_version = match lld_mode { LldMode::External => { - let mut cmd = BootstrapCommand::new("lld").capture_stdout(); + let mut cmd = command("lld").capture_stdout(); cmd.arg("-flavor").arg("ld").arg("--version"); - let out = builder.run(cmd).stdout(); + let out = cmd.run(builder).stdout(); match (out.find(char::is_numeric), out.find('.')) { (Some(b), Some(e)) => out.as_str()[b..e].parse::<i32>().ok().unwrap_or(14) > 10, _ => true, @@ -499,10 +499,10 @@ pub fn check_cfg_arg(name: &str, values: Option<&[&str]>) -> String { /// Whenever a git invocation is needed, this function should be preferred over /// manually building a git `BootstrapCommand`. This approach allows us to manage /// bootstrap-specific needs/hacks from a single source, rather than applying them on next to every -/// `BootstrapCommand::new("git")`, which is painful to ensure that the required change is applied +/// git command creation, which is painful to ensure that the required change is applied /// on each one of them correctly. pub fn git(source_dir: Option<&Path>) -> BootstrapCommand { - let mut git = BootstrapCommand::new("git"); + let mut git = command("git"); if let Some(source_dir) = source_dir { git.current_dir(source_dir); diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs index 1a35dc1fd38..4f0104e4bba 100644 --- a/src/bootstrap/src/utils/tarball.rs +++ b/src/bootstrap/src/utils/tarball.rs @@ -379,7 +379,7 @@ impl<'a> Tarball<'a> { cmd.args(["--override-file-mtime", timestamp.trim()]); } - self.builder.run(cmd); + cmd.run(self.builder); // Ensure there are no symbolic links in the tarball. In particular, // rustup-toolchain-install-master and most versions of Windows can't handle symbolic links. diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1-threads.md b/src/doc/rustc/src/platform-support/wasm32-wasip1-threads.md index 2b3d15e93c8..c3eda26ca8e 100644 --- a/src/doc/rustc/src/platform-support/wasm32-wasip1-threads.md +++ b/src/doc/rustc/src/platform-support/wasm32-wasip1-threads.md @@ -107,7 +107,7 @@ flag, for example: Users need to install or built wasi-sdk since release 20.0 https://github.com/WebAssembly/wasi-sdk/releases/tag/wasi-sdk-20 -and specify path to *wasi-root* `.cargo/config.toml` +and specify path to *wasi-root* `config.toml` ```toml [target.wasm32-wasip1-threads] diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b5660cd8492..aa596897fc4 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1404,8 +1404,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( let mut predicates = tcx.explicit_predicates_of(assoc_item.def_id).predicates; if let ty::TraitContainer = assoc_item.container { - let bounds = - tcx.explicit_item_bounds(assoc_item.def_id).instantiate_identity_iter_copied(); + let bounds = tcx.explicit_item_bounds(assoc_item.def_id).iter_identity_copied(); predicates = tcx.arena.alloc_from_iter(bounds.chain(predicates.iter().copied())); } let mut generics = clean_ty_generics( diff --git a/src/tools/cargo b/src/tools/cargo -Subproject a515d463427b3912ec0365d106791f88c1c14e1 +Subproject 154fdac39ae9629954e19e9986fd2cf2cdd8d96 diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 42b10f69c0c..f206b2ceebc 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -330,7 +330,8 @@ fn check_terminator<'tcx>( target: _, unwind: _, fn_span: _, - } => { + } + | TerminatorKind::TailCall { func, args, fn_span: _ } => { let fn_ty = func.ty(body, tcx); if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() { if !is_const_fn(tcx, fn_def_id, msrv) { diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index e5d20564196..acaeb93f44a 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -99,7 +99,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' for (predicate, _span) in cx .tcx .explicit_item_super_predicates(def_id) - .instantiate_identity_iter_copied() + .iter_identity_copied() { match predicate.kind().skip_binder() { // For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed index 7657ef470c5..c76f7a81843 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.fixed +++ b/src/tools/clippy/tests/ui/or_fun_call.fixed @@ -98,7 +98,7 @@ fn or_fun_call() { let opt = Some(1); let hello = "Hello"; - let _ = opt.ok_or(format!("{} world.", hello)); + let _ = opt.ok_or_else(|| format!("{} world.", hello)); // index let map = HashMap::<u64, u64>::new(); diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr index b5a30f29923..3070db22fc5 100644 --- a/src/tools/clippy/tests/ui/or_fun_call.stderr +++ b/src/tools/clippy/tests/ui/or_fun_call.stderr @@ -100,6 +100,12 @@ error: use of `unwrap_or` to construct default value LL | let _ = stringy.unwrap_or(String::new()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` +error: use of `ok_or` followed by a function call + --> tests/ui/or_fun_call.rs:101:17 + | +LL | let _ = opt.ok_or(format!("{} world.", hello)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))` + error: use of `unwrap_or` followed by a function call --> tests/ui/or_fun_call.rs:105:21 | @@ -190,5 +196,5 @@ error: use of `unwrap_or_else` to construct default value LL | let _ = stringy.unwrap_or_else(String::new); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()` -error: aborting due to 31 previous errors +error: aborting due to 32 previous errors diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 25d559d789f..e90d3732ca5 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -51917e2e69702e5752bce6a4f3bfd285d0f4ae39 +99b7134389e9766462601a2fc4013840b9d31745 diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 02bfd6ec815..adb84593561 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1455,7 +1455,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ecx: &mut InterpCx<'tcx, Self>, frame: Frame<'tcx, Provenance, FrameExtra<'tcx>>, unwinding: bool, - ) -> InterpResult<'tcx, StackPopJump> { + ) -> InterpResult<'tcx, ReturnAction> { if frame.extra.is_user_relevant { // All that we store is whether or not the frame we just removed is local, so now we // have no idea where the next topmost local frame is. So we recompute it. diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index ef832f5bbbd..306dce5edcd 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -113,7 +113,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, mut extra: FrameExtra<'tcx>, unwinding: bool, - ) -> InterpResult<'tcx, StackPopJump> { + ) -> InterpResult<'tcx, ReturnAction> { let this = self.eval_context_mut(); trace!("handle_stack_pop_unwind(extra = {:?}, unwinding = {})", extra, unwinding); @@ -150,9 +150,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; // We pushed a new stack frame, the engine should not do any jumping now! - Ok(StackPopJump::NoJump) + Ok(ReturnAction::NoJump) } else { - Ok(StackPopJump::Normal) + Ok(ReturnAction::Normal) } } diff --git a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.rs b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.rs new file mode 100644 index 00000000000..5f00dbf2573 --- /dev/null +++ b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.rs @@ -0,0 +1,10 @@ +//@error-in-other-file: Undefined Behavior: calling a function with calling convention C using calling convention Rust +#![feature(explicit_tail_calls)] +#![allow(incomplete_features)] + +fn main() { + let f = unsafe { std::mem::transmute::<extern "C" fn(), fn()>(f) }; + become f(); +} + +extern "C" fn f() {} diff --git a/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr new file mode 100644 index 00000000000..b157e9f0b21 --- /dev/null +++ b/src/tools/miri/tests/fail/tail_calls/cc-mismatch.stderr @@ -0,0 +1,25 @@ +error: Undefined Behavior: calling a function with calling convention C using calling convention Rust + --> RUSTLIB/core/src/ops/function.rs:LL:CC + | +LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ calling a function with calling convention C using calling convention Rust + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `<fn() as std::ops::FnOnce<()>>::call_once - shim(fn())` at RUSTLIB/core/src/ops/function.rs:LL:CC + = note: inside `std::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once` at RUSTLIB/core/src/ops/function.rs:LL:CC + = note: inside `std::panicking::r#try::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::r#try::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::panicking::r#try::do_call::<{closure@std::rt::lang_start_internal::{closure#2}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panicking::r#try::<isize, {closure@std::rt::lang_start_internal::{closure#2}}>` at RUSTLIB/std/src/panicking.rs:LL:CC + = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#2}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC + = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC + = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.rs b/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.rs new file mode 100644 index 00000000000..6df132d3255 --- /dev/null +++ b/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.rs @@ -0,0 +1,17 @@ +#![feature(explicit_tail_calls)] +#![allow(incomplete_features)] + +fn main() { + // FIXME(explicit_tail_calls): + // the error should point to `become g(x)`, + // but tail calls mess up the backtrace it seems like... + f(0); + //~^ error: Undefined Behavior: calling a function with argument of type i32 passing data of type u32 +} + +fn f(x: u32) { + let g = unsafe { std::mem::transmute::<fn(i32), fn(u32)>(g) }; + become g(x); +} + +fn g(_: i32) {} diff --git a/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.stderr b/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.stderr new file mode 100644 index 00000000000..8823ab9b970 --- /dev/null +++ b/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.stderr @@ -0,0 +1,17 @@ +error: Undefined Behavior: calling a function with argument of type i32 passing data of type u32 + --> $DIR/signature-mismatch-arg.rs:LL:CC + | +LL | f(0); + | ^^^^ calling a function with argument of type i32 passing data of type u32 + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets + = help: if you think this code should be accepted anyway, please report an issue with Miri + = note: BACKTRACE: + = note: inside `main` at $DIR/signature-mismatch-arg.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass/intptrcast.rs b/src/tools/miri/tests/pass/intptrcast.rs index 4e9fa12c181..fb1a1dfae5d 100644 --- a/src/tools/miri/tests/pass/intptrcast.rs +++ b/src/tools/miri/tests/pass/intptrcast.rs @@ -35,7 +35,7 @@ fn cast_dangling() { fn format() { // Pointer string formatting! We can't check the output as it changes when libstd changes, // but we can make sure Miri does not error. - format!("{:?}", &mut 13 as *mut _); + let _ = format!("{:?}", &mut 13 as *mut _); } fn transmute() { @@ -52,7 +52,7 @@ fn ptr_bitops1() { let one = bytes.as_ptr().wrapping_offset(1); let three = bytes.as_ptr().wrapping_offset(3); let res = (one as usize) | (three as usize); - format!("{}", res); + let _ = format!("{}", res); } fn ptr_bitops2() { diff --git a/src/tools/miri/tests/pass/packed_struct.rs b/src/tools/miri/tests/pass/packed_struct.rs index b86235e0c67..039eb5adef0 100644 --- a/src/tools/miri/tests/pass/packed_struct.rs +++ b/src/tools/miri/tests/pass/packed_struct.rs @@ -138,7 +138,7 @@ fn test_derive() { assert_eq!(x.partial_cmp(&y).unwrap(), x.cmp(&y)); x.hash(&mut DefaultHasher::new()); P::default(); - format!("{:?}", x); + let _ = format!("{:?}", x); } fn main() { diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 35980fad15d..16d3e8cab30 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -202,7 +202,7 @@ fn test_errors() { // Opening a non-existing file should fail with a "not found" error. assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind()); // Make sure we can also format this. - format!("{0}: {0:?}", File::open(&path).unwrap_err()); + let _ = format!("{0}: {0:?}", File::open(&path).unwrap_err()); // Removing a non-existing file should fail with a "not found" error. assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind()); // Reading the metadata of a non-existing file should fail with a "not found" error. @@ -301,5 +301,5 @@ fn test_from_raw_os_error() { let error = Error::from_raw_os_error(code); assert!(matches!(error.kind(), ErrorKind::Uncategorized)); // Make sure we can also format this. - format!("{error:?}"); + let _ = format!("{error:?}"); } diff --git a/src/tools/miri/tests/pass/shims/io.rs b/src/tools/miri/tests/pass/shims/io.rs index d20fc75b793..420ef95a0cb 100644 --- a/src/tools/miri/tests/pass/shims/io.rs +++ b/src/tools/miri/tests/pass/shims/io.rs @@ -15,5 +15,5 @@ fn main() { panic!("unsupported OS") }; let err = io::Error::from_raw_os_error(raw_os_error); - format!("{err}: {err:?}"); + let _ = format!("{err}: {err:?}"); } diff --git a/src/tools/miri/tests/pass/tail_call.rs b/src/tools/miri/tests/pass/tail_call.rs new file mode 100644 index 00000000000..f6200706398 --- /dev/null +++ b/src/tools/miri/tests/pass/tail_call.rs @@ -0,0 +1,39 @@ +#![allow(incomplete_features)] +#![feature(explicit_tail_calls)] + +fn main() { + assert_eq!(factorial(10), 3_628_800); + assert_eq!(mutually_recursive_identity(1000), 1000); +} + +fn factorial(n: u32) -> u32 { + fn factorial_acc(n: u32, acc: u32) -> u32 { + match n { + 0 => acc, + _ => become factorial_acc(n - 1, acc * n), + } + } + + factorial_acc(n, 1) +} + +// this is of course very silly, but we need to demonstrate mutual recursion somehow so... +fn mutually_recursive_identity(x: u32) -> u32 { + fn switch(src: u32, tgt: u32) -> u32 { + match src { + 0 => tgt, + _ if src % 7 == 0 => become advance_with_extra_steps(src, tgt), + _ => become advance(src, tgt), + } + } + + fn advance(src: u32, tgt: u32) -> u32 { + become switch(src - 1, tgt + 1) + } + + fn advance_with_extra_steps(src: u32, tgt: u32) -> u32 { + become advance(src, tgt) + } + + switch(x, 0) +} diff --git a/src/tools/miri/tests/pass/vecdeque.rs b/src/tools/miri/tests/pass/vecdeque.rs index 77c4ca5a04e..9153c428e18 100644 --- a/src/tools/miri/tests/pass/vecdeque.rs +++ b/src/tools/miri/tests/pass/vecdeque.rs @@ -31,8 +31,8 @@ fn main() { } // Regression test for Debug impl's - format!("{:?} {:?}", dst, dst.iter()); - format!("{:?}", VecDeque::<u32>::new().iter()); + let _ = format!("{:?} {:?}", dst, dst.iter()); + let _ = format!("{:?}", VecDeque::<u32>::new().iter()); for a in dst { assert_eq!(*a, 2); diff --git a/src/tools/run-make-support/src/diff/mod.rs b/src/tools/run-make-support/src/diff/mod.rs index 3e0bdc1c6f6..24fa88af82e 100644 --- a/src/tools/run-make-support/src/diff/mod.rs +++ b/src/tools/run-make-support/src/diff/mod.rs @@ -87,9 +87,7 @@ impl Diff { self } - #[track_caller] - pub fn run(&mut self) { - self.drop_bomb.defuse(); + fn run_common(&self) -> (&str, &str, String, String) { let expected = self.expected.as_ref().expect("expected text not set"); let mut actual = self.actual.as_ref().expect("actual text not set").to_string(); let expected_name = self.expected_name.as_ref().unwrap(); @@ -104,6 +102,14 @@ impl Diff { .header(expected_name, actual_name) .to_string(); + (expected_name, actual_name, output, actual) + } + + #[track_caller] + pub fn run(&mut self) { + self.drop_bomb.defuse(); + let (expected_name, actual_name, output, actual) = self.run_common(); + if !output.is_empty() { // If we can bless (meaning we have a file to write into and the `RUSTC_BLESS_TEST` // environment variable set), then we write into the file and return. @@ -120,4 +126,26 @@ impl Diff { ) } } + + #[track_caller] + pub fn run_fail(&mut self) { + self.drop_bomb.defuse(); + let (expected_name, actual_name, output, actual) = self.run_common(); + + if output.is_empty() { + // If we can bless (meaning we have a file to write into and the `RUSTC_BLESS_TEST` + // environment variable set), then we write into the file and return. + if let Some(ref expected_file) = self.expected_file { + if std::env::var("RUSTC_BLESS_TEST").is_ok() { + println!("Blessing `{}`", expected_file.display()); + fs_wrapper::write(expected_file, actual); + return; + } + } + panic!( + "test failed: `{}` is not different from `{}`\n\n{}", + expected_name, actual_name, output + ) + } + } } diff --git a/src/tools/run-make-support/src/fs_wrapper.rs b/src/tools/run-make-support/src/fs_wrapper.rs index 8a2bfce8b4a..0f0d6f6618c 100644 --- a/src/tools/run-make-support/src/fs_wrapper.rs +++ b/src/tools/run-make-support/src/fs_wrapper.rs @@ -1,7 +1,7 @@ use std::fs; use std::path::Path; -/// A wrapper around [`std::fs::remove_file`] which includes the file path in the panic message.. +/// A wrapper around [`std::fs::remove_file`] which includes the file path in the panic message. #[track_caller] pub fn remove_file<P: AsRef<Path>>(path: P) { fs::remove_file(path.as_ref()) @@ -18,21 +18,21 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) { )); } -/// A wrapper around [`std::fs::File::create`] which includes the file path in the panic message.. +/// A wrapper around [`std::fs::File::create`] which includes the file path in the panic message. #[track_caller] pub fn create_file<P: AsRef<Path>>(path: P) { fs::File::create(path.as_ref()) .expect(&format!("the file in path \"{}\" could not be created", path.as_ref().display())); } -/// A wrapper around [`std::fs::read`] which includes the file path in the panic message.. +/// A wrapper around [`std::fs::read`] which includes the file path in the panic message. #[track_caller] pub fn read<P: AsRef<Path>>(path: P) -> Vec<u8> { fs::read(path.as_ref()) .expect(&format!("the file in path \"{}\" could not be read", path.as_ref().display())) } -/// A wrapper around [`std::fs::read_to_string`] which includes the file path in the panic message.. +/// A wrapper around [`std::fs::read_to_string`] which includes the file path in the panic message. #[track_caller] pub fn read_to_string<P: AsRef<Path>>(path: P) -> String { fs::read_to_string(path.as_ref()).expect(&format!( @@ -41,14 +41,14 @@ pub fn read_to_string<P: AsRef<Path>>(path: P) -> String { )) } -/// A wrapper around [`std::fs::read_dir`] which includes the file path in the panic message.. +/// A wrapper around [`std::fs::read_dir`] which includes the file path in the panic message. #[track_caller] pub fn read_dir<P: AsRef<Path>>(path: P) -> fs::ReadDir { fs::read_dir(path.as_ref()) .expect(&format!("the directory in path \"{}\" could not be read", path.as_ref().display())) } -/// A wrapper around [`std::fs::write`] which includes the file path in the panic message.. +/// A wrapper around [`std::fs::write`] which includes the file path in the panic message. #[track_caller] pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) { fs::write(path.as_ref(), contents.as_ref()).expect(&format!( @@ -57,7 +57,7 @@ pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) { )); } -/// A wrapper around [`std::fs::remove_dir_all`] which includes the file path in the panic message.. +/// A wrapper around [`std::fs::remove_dir_all`] which includes the file path in the panic message. #[track_caller] pub fn remove_dir_all<P: AsRef<Path>>(path: P) { fs::remove_dir_all(path.as_ref()).expect(&format!( @@ -66,7 +66,7 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) { )); } -/// A wrapper around [`std::fs::create_dir`] which includes the file path in the panic message.. +/// A wrapper around [`std::fs::create_dir`] which includes the file path in the panic message. #[track_caller] pub fn create_dir<P: AsRef<Path>>(path: P) { fs::create_dir(path.as_ref()).expect(&format!( @@ -75,7 +75,7 @@ pub fn create_dir<P: AsRef<Path>>(path: P) { )); } -/// A wrapper around [`std::fs::create_dir_all`] which includes the file path in the panic message.. +/// A wrapper around [`std::fs::create_dir_all`] which includes the file path in the panic message. #[track_caller] pub fn create_dir_all<P: AsRef<Path>>(path: P) { fs::create_dir_all(path.as_ref()).expect(&format!( @@ -84,7 +84,7 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) { )); } -/// A wrapper around [`std::fs::metadata`] which includes the file path in the panic message.. +/// A wrapper around [`std::fs::metadata`] which includes the file path in the panic message. #[track_caller] pub fn metadata<P: AsRef<Path>>(path: P) -> fs::Metadata { fs::metadata(path.as_ref()).expect(&format!( diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 7bb89106de1..5655318267a 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -35,7 +35,7 @@ pub use llvm::{ LlvmProfdata, LlvmReadobj, }; pub use run::{cmd, run, run_fail, run_with_args}; -pub use rustc::{aux_build, rustc, Rustc}; +pub use rustc::{aux_build, bare_rustc, rustc, Rustc}; pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc}; #[track_caller] @@ -296,12 +296,27 @@ pub fn not_contains<P: AsRef<Path>>(path: P, expected: &str) -> bool { } /// Returns true if the filename at `path` is not in `expected`. -pub fn filename_not_in_denylist<P: AsRef<Path>>(path: P, expected: &[String]) -> bool { +pub fn filename_not_in_denylist<P: AsRef<Path>, V: AsRef<[String]>>(path: P, expected: V) -> bool { + let expected = expected.as_ref(); path.as_ref() .file_name() .is_some_and(|name| !expected.contains(&name.to_str().unwrap().to_owned())) } +/// Gathers all files in the current working directory that have the extension `ext`, and counts +/// the number of lines within that contain a match with the regex pattern `re`. +pub fn count_regex_matches_in_files_with_extension(re: ®ex::Regex, ext: &str) -> usize { + let fetched_files = shallow_find_files(cwd(), |path| has_extension(path, ext)); + + let mut count = 0; + for file in fetched_files { + let content = fs_wrapper::read_to_string(file); + count += content.lines().filter(|line| re.is_match(&line)).count(); + } + + count +} + /// Use `cygpath -w` on a path to get a Windows path string back. This assumes that `cygpath` is /// available on the platform! #[track_caller] diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index 885b361f72a..a2a7c8064dc 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -10,6 +10,12 @@ pub fn rustc() -> Rustc { Rustc::new() } +/// Construct a plain `rustc` invocation with no flags set. +#[track_caller] +pub fn bare_rustc() -> Rustc { + Rustc::bare() +} + /// Construct a new `rustc` aux-build invocation. #[track_caller] pub fn aux_build() -> Rustc { @@ -30,7 +36,6 @@ fn setup_common() -> Command { let rustc = env_var("RUSTC"); let mut cmd = Command::new(rustc); set_host_rpath(&mut cmd); - cmd.arg("-L").arg(cwd()); cmd } @@ -40,6 +45,14 @@ impl Rustc { /// Construct a new `rustc` invocation. #[track_caller] pub fn new() -> Self { + let mut cmd = setup_common(); + cmd.arg("-L").arg(cwd()); + Self { cmd } + } + + /// Construct a bare `rustc` invocation with no flags set. + #[track_caller] + pub fn bare() -> Self { let cmd = setup_common(); Self { cmd } } diff --git a/src/tools/rust-analyzer/crates/stdx/src/anymap.rs b/src/tools/rust-analyzer/crates/stdx/src/anymap.rs index d189b56a468..4eafcfb060f 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/anymap.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/anymap.rs @@ -68,8 +68,6 @@ pub type RawMap<A> = hash_map::HashMap<TypeId, Box<A>, BuildHasherDefault<TypeId /// The type parameter `A` allows you to use a different value type; normally you will want /// it to be `core::any::Any` (also known as `std::any::Any`), but there are other choices: /// -/// - If you want the entire map to be cloneable, use `CloneAny` instead of `Any`; with -/// that, you can only add types that implement `Clone` to the map. /// - You can add on `+ Send` or `+ Send + Sync` (e.g. `Map<dyn Any + Send>`) to add those /// auto traits. /// @@ -79,9 +77,6 @@ pub type RawMap<A> = hash_map::HashMap<TypeId, Box<A>, BuildHasherDefault<TypeId /// also spelled [`AnyMap`] for convenience. /// - <code>[Map]<dyn [core::any::Any] + Send></code> /// - <code>[Map]<dyn [core::any::Any] + Send + Sync></code> -/// - <code>[Map]<dyn [CloneAny]></code> -/// - <code>[Map]<dyn [CloneAny] + Send></code> -/// - <code>[Map]<dyn [CloneAny] + Send + Sync></code> /// /// ## Example /// @@ -205,12 +200,6 @@ mod tests { assert_debug::<Map<dyn Any>>(); assert_debug::<Map<dyn Any + Send>>(); assert_debug::<Map<dyn Any + Send + Sync>>(); - assert_send::<Map<dyn CloneAny + Send>>(); - assert_send::<Map<dyn CloneAny + Send + Sync>>(); - assert_sync::<Map<dyn CloneAny + Send + Sync>>(); - assert_debug::<Map<dyn CloneAny>>(); - assert_debug::<Map<dyn CloneAny + Send>>(); - assert_debug::<Map<dyn CloneAny + Send + Sync>>(); } #[test] @@ -232,53 +221,6 @@ mod tests { } } -// impl some traits for dyn Any -use core::fmt; - -#[doc(hidden)] -pub trait CloneToAny { - /// Clone `self` into a new `Box<dyn CloneAny>` object. - fn clone_to_any(&self) -> Box<dyn CloneAny>; -} - -impl<T: Any + Clone> CloneToAny for T { - #[inline] - fn clone_to_any(&self) -> Box<dyn CloneAny> { - Box::new(self.clone()) - } -} - -macro_rules! impl_clone { - ($t:ty) => { - impl Clone for Box<$t> { - #[inline] - fn clone(&self) -> Box<$t> { - // SAFETY: this dance is to reapply any Send/Sync marker. I’m not happy about this - // approach, given that I used to do it in safe code, but then came a dodgy - // future-compatibility warning where_clauses_object_safety, which is spurious for - // auto traits but still super annoying (future-compatibility lints seem to mean - // your bin crate needs a corresponding allow!). Although I explained my plight¹ - // and it was all explained and agreed upon, no action has been taken. So I finally - // caved and worked around it by doing it this way, which matches what’s done for - // core::any², so it’s probably not *too* bad. - // - // ¹ https://github.com/rust-lang/rust/issues/51443#issuecomment-421988013 - // ² https://github.com/rust-lang/rust/blob/e7825f2b690c9a0d21b6f6d84c404bb53b151b38/library/alloc/src/boxed.rs#L1613-L1616 - let clone: Box<dyn CloneAny> = (**self).clone_to_any(); - let raw: *mut dyn CloneAny = Box::into_raw(clone); - unsafe { Box::from_raw(raw as *mut $t) } - } - } - - impl fmt::Debug for $t { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad(stringify!($t)) - } - } - }; -} - /// Methods for downcasting from an `Any`-like trait object. /// /// This should only be implemented on trait objects for subtraits of `Any`, though you can @@ -350,16 +292,3 @@ macro_rules! implement { implement!(Any); implement!(Any + Send); implement!(Any + Send + Sync); - -/// [`Any`], but with cloning. -/// -/// Every type with no non-`'static` references that implements `Clone` implements `CloneAny`. -/// See [`core::any`] for more details on `Any` in general. -pub trait CloneAny: Any + CloneToAny {} -impl<T: Any + Clone> CloneAny for T {} -implement!(CloneAny); -implement!(CloneAny + Send); -implement!(CloneAny + Send + Sync); -impl_clone!(dyn CloneAny); -impl_clone!(dyn CloneAny + Send); -impl_clone!(dyn CloneAny + Send + Sync); diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index ff98bd538db..d26d6fe360d 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -23,8 +23,6 @@ run-make/dep-info-spaces/Makefile run-make/dep-info/Makefile run-make/dump-ice-to-disk/Makefile run-make/dump-mono-stats/Makefile -run-make/emit-path-unhashed/Makefile -run-make/emit-shared-files/Makefile run-make/emit-to-stdout/Makefile run-make/env-dep-info/Makefile run-make/export-executable-symbols/Makefile @@ -49,7 +47,6 @@ run-make/foreign-rust-exceptions/Makefile run-make/incr-add-rust-src-component/Makefile run-make/incr-foreign-head-span/Makefile run-make/interdependent-c-libraries/Makefile -run-make/intrinsic-unreachable/Makefile run-make/issue-107094/Makefile run-make/issue-109934-lto-debuginfo/Makefile run-make/issue-14698/Makefile @@ -62,7 +59,6 @@ run-make/issue-28595/Makefile run-make/issue-33329/Makefile run-make/issue-35164/Makefile run-make/issue-36710/Makefile -run-make/issue-37839/Makefile run-make/issue-47551/Makefile run-make/issue-69368/Makefile run-make/issue-83045/Makefile @@ -101,9 +97,6 @@ run-make/no-builtins-attribute/Makefile run-make/no-duplicate-libs/Makefile run-make/obey-crate-type-flag/Makefile run-make/panic-abort-eh_frame/Makefile -run-make/pass-linker-flags-flavor/Makefile -run-make/pass-linker-flags-from-dep/Makefile -run-make/pass-linker-flags/Makefile run-make/pass-non-c-like-enum-to-c/Makefile run-make/pdb-buildinfo-cl-cmd/Makefile run-make/pgo-gen-lto/Makefile @@ -132,9 +125,6 @@ run-make/rustc-macro-dep-files/Makefile run-make/sanitizer-cdylib-link/Makefile run-make/sanitizer-dylib-link/Makefile run-make/sanitizer-staticlib-link/Makefile -run-make/sepcomp-cci-copies/Makefile -run-make/sepcomp-inlining/Makefile -run-make/sepcomp-separate/Makefile run-make/share-generics-dylib/Makefile run-make/silly-file-names/Makefile run-make/simd-ffi/Makefile @@ -149,14 +139,9 @@ run-make/symbol-mangling-hashed/Makefile run-make/symbol-visibility/Makefile run-make/symbols-include-type-name/Makefile run-make/sysroot-crates-are-unstable/Makefile -run-make/target-cpu-native/Makefile -run-make/target-specs/Makefile -run-make/target-without-atomic-cas/Makefile run-make/test-benches/Makefile run-make/thumb-none-cortex-m/Makefile run-make/thumb-none-qemu/Makefile -run-make/track-path-dep-info/Makefile -run-make/track-pgo-dep-info/Makefile run-make/translation/Makefile run-make/type-mismatch-same-crate-name/Makefile run-make/unstable-flag-required/Makefile diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index 7e5656926ab..2f8abc77566 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -45,6 +45,8 @@ const EXCEPTION_PATHS: &[&str] = &[ // pointer regardless of the target architecture. As a result, // we must use `#[cfg(windows)]` to conditionally compile the // correct `VaList` structure for windows. + "library/core/src/ffi/va_list.rs", + // We placed a linkage against Windows libraries here "library/core/src/ffi/mod.rs", "library/std/src/sys", // Platform-specific code for std lives here. "library/std/src/os", // Platform-specific public interfaces diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index e4d54d2a2b5..8e693c35adc 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -110,6 +110,7 @@ const ROOT_PROBLEMATIC_CONSTS: &[u32] = &[ 173390526, 721077, ]; +// Returns all permutations of problematic consts, over 2000 elements. fn generate_problematic_strings( consts: &[u32], letter_digit: &FxHashMap<char, char>, @@ -319,6 +320,8 @@ pub fn check(path: &Path, bad: &mut bool) { ROOT_PROBLEMATIC_CONSTS, &[('A', '4'), ('B', '8'), ('E', '3')].iter().cloned().collect(), ); + // This creates a RegexSet as regex contains performance optimizations to be able to deal with these over + // 2000 needles efficiently. This runs over the entire source code, so performance matters. let problematic_regex = RegexSet::new(problematic_consts_strings.as_slice()).unwrap(); walk(path, skip, &mut |entry, contents| { diff --git a/tests/coverage-run-rustdoc/doctest.coverage b/tests/coverage-run-rustdoc/doctest.coverage index 1bbf364759b..396811c5487 100644 --- a/tests/coverage-run-rustdoc/doctest.coverage +++ b/tests/coverage-run-rustdoc/doctest.coverage @@ -34,10 +34,10 @@ $DIR/doctest.rs: LL| |//! LL| |//! doctest returning a result: LL| 1|//! ``` - LL| 1|//! #[derive(Debug, PartialEq)] - LL| 1|//! struct SomeError { - LL| 1|//! msg: String, - LL| 1|//! } + LL| |//! #[derive(Debug, PartialEq)] + LL| |//! struct SomeError { + LL| |//! msg: String, + LL| |//! } LL| 1|//! let mut res = Err(SomeError { msg: String::from("a message") }); LL| 1|//! if res.is_ok() { LL| 0|//! res?; diff --git a/tests/coverage/async.cov-map b/tests/coverage/async.cov-map index 7d16372375a..9e5a4bdc60f 100644 --- a/tests/coverage/async.cov-map +++ b/tests/coverage/async.cov-map @@ -167,15 +167,16 @@ Number of file 0 mappings: 14 = ((c6 + c7) + c8) Function name: async::j -Raw bytes (53): 0x[01, 01, 02, 07, 0d, 05, 09, 09, 01, 35, 01, 13, 0c, 05, 14, 09, 00, 0a, 01, 00, 0e, 00, 1b, 05, 00, 1f, 00, 27, 09, 01, 09, 00, 0a, 11, 00, 0e, 00, 1a, 09, 00, 1e, 00, 20, 0d, 01, 0e, 00, 10, 03, 02, 01, 00, 02] +Raw bytes (58): 0x[01, 01, 02, 07, 0d, 05, 09, 0a, 01, 35, 01, 00, 0d, 01, 0b, 0b, 00, 0c, 05, 01, 09, 00, 0a, 01, 00, 0e, 00, 1b, 05, 00, 1f, 00, 27, 09, 01, 09, 00, 0a, 11, 00, 0e, 00, 1a, 09, 00, 1e, 00, 20, 0d, 01, 0e, 00, 10, 03, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 2 - expression 0 operands: lhs = Expression(1, Add), rhs = Counter(3) - expression 1 operands: lhs = Counter(1), rhs = Counter(2) -Number of file 0 mappings: 9 -- Code(Counter(0)) at (prev + 53, 1) to (start + 19, 12) -- Code(Counter(1)) at (prev + 20, 9) to (start + 0, 10) +Number of file 0 mappings: 10 +- Code(Counter(0)) at (prev + 53, 1) to (start + 0, 13) +- Code(Counter(0)) at (prev + 11, 11) to (start + 0, 12) +- Code(Counter(1)) at (prev + 1, 9) to (start + 0, 10) - Code(Counter(0)) at (prev + 0, 14) to (start + 0, 27) - Code(Counter(1)) at (prev + 0, 31) to (start + 0, 39) - Code(Counter(2)) at (prev + 1, 9) to (start + 0, 10) @@ -186,7 +187,7 @@ Number of file 0 mappings: 9 = ((c1 + c2) + c3) Function name: async::j::c -Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 37, 05, 01, 12, 05, 02, 0d, 00, 0e, 02, 0a, 0d, 00, 0e, 01, 02, 05, 00, 06] +Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 37, 05, 01, 12, 05, 02, 0d, 00, 0e, 02, 02, 0d, 00, 0e, 01, 02, 05, 00, 06] Number of files: 1 - file 0 => global file 1 Number of expressions: 1 @@ -194,40 +195,40 @@ Number of expressions: 1 Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 55, 5) to (start + 1, 18) - Code(Counter(1)) at (prev + 2, 13) to (start + 0, 14) -- Code(Expression(0, Sub)) at (prev + 10, 13) to (start + 0, 14) +- Code(Expression(0, Sub)) at (prev + 2, 13) to (start + 0, 14) = (c0 - c1) - Code(Counter(0)) at (prev + 2, 5) to (start + 0, 6) Function name: async::j::d -Raw bytes (9): 0x[01, 01, 00, 01, 01, 46, 05, 00, 17] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 3e, 05, 00, 17] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 70, 5) to (start + 0, 23) +- Code(Counter(0)) at (prev + 62, 5) to (start + 0, 23) Function name: async::j::f -Raw bytes (9): 0x[01, 01, 00, 01, 01, 47, 05, 00, 17] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 3f, 05, 00, 17] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 71, 5) to (start + 0, 23) +- Code(Counter(0)) at (prev + 63, 5) to (start + 0, 23) Function name: async::k (unused) -Raw bytes (29): 0x[01, 01, 00, 05, 00, 4f, 01, 01, 0c, 00, 02, 0e, 00, 10, 00, 01, 0e, 00, 10, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02] +Raw bytes (29): 0x[01, 01, 00, 05, 00, 47, 01, 01, 0c, 00, 02, 0e, 00, 10, 00, 01, 0e, 00, 10, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 5 -- Code(Zero) at (prev + 79, 1) to (start + 1, 12) +- Code(Zero) at (prev + 71, 1) to (start + 1, 12) - Code(Zero) at (prev + 2, 14) to (start + 0, 16) - Code(Zero) at (prev + 1, 14) to (start + 0, 16) - Code(Zero) at (prev + 1, 14) to (start + 0, 16) - Code(Zero) at (prev + 2, 1) to (start + 0, 2) Function name: async::l -Raw bytes (37): 0x[01, 01, 04, 01, 07, 05, 09, 0f, 02, 09, 05, 05, 01, 57, 01, 01, 0c, 02, 02, 0e, 00, 10, 05, 01, 0e, 00, 10, 09, 01, 0e, 00, 10, 0b, 02, 01, 00, 02] +Raw bytes (37): 0x[01, 01, 04, 01, 07, 05, 09, 0f, 02, 09, 05, 05, 01, 4f, 01, 01, 0c, 02, 02, 0e, 00, 10, 05, 01, 0e, 00, 10, 09, 01, 0e, 00, 10, 0b, 02, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 4 @@ -236,7 +237,7 @@ Number of expressions: 4 - expression 2 operands: lhs = Expression(3, Add), rhs = Expression(0, Sub) - expression 3 operands: lhs = Counter(2), rhs = Counter(1) Number of file 0 mappings: 5 -- Code(Counter(0)) at (prev + 87, 1) to (start + 1, 12) +- Code(Counter(0)) at (prev + 79, 1) to (start + 1, 12) - Code(Expression(0, Sub)) at (prev + 2, 14) to (start + 0, 16) = (c0 - (c1 + c2)) - Code(Counter(1)) at (prev + 1, 14) to (start + 0, 16) @@ -245,26 +246,26 @@ Number of file 0 mappings: 5 = ((c2 + c1) + (c0 - (c1 + c2))) Function name: async::m -Raw bytes (9): 0x[01, 01, 00, 01, 01, 5f, 01, 00, 19] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 57, 01, 00, 19] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 95, 1) to (start + 0, 25) +- Code(Counter(0)) at (prev + 87, 1) to (start + 0, 25) Function name: async::m::{closure#0} (unused) -Raw bytes (9): 0x[01, 01, 00, 01, 00, 5f, 19, 00, 22] +Raw bytes (9): 0x[01, 01, 00, 01, 00, 57, 19, 00, 22] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Zero) at (prev + 95, 25) to (start + 0, 34) +- Code(Zero) at (prev + 87, 25) to (start + 0, 34) Function name: async::main -Raw bytes (9): 0x[01, 01, 00, 01, 01, 61, 01, 08, 02] +Raw bytes (9): 0x[01, 01, 00, 01, 01, 59, 01, 08, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 97, 1) to (start + 8, 2) +- Code(Counter(0)) at (prev + 89, 1) to (start + 8, 2) diff --git a/tests/coverage/async.coverage b/tests/coverage/async.coverage index e943911d310..f5473829b02 100644 --- a/tests/coverage/async.coverage +++ b/tests/coverage/async.coverage @@ -53,25 +53,15 @@ LL| 1|} LL| | LL| 1|fn j(x: u8) { - LL| 1| // non-async versions of `c()`, `d()`, and `f()` to make it similar to async `i()`. + LL| | // non-async versions of `c()`, `d()`, and `f()` to make it similar to async `i()`. LL| 1| fn c(x: u8) -> u8 { LL| 1| if x == 8 { - LL| 1| 1 // This line appears covered, but the 1-character expression span covering the `1` - ^0 - LL| 1| // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because - LL| 1| // `fn j()` executes the open brace for the function body, followed by the function's - LL| 1| // first executable statement, `match x`. Inner function declarations are not - LL| 1| // "visible" to the MIR for `j()`, so the code region counts all lines between the - LL| 1| // open brace and the first statement as executed, which is, in a sense, true. - LL| 1| // `llvm-cov show` overcomes this kind of situation by showing the actual counts - LL| 1| // of the enclosed coverages, (that is, the `1` expression was not executed, and - LL| 1| // accurately displays a `0`). - LL| 1| } else { + LL| 0| 1 + LL| | } else { LL| 1| 0 - LL| 1| } + LL| | } LL| 1| } - LL| 1| fn d() -> u8 { 1 } // inner function is defined in-line, but the function is not executed - ^0 + LL| 0| fn d() -> u8 { 1 } // inner function is defined in-line, but the function is not executed LL| 1| fn f() -> u8 { 1 } LL| 1| match x { LL| 1| y if c(x) == y + 1 => { d(); } diff --git a/tests/coverage/async.rs b/tests/coverage/async.rs index 5018ade0125..7e6ad761ecd 100644 --- a/tests/coverage/async.rs +++ b/tests/coverage/async.rs @@ -54,15 +54,7 @@ fn j(x: u8) { // non-async versions of `c()`, `d()`, and `f()` to make it similar to async `i()`. fn c(x: u8) -> u8 { if x == 8 { - 1 // This line appears covered, but the 1-character expression span covering the `1` - // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because - // `fn j()` executes the open brace for the function body, followed by the function's - // first executable statement, `match x`. Inner function declarations are not - // "visible" to the MIR for `j()`, so the code region counts all lines between the - // open brace and the first statement as executed, which is, in a sense, true. - // `llvm-cov show` overcomes this kind of situation by showing the actual counts - // of the enclosed coverages, (that is, the `1` expression was not executed, and - // accurately displays a `0`). + 1 } else { 0 } diff --git a/tests/coverage/attr/nested.cov-map b/tests/coverage/attr/nested.cov-map index 0f2d5542f75..466aec8956e 100644 --- a/tests/coverage/attr/nested.cov-map +++ b/tests/coverage/attr/nested.cov-map @@ -1,18 +1,18 @@ Function name: nested::closure_expr -Raw bytes (14): 0x[01, 01, 00, 02, 01, 44, 01, 01, 0f, 01, 0b, 05, 01, 02] +Raw bytes (14): 0x[01, 01, 00, 02, 01, 3f, 01, 01, 0f, 01, 0b, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 2 -- Code(Counter(0)) at (prev + 68, 1) to (start + 1, 15) +- Code(Counter(0)) at (prev + 63, 1) to (start + 1, 15) - Code(Counter(0)) at (prev + 11, 5) to (start + 1, 2) Function name: nested::closure_tail -Raw bytes (14): 0x[01, 01, 00, 02, 01, 53, 01, 01, 0f, 01, 11, 05, 01, 02] +Raw bytes (14): 0x[01, 01, 00, 02, 01, 4e, 01, 01, 0f, 01, 11, 05, 01, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 Number of file 0 mappings: 2 -- Code(Counter(0)) at (prev + 83, 1) to (start + 1, 15) +- Code(Counter(0)) at (prev + 78, 1) to (start + 1, 15) - Code(Counter(0)) at (prev + 17, 5) to (start + 1, 2) diff --git a/tests/coverage/attr/nested.coverage b/tests/coverage/attr/nested.coverage index bdd117b7dfa..2d64fe698ea 100644 --- a/tests/coverage/attr/nested.coverage +++ b/tests/coverage/attr/nested.coverage @@ -4,11 +4,6 @@ LL| |// Demonstrates the interaction between #[coverage(off)] and various kinds of LL| |// nested function. LL| | - LL| |// FIXME(#126625): Coverage attributes should apply recursively to nested functions. - LL| |// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`, - LL| |// its lines can still be marked with misleading execution counts from its enclosing - LL| |// function. - LL| | LL| |#[coverage(off)] LL| |fn do_stuff() {} LL| | diff --git a/tests/coverage/attr/nested.rs b/tests/coverage/attr/nested.rs index c7ff835f44f..8213e29b6fc 100644 --- a/tests/coverage/attr/nested.rs +++ b/tests/coverage/attr/nested.rs @@ -4,11 +4,6 @@ // Demonstrates the interaction between #[coverage(off)] and various kinds of // nested function. -// FIXME(#126625): Coverage attributes should apply recursively to nested functions. -// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`, -// its lines can still be marked with misleading execution counts from its enclosing -// function. - #[coverage(off)] fn do_stuff() {} diff --git a/tests/coverage/attr/off-on-sandwich.cov-map b/tests/coverage/attr/off-on-sandwich.cov-map index ed77d7d17e6..d5fbac6ebf7 100644 --- a/tests/coverage/attr/off-on-sandwich.cov-map +++ b/tests/coverage/attr/off-on-sandwich.cov-map @@ -1,24 +1,27 @@ Function name: off_on_sandwich::dense_a::dense_b -Raw bytes (9): 0x[01, 01, 00, 01, 01, 14, 05, 07, 06] +Raw bytes (14): 0x[01, 01, 00, 02, 01, 0f, 05, 02, 12, 01, 07, 05, 00, 06] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 -Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 20, 5) to (start + 7, 6) +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 15, 5) to (start + 2, 18) +- Code(Counter(0)) at (prev + 7, 5) to (start + 0, 6) Function name: off_on_sandwich::sparse_a::sparse_b::sparse_c -Raw bytes (9): 0x[01, 01, 00, 01, 01, 26, 09, 0b, 0a] +Raw bytes (14): 0x[01, 01, 00, 02, 01, 21, 09, 02, 17, 01, 0b, 09, 00, 0a] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 -Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 38, 9) to (start + 11, 10) +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 33, 9) to (start + 2, 23) +- Code(Counter(0)) at (prev + 11, 9) to (start + 0, 10) Function name: off_on_sandwich::sparse_a::sparse_b::sparse_c::sparse_d -Raw bytes (9): 0x[01, 01, 00, 01, 01, 29, 0d, 07, 0e] +Raw bytes (14): 0x[01, 01, 00, 02, 01, 24, 0d, 02, 1b, 01, 07, 0d, 00, 0e] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 -Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 41, 13) to (start + 7, 14) +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 36, 13) to (start + 2, 27) +- Code(Counter(0)) at (prev + 7, 13) to (start + 0, 14) diff --git a/tests/coverage/attr/off-on-sandwich.coverage b/tests/coverage/attr/off-on-sandwich.coverage index 58c128b8342..675697906ee 100644 --- a/tests/coverage/attr/off-on-sandwich.coverage +++ b/tests/coverage/attr/off-on-sandwich.coverage @@ -4,11 +4,6 @@ LL| |// Demonstrates the interaction of `#[coverage(off)]` and `#[coverage(on)]` LL| |// in nested functions. LL| | - LL| |// FIXME(#126625): Coverage attributes should apply recursively to nested functions. - LL| |// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`, - LL| |// its lines can still be marked with misleading execution counts from its enclosing - LL| |// function. - LL| | LL| |#[coverage(off)] LL| |fn do_stuff() {} LL| | @@ -20,10 +15,10 @@ LL| 2| fn dense_b() { LL| 2| dense_c(); LL| 2| dense_c(); - LL| 2| #[coverage(off)] - LL| 2| fn dense_c() { - LL| 2| do_stuff(); - LL| 2| } + LL| | #[coverage(off)] + LL| | fn dense_c() { + LL| | do_stuff(); + LL| | } LL| 2| } LL| |} LL| | @@ -41,10 +36,10 @@ LL| 8| fn sparse_d() { LL| 8| sparse_e(); LL| 8| sparse_e(); - LL| 8| #[coverage(off)] - LL| 8| fn sparse_e() { - LL| 8| do_stuff(); - LL| 8| } + LL| | #[coverage(off)] + LL| | fn sparse_e() { + LL| | do_stuff(); + LL| | } LL| 8| } LL| 4| } LL| | } diff --git a/tests/coverage/attr/off-on-sandwich.rs b/tests/coverage/attr/off-on-sandwich.rs index 6b21b180223..261634e0029 100644 --- a/tests/coverage/attr/off-on-sandwich.rs +++ b/tests/coverage/attr/off-on-sandwich.rs @@ -4,11 +4,6 @@ // Demonstrates the interaction of `#[coverage(off)]` and `#[coverage(on)]` // in nested functions. -// FIXME(#126625): Coverage attributes should apply recursively to nested functions. -// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`, -// its lines can still be marked with misleading execution counts from its enclosing -// function. - #[coverage(off)] fn do_stuff() {} diff --git a/tests/coverage/closure_macro.cov-map b/tests/coverage/closure_macro.cov-map index 156947f4e21..eb5f94d1080 100644 --- a/tests/coverage/closure_macro.cov-map +++ b/tests/coverage/closure_macro.cov-map @@ -7,15 +7,16 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 29, 1) to (start + 2, 2) Function name: closure_macro::main -Raw bytes (31): 0x[01, 01, 01, 01, 05, 05, 01, 21, 01, 01, 21, 02, 02, 09, 00, 0f, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 01, 03, 01, 00, 02] +Raw bytes (36): 0x[01, 01, 01, 01, 05, 06, 01, 21, 01, 01, 21, 02, 02, 09, 00, 0f, 01, 00, 12, 00, 54, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 01, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -Number of file 0 mappings: 5 +Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 33, 1) to (start + 1, 33) - Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 15) = (c0 - c1) +- Code(Counter(0)) at (prev + 0, 18) to (start + 0, 84) - Code(Counter(1)) at (prev + 0, 84) to (start + 0, 85) - Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 2, 11) = (c0 - c1) diff --git a/tests/coverage/closure_macro_async.cov-map b/tests/coverage/closure_macro_async.cov-map index 0f2b4e01748..1286d663bd4 100644 --- a/tests/coverage/closure_macro_async.cov-map +++ b/tests/coverage/closure_macro_async.cov-map @@ -15,15 +15,16 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 35, 1) to (start + 0, 43) Function name: closure_macro_async::test::{closure#0} -Raw bytes (31): 0x[01, 01, 01, 01, 05, 05, 01, 23, 2b, 01, 21, 02, 02, 09, 00, 0f, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 01, 03, 01, 00, 02] +Raw bytes (36): 0x[01, 01, 01, 01, 05, 06, 01, 23, 2b, 01, 21, 02, 02, 09, 00, 0f, 01, 00, 12, 00, 54, 05, 00, 54, 00, 55, 02, 02, 09, 02, 0b, 01, 03, 01, 00, 02] Number of files: 1 - file 0 => global file 1 Number of expressions: 1 - expression 0 operands: lhs = Counter(0), rhs = Counter(1) -Number of file 0 mappings: 5 +Number of file 0 mappings: 6 - Code(Counter(0)) at (prev + 35, 43) to (start + 1, 33) - Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 0, 15) = (c0 - c1) +- Code(Counter(0)) at (prev + 0, 18) to (start + 0, 84) - Code(Counter(1)) at (prev + 0, 84) to (start + 0, 85) - Code(Expression(0, Sub)) at (prev + 2, 9) to (start + 2, 11) = (c0 - c1) diff --git a/tests/coverage/holes.cov-map b/tests/coverage/holes.cov-map new file mode 100644 index 00000000000..9350bd9a405 --- /dev/null +++ b/tests/coverage/holes.cov-map @@ -0,0 +1,47 @@ +Function name: <holes::main::MyStruct>::_method (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 25, 09, 00, 1d] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 37, 9) to (start + 0, 29) + +Function name: holes::main +Raw bytes (44): 0x[01, 01, 00, 08, 01, 08, 01, 06, 11, 01, 0f, 05, 00, 12, 01, 04, 05, 00, 12, 01, 07, 05, 00, 12, 01, 06, 05, 00, 12, 01, 06, 05, 03, 0f, 01, 0a, 05, 03, 0f, 01, 0a, 05, 01, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 8, 1) to (start + 6, 17) +- Code(Counter(0)) at (prev + 15, 5) to (start + 0, 18) +- Code(Counter(0)) at (prev + 4, 5) to (start + 0, 18) +- Code(Counter(0)) at (prev + 7, 5) to (start + 0, 18) +- Code(Counter(0)) at (prev + 6, 5) to (start + 0, 18) +- Code(Counter(0)) at (prev + 6, 5) to (start + 3, 15) +- Code(Counter(0)) at (prev + 10, 5) to (start + 3, 15) +- Code(Counter(0)) at (prev + 10, 5) to (start + 1, 2) + +Function name: holes::main::_unused_fn (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 19, 05, 00, 17] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 25, 5) to (start + 0, 23) + +Function name: holes::main::{closure#0} (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 12, 09, 02, 0a] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 18, 9) to (start + 2, 10) + +Function name: holes::main::{closure#1} (unused) +Raw bytes (9): 0x[01, 01, 00, 01, 00, 3d, 09, 02, 0a] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 61, 9) to (start + 2, 10) + diff --git a/tests/coverage/holes.coverage b/tests/coverage/holes.coverage new file mode 100644 index 00000000000..6e65435f7e3 --- /dev/null +++ b/tests/coverage/holes.coverage @@ -0,0 +1,68 @@ + LL| |//@ edition: 2021 + LL| | + LL| |// Nested items/closures should be treated as "holes", so that their spans are + LL| |// not displayed as executable code in the enclosing function. + LL| | + LL| |use core::hint::black_box; + LL| | + LL| 1|fn main() { + LL| 1| black_box(()); + LL| 1| + LL| 1| // Splitting this across multiple lines makes it easier to see where the + LL| 1| // coverage mapping regions begin and end. + LL| 1| #[rustfmt::skip] + LL| 1| let _closure = + LL| | | + LL| | _arg: (), + LL| | | + LL| 0| { + LL| 0| black_box(()); + LL| 0| } + LL| | ; + LL| | + LL| 1| black_box(()); + LL| | + LL| 0| fn _unused_fn() {} + LL| | + LL| 1| black_box(()); + LL| | + LL| | struct MyStruct { + LL| | _x: u32, + LL| | _y: u32, + LL| | } + LL| | + LL| 1| black_box(()); + LL| | + LL| | impl MyStruct { + LL| 0| fn _method(&self) {} + LL| | } + LL| | + LL| 1| black_box(()); + LL| | + LL| | macro_rules! _my_macro { + LL| | () => {}; + LL| | } + LL| | + LL| 1| black_box(()); + LL| 1| + LL| 1| #[rustfmt::skip] + LL| 1| let _const = + LL| | const + LL| | { + LL| | 7 + 4 + LL| | } + LL| | ; + LL| | + LL| 1| black_box(()); + LL| 1| + LL| 1| #[rustfmt::skip] + LL| 1| let _async = + LL| | async + LL| 0| { + LL| 0| 7 + 4 + LL| 0| } + LL| | ; + LL| | + LL| 1| black_box(()); + LL| 1|} + diff --git a/tests/coverage/holes.rs b/tests/coverage/holes.rs new file mode 100644 index 00000000000..b3a71e759c8 --- /dev/null +++ b/tests/coverage/holes.rs @@ -0,0 +1,67 @@ +//@ edition: 2021 + +// Nested items/closures should be treated as "holes", so that their spans are +// not displayed as executable code in the enclosing function. + +use core::hint::black_box; + +fn main() { + black_box(()); + + // Splitting this across multiple lines makes it easier to see where the + // coverage mapping regions begin and end. + #[rustfmt::skip] + let _closure = + | + _arg: (), + | + { + black_box(()); + } + ; + + black_box(()); + + fn _unused_fn() {} + + black_box(()); + + struct MyStruct { + _x: u32, + _y: u32, + } + + black_box(()); + + impl MyStruct { + fn _method(&self) {} + } + + black_box(()); + + macro_rules! _my_macro { + () => {}; + } + + black_box(()); + + #[rustfmt::skip] + let _const = + const + { + 7 + 4 + } + ; + + black_box(()); + + #[rustfmt::skip] + let _async = + async + { + 7 + 4 + } + ; + + black_box(()); +} diff --git a/tests/coverage/no_cov_crate.cov-map b/tests/coverage/no_cov_crate.cov-map index 281efb6d00d..75234f6c3b7 100644 --- a/tests/coverage/no_cov_crate.cov-map +++ b/tests/coverage/no_cov_crate.cov-map @@ -31,20 +31,22 @@ Number of file 0 mappings: 1 - Code(Counter(0)) at (prev + 77, 1) to (start + 11, 2) Function name: no_cov_crate::nested_fns::outer -Raw bytes (9): 0x[01, 01, 00, 01, 01, 31, 05, 0c, 06] +Raw bytes (14): 0x[01, 01, 00, 02, 01, 31, 05, 02, 23, 01, 0c, 05, 00, 06] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 -Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 49, 5) to (start + 12, 6) +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 49, 5) to (start + 2, 35) +- Code(Counter(0)) at (prev + 12, 5) to (start + 0, 6) Function name: no_cov_crate::nested_fns::outer_both_covered -Raw bytes (9): 0x[01, 01, 00, 01, 01, 3f, 05, 0b, 06] +Raw bytes (14): 0x[01, 01, 00, 02, 01, 3f, 05, 02, 17, 01, 0b, 05, 00, 06] Number of files: 1 - file 0 => global file 1 Number of expressions: 0 -Number of file 0 mappings: 1 -- Code(Counter(0)) at (prev + 63, 5) to (start + 11, 6) +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 63, 5) to (start + 2, 23) +- Code(Counter(0)) at (prev + 11, 5) to (start + 0, 6) Function name: no_cov_crate::nested_fns::outer_both_covered::inner Raw bytes (26): 0x[01, 01, 01, 01, 05, 04, 01, 43, 09, 01, 17, 05, 01, 18, 02, 0e, 02, 02, 14, 02, 0e, 01, 03, 09, 00, 0a] diff --git a/tests/coverage/no_cov_crate.coverage b/tests/coverage/no_cov_crate.coverage index 29ad1f979cf..6a43e52652e 100644 --- a/tests/coverage/no_cov_crate.coverage +++ b/tests/coverage/no_cov_crate.coverage @@ -49,21 +49,21 @@ LL| 1| pub fn outer(is_true: bool) { LL| 1| println!("called and covered"); LL| 1| inner_not_covered(is_true); - LL| 1| - LL| 1| #[coverage(off)] - LL| 1| fn inner_not_covered(is_true: bool) { - LL| 1| if is_true { - LL| 1| println!("called but not covered"); - LL| 1| } else { - LL| 1| println!("absolutely not covered"); - LL| 1| } - LL| 1| } + LL| | + LL| | #[coverage(off)] + LL| | fn inner_not_covered(is_true: bool) { + LL| | if is_true { + LL| | println!("called but not covered"); + LL| | } else { + LL| | println!("absolutely not covered"); + LL| | } + LL| | } LL| 1| } LL| | LL| 1| pub fn outer_both_covered(is_true: bool) { LL| 1| println!("called and covered"); LL| 1| inner(is_true); - LL| 1| + LL| | LL| 1| fn inner(is_true: bool) { LL| 1| if is_true { LL| 1| println!("called and covered"); diff --git a/tests/crashes/125099.rs b/tests/crashes/125099.rs deleted file mode 100644 index bfc8c8fdcf6..00000000000 --- a/tests/crashes/125099.rs +++ /dev/null @@ -1,24 +0,0 @@ -//@ known-bug: rust-lang/rust#125099 - -pub trait ContFn<T>: Fn(T) -> Self::Future { - type Future; -} -impl<T, F> ContFn<T> for F -where - F: Fn(T), -{ - type Future = (); -} - -pub trait SeqHandler { - type Requires; - fn process<F: ContFn<Self::Requires>>() -> impl Sized; -} - -pub struct ConvertToU64; -impl SeqHandler for ConvertToU64 { - type Requires = u64; - fn process<F: ContFn<Self::Requires>>() -> impl Sized {} -} - -fn main() {} diff --git a/tests/crashes/127332.rs b/tests/crashes/127332.rs deleted file mode 100644 index 5c14af01cec..00000000000 --- a/tests/crashes/127332.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ known-bug: rust-lang/rust #127332 - -async fn fun() { - enum Foo { - A { x: u32 }, - } - let orig = Foo::A { x: 5 }; - Foo::A { x: 6, ..orig }; -} diff --git a/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-abort.diff b/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-abort.diff new file mode 100644 index 00000000000..44673ea00a9 --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-abort.diff @@ -0,0 +1,108 @@ +- // MIR for `f` before ElaborateDrops ++ // MIR for `f` after ElaborateDrops + + fn f() -> () { + let mut _0: (); + let mut _1: !; + let _2: std::string::String; + let _6: (); + let mut _7: std::string::String; ++ let mut _8: bool; + scope 1 { + debug _a => _2; + let _3: i32; + scope 2 { + debug _b => _3; + let _4: std::string::String; + scope 3 { + debug _c => _4; + let _5: std::string::String; + scope 4 { + debug _d => _5; + } + } + } + } + + bb0: { ++ _8 = const false; + StorageLive(_2); + _2 = String::new() -> [return: bb1, unwind: bb12]; + } + + bb1: { + StorageLive(_3); + _3 = const 12_i32; + StorageLive(_4); + _4 = String::new() -> [return: bb2, unwind: bb11]; + } + + bb2: { ++ _8 = const true; + StorageLive(_5); + _5 = String::new() -> [return: bb3, unwind: bb10]; + } + + bb3: { + StorageLive(_6); + StorageLive(_7); ++ _8 = const false; + _7 = move _4; + _6 = std::mem::drop::<String>(move _7) -> [return: bb4, unwind: bb8]; + } + + bb4: { + StorageDead(_7); + StorageDead(_6); + drop(_5) -> [return: bb5, unwind: bb10]; + } + + bb5: { + StorageDead(_5); +- drop(_4) -> [return: bb6, unwind: bb11]; ++ goto -> bb6; + } + + bb6: { ++ _8 = const false; + StorageDead(_4); + StorageDead(_3); + drop(_2) -> [return: bb7, unwind: bb12]; + } + + bb7: { + StorageDead(_2); + tailcall g(); + } + + bb8 (cleanup): { +- drop(_7) -> [return: bb9, unwind terminate(cleanup)]; ++ goto -> bb9; + } + + bb9 (cleanup): { + drop(_5) -> [return: bb10, unwind terminate(cleanup)]; + } + + bb10 (cleanup): { +- drop(_4) -> [return: bb11, unwind terminate(cleanup)]; ++ goto -> bb14; + } + + bb11 (cleanup): { + drop(_2) -> [return: bb12, unwind terminate(cleanup)]; + } + + bb12 (cleanup): { + resume; ++ } ++ ++ bb13 (cleanup): { ++ drop(_4) -> [return: bb11, unwind terminate(cleanup)]; ++ } ++ ++ bb14 (cleanup): { ++ switchInt(_8) -> [0: bb11, otherwise: bb13]; + } + } + diff --git a/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-unwind.diff b/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-unwind.diff new file mode 100644 index 00000000000..a6d33a24595 --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-unwind.diff @@ -0,0 +1,109 @@ +- // MIR for `f` before ElaborateDrops ++ // MIR for `f` after ElaborateDrops + + fn f() -> () { + let mut _0: (); + let mut _1: !; + let _2: std::string::String; + let _6: (); + let mut _7: std::string::String; ++ let mut _8: bool; + scope 1 { + debug _a => _2; + let _3: i32; + scope 2 { + debug _b => _3; + let _4: std::string::String; + scope 3 { + debug _c => _4; + let _5: std::string::String; + scope 4 { + debug _d => _5; + } + } + } + } + + bb0: { ++ _8 = const false; + StorageLive(_2); + _2 = String::new() -> [return: bb1, unwind continue]; + } + + bb1: { + StorageLive(_3); + _3 = const 12_i32; + StorageLive(_4); + _4 = String::new() -> [return: bb2, unwind: bb11]; + } + + bb2: { ++ _8 = const true; + StorageLive(_5); + _5 = String::new() -> [return: bb3, unwind: bb10]; + } + + bb3: { + StorageLive(_6); + StorageLive(_7); ++ _8 = const false; + _7 = move _4; + _6 = std::mem::drop::<String>(move _7) -> [return: bb4, unwind: bb8]; + } + + bb4: { + StorageDead(_7); + StorageDead(_6); + drop(_5) -> [return: bb5, unwind: bb10]; + } + + bb5: { + StorageDead(_5); +- drop(_4) -> [return: bb6, unwind: bb11]; ++ goto -> bb6; + } + + bb6: { ++ _8 = const false; + StorageDead(_4); + StorageDead(_3); +- drop(_2) -> [return: bb7, unwind continue]; ++ drop(_2) -> [return: bb7, unwind: bb12]; + } + + bb7: { + StorageDead(_2); + tailcall g(); + } + + bb8 (cleanup): { +- drop(_7) -> [return: bb9, unwind terminate(cleanup)]; ++ goto -> bb9; + } + + bb9 (cleanup): { + drop(_5) -> [return: bb10, unwind terminate(cleanup)]; + } + + bb10 (cleanup): { +- drop(_4) -> [return: bb11, unwind terminate(cleanup)]; ++ goto -> bb14; + } + + bb11 (cleanup): { + drop(_2) -> [return: bb12, unwind terminate(cleanup)]; + } + + bb12 (cleanup): { + resume; ++ } ++ ++ bb13 (cleanup): { ++ drop(_4) -> [return: bb11, unwind terminate(cleanup)]; ++ } ++ ++ bb14 (cleanup): { ++ switchInt(_8) -> [0: bb11, otherwise: bb13]; + } + } + diff --git a/tests/mir-opt/tail_call_drops.f.built.after.panic-abort.mir b/tests/mir-opt/tail_call_drops.f.built.after.panic-abort.mir new file mode 100644 index 00000000000..2c3d62491d7 --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f.built.after.panic-abort.mir @@ -0,0 +1,118 @@ +// MIR for `f` after built + +fn f() -> () { + let mut _0: (); + let mut _1: !; + let _2: std::string::String; + let _6: (); + let mut _7: std::string::String; + scope 1 { + debug _a => _2; + let _3: i32; + scope 2 { + debug _b => _3; + let _4: std::string::String; + scope 3 { + debug _c => _4; + let _5: std::string::String; + scope 4 { + debug _d => _5; + } + } + } + } + + bb0: { + StorageLive(_2); + _2 = String::new() -> [return: bb1, unwind: bb17]; + } + + bb1: { + FakeRead(ForLet(None), _2); + StorageLive(_3); + _3 = const 12_i32; + FakeRead(ForLet(None), _3); + StorageLive(_4); + _4 = String::new() -> [return: bb2, unwind: bb16]; + } + + bb2: { + FakeRead(ForLet(None), _4); + StorageLive(_5); + _5 = String::new() -> [return: bb3, unwind: bb15]; + } + + bb3: { + FakeRead(ForLet(None), _5); + StorageLive(_6); + StorageLive(_7); + _7 = move _4; + _6 = std::mem::drop::<String>(move _7) -> [return: bb4, unwind: bb13]; + } + + bb4: { + StorageDead(_7); + StorageDead(_6); + drop(_5) -> [return: bb5, unwind: bb15]; + } + + bb5: { + StorageDead(_5); + drop(_4) -> [return: bb6, unwind: bb16]; + } + + bb6: { + StorageDead(_4); + StorageDead(_3); + drop(_2) -> [return: bb7, unwind: bb17]; + } + + bb7: { + StorageDead(_2); + tailcall g(); + } + + bb8: { + drop(_5) -> [return: bb9, unwind: bb15]; + } + + bb9: { + StorageDead(_5); + drop(_4) -> [return: bb10, unwind: bb16]; + } + + bb10: { + StorageDead(_4); + StorageDead(_3); + drop(_2) -> [return: bb11, unwind: bb17]; + } + + bb11: { + StorageDead(_2); + unreachable; + } + + bb12: { + return; + } + + bb13 (cleanup): { + drop(_7) -> [return: bb14, unwind terminate(cleanup)]; + } + + bb14 (cleanup): { + drop(_5) -> [return: bb15, unwind terminate(cleanup)]; + } + + bb15 (cleanup): { + drop(_4) -> [return: bb16, unwind terminate(cleanup)]; + } + + bb16 (cleanup): { + drop(_2) -> [return: bb17, unwind terminate(cleanup)]; + } + + bb17 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/tail_call_drops.f.built.after.panic-unwind.mir b/tests/mir-opt/tail_call_drops.f.built.after.panic-unwind.mir new file mode 100644 index 00000000000..2c3d62491d7 --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f.built.after.panic-unwind.mir @@ -0,0 +1,118 @@ +// MIR for `f` after built + +fn f() -> () { + let mut _0: (); + let mut _1: !; + let _2: std::string::String; + let _6: (); + let mut _7: std::string::String; + scope 1 { + debug _a => _2; + let _3: i32; + scope 2 { + debug _b => _3; + let _4: std::string::String; + scope 3 { + debug _c => _4; + let _5: std::string::String; + scope 4 { + debug _d => _5; + } + } + } + } + + bb0: { + StorageLive(_2); + _2 = String::new() -> [return: bb1, unwind: bb17]; + } + + bb1: { + FakeRead(ForLet(None), _2); + StorageLive(_3); + _3 = const 12_i32; + FakeRead(ForLet(None), _3); + StorageLive(_4); + _4 = String::new() -> [return: bb2, unwind: bb16]; + } + + bb2: { + FakeRead(ForLet(None), _4); + StorageLive(_5); + _5 = String::new() -> [return: bb3, unwind: bb15]; + } + + bb3: { + FakeRead(ForLet(None), _5); + StorageLive(_6); + StorageLive(_7); + _7 = move _4; + _6 = std::mem::drop::<String>(move _7) -> [return: bb4, unwind: bb13]; + } + + bb4: { + StorageDead(_7); + StorageDead(_6); + drop(_5) -> [return: bb5, unwind: bb15]; + } + + bb5: { + StorageDead(_5); + drop(_4) -> [return: bb6, unwind: bb16]; + } + + bb6: { + StorageDead(_4); + StorageDead(_3); + drop(_2) -> [return: bb7, unwind: bb17]; + } + + bb7: { + StorageDead(_2); + tailcall g(); + } + + bb8: { + drop(_5) -> [return: bb9, unwind: bb15]; + } + + bb9: { + StorageDead(_5); + drop(_4) -> [return: bb10, unwind: bb16]; + } + + bb10: { + StorageDead(_4); + StorageDead(_3); + drop(_2) -> [return: bb11, unwind: bb17]; + } + + bb11: { + StorageDead(_2); + unreachable; + } + + bb12: { + return; + } + + bb13 (cleanup): { + drop(_7) -> [return: bb14, unwind terminate(cleanup)]; + } + + bb14 (cleanup): { + drop(_5) -> [return: bb15, unwind terminate(cleanup)]; + } + + bb15 (cleanup): { + drop(_4) -> [return: bb16, unwind terminate(cleanup)]; + } + + bb16 (cleanup): { + drop(_2) -> [return: bb17, unwind terminate(cleanup)]; + } + + bb17 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-abort.diff b/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-abort.diff new file mode 100644 index 00000000000..c7df2bb2207 --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-abort.diff @@ -0,0 +1,184 @@ +- // MIR for `f_with_arg` before ElaborateDrops ++ // MIR for `f_with_arg` after ElaborateDrops + + fn f_with_arg(_1: String, _2: String) -> () { + debug _arg1 => _1; + debug _arg2 => _2; + let mut _0: (); + let mut _3: !; + let _4: std::string::String; + let _8: (); + let mut _9: std::string::String; + let mut _10: std::string::String; + let mut _11: std::string::String; ++ let mut _12: bool; + scope 1 { + debug _a => _4; + let _5: i32; + scope 2 { + debug _b => _5; + let _6: std::string::String; + scope 3 { + debug _c => _6; + let _7: std::string::String; + scope 4 { + debug _d => _7; + } + } + } + } + + bb0: { ++ _12 = const false; + StorageLive(_4); + _4 = String::new() -> [return: bb1, unwind: bb27]; + } + + bb1: { + StorageLive(_5); + _5 = const 12_i32; + StorageLive(_6); + _6 = String::new() -> [return: bb2, unwind: bb26]; + } + + bb2: { ++ _12 = const true; + StorageLive(_7); + _7 = String::new() -> [return: bb3, unwind: bb25]; + } + + bb3: { + StorageLive(_8); + StorageLive(_9); ++ _12 = const false; + _9 = move _6; + _8 = std::mem::drop::<String>(move _9) -> [return: bb4, unwind: bb23]; + } + + bb4: { + StorageDead(_9); + StorageDead(_8); + StorageLive(_10); + _10 = String::new() -> [return: bb5, unwind: bb24]; + } + + bb5: { + StorageLive(_11); + _11 = String::new() -> [return: bb6, unwind: bb22]; + } + + bb6: { + drop(_7) -> [return: bb7, unwind: bb20]; + } + + bb7: { + StorageDead(_7); +- drop(_6) -> [return: bb8, unwind: bb18]; ++ goto -> bb8; + } + + bb8: { ++ _12 = const false; + StorageDead(_6); + StorageDead(_5); + drop(_4) -> [return: bb9, unwind: bb16]; + } + + bb9: { + StorageDead(_4); + drop(_2) -> [return: bb10, unwind: bb14]; + } + + bb10: { + drop(_1) -> [return: bb11, unwind: bb12]; + } + + bb11: { + tailcall g_with_arg(Spanned { node: move _10, span: $DIR/tail_call_drops.rs:36:23: 36:36 (#0) }, Spanned { node: move _11, span: $DIR/tail_call_drops.rs:36:38: 36:51 (#0) }); + } + + bb12 (cleanup): { + drop(_10) -> [return: bb13, unwind terminate(cleanup)]; + } + + bb13 (cleanup): { + drop(_11) -> [return: bb29, unwind terminate(cleanup)]; + } + + bb14 (cleanup): { + drop(_10) -> [return: bb15, unwind terminate(cleanup)]; + } + + bb15 (cleanup): { + drop(_11) -> [return: bb28, unwind terminate(cleanup)]; + } + + bb16 (cleanup): { + drop(_10) -> [return: bb17, unwind terminate(cleanup)]; + } + + bb17 (cleanup): { + drop(_11) -> [return: bb27, unwind terminate(cleanup)]; + } + + bb18 (cleanup): { +- drop(_10) -> [return: bb19, unwind terminate(cleanup)]; ++ goto -> bb19; + } + + bb19 (cleanup): { +- drop(_11) -> [return: bb26, unwind terminate(cleanup)]; ++ goto -> bb26; + } + + bb20 (cleanup): { + drop(_10) -> [return: bb21, unwind terminate(cleanup)]; + } + + bb21 (cleanup): { + drop(_11) -> [return: bb25, unwind terminate(cleanup)]; + } + + bb22 (cleanup): { + drop(_10) -> [return: bb24, unwind terminate(cleanup)]; + } + + bb23 (cleanup): { +- drop(_9) -> [return: bb24, unwind terminate(cleanup)]; ++ goto -> bb24; + } + + bb24 (cleanup): { + drop(_7) -> [return: bb25, unwind terminate(cleanup)]; + } + + bb25 (cleanup): { +- drop(_6) -> [return: bb26, unwind terminate(cleanup)]; ++ goto -> bb31; + } + + bb26 (cleanup): { + drop(_4) -> [return: bb27, unwind terminate(cleanup)]; + } + + bb27 (cleanup): { + drop(_2) -> [return: bb28, unwind terminate(cleanup)]; + } + + bb28 (cleanup): { + drop(_1) -> [return: bb29, unwind terminate(cleanup)]; + } + + bb29 (cleanup): { + resume; ++ } ++ ++ bb30 (cleanup): { ++ drop(_6) -> [return: bb26, unwind terminate(cleanup)]; ++ } ++ ++ bb31 (cleanup): { ++ switchInt(_12) -> [0: bb26, otherwise: bb30]; + } + } + diff --git a/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-unwind.diff b/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-unwind.diff new file mode 100644 index 00000000000..c7df2bb2207 --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-unwind.diff @@ -0,0 +1,184 @@ +- // MIR for `f_with_arg` before ElaborateDrops ++ // MIR for `f_with_arg` after ElaborateDrops + + fn f_with_arg(_1: String, _2: String) -> () { + debug _arg1 => _1; + debug _arg2 => _2; + let mut _0: (); + let mut _3: !; + let _4: std::string::String; + let _8: (); + let mut _9: std::string::String; + let mut _10: std::string::String; + let mut _11: std::string::String; ++ let mut _12: bool; + scope 1 { + debug _a => _4; + let _5: i32; + scope 2 { + debug _b => _5; + let _6: std::string::String; + scope 3 { + debug _c => _6; + let _7: std::string::String; + scope 4 { + debug _d => _7; + } + } + } + } + + bb0: { ++ _12 = const false; + StorageLive(_4); + _4 = String::new() -> [return: bb1, unwind: bb27]; + } + + bb1: { + StorageLive(_5); + _5 = const 12_i32; + StorageLive(_6); + _6 = String::new() -> [return: bb2, unwind: bb26]; + } + + bb2: { ++ _12 = const true; + StorageLive(_7); + _7 = String::new() -> [return: bb3, unwind: bb25]; + } + + bb3: { + StorageLive(_8); + StorageLive(_9); ++ _12 = const false; + _9 = move _6; + _8 = std::mem::drop::<String>(move _9) -> [return: bb4, unwind: bb23]; + } + + bb4: { + StorageDead(_9); + StorageDead(_8); + StorageLive(_10); + _10 = String::new() -> [return: bb5, unwind: bb24]; + } + + bb5: { + StorageLive(_11); + _11 = String::new() -> [return: bb6, unwind: bb22]; + } + + bb6: { + drop(_7) -> [return: bb7, unwind: bb20]; + } + + bb7: { + StorageDead(_7); +- drop(_6) -> [return: bb8, unwind: bb18]; ++ goto -> bb8; + } + + bb8: { ++ _12 = const false; + StorageDead(_6); + StorageDead(_5); + drop(_4) -> [return: bb9, unwind: bb16]; + } + + bb9: { + StorageDead(_4); + drop(_2) -> [return: bb10, unwind: bb14]; + } + + bb10: { + drop(_1) -> [return: bb11, unwind: bb12]; + } + + bb11: { + tailcall g_with_arg(Spanned { node: move _10, span: $DIR/tail_call_drops.rs:36:23: 36:36 (#0) }, Spanned { node: move _11, span: $DIR/tail_call_drops.rs:36:38: 36:51 (#0) }); + } + + bb12 (cleanup): { + drop(_10) -> [return: bb13, unwind terminate(cleanup)]; + } + + bb13 (cleanup): { + drop(_11) -> [return: bb29, unwind terminate(cleanup)]; + } + + bb14 (cleanup): { + drop(_10) -> [return: bb15, unwind terminate(cleanup)]; + } + + bb15 (cleanup): { + drop(_11) -> [return: bb28, unwind terminate(cleanup)]; + } + + bb16 (cleanup): { + drop(_10) -> [return: bb17, unwind terminate(cleanup)]; + } + + bb17 (cleanup): { + drop(_11) -> [return: bb27, unwind terminate(cleanup)]; + } + + bb18 (cleanup): { +- drop(_10) -> [return: bb19, unwind terminate(cleanup)]; ++ goto -> bb19; + } + + bb19 (cleanup): { +- drop(_11) -> [return: bb26, unwind terminate(cleanup)]; ++ goto -> bb26; + } + + bb20 (cleanup): { + drop(_10) -> [return: bb21, unwind terminate(cleanup)]; + } + + bb21 (cleanup): { + drop(_11) -> [return: bb25, unwind terminate(cleanup)]; + } + + bb22 (cleanup): { + drop(_10) -> [return: bb24, unwind terminate(cleanup)]; + } + + bb23 (cleanup): { +- drop(_9) -> [return: bb24, unwind terminate(cleanup)]; ++ goto -> bb24; + } + + bb24 (cleanup): { + drop(_7) -> [return: bb25, unwind terminate(cleanup)]; + } + + bb25 (cleanup): { +- drop(_6) -> [return: bb26, unwind terminate(cleanup)]; ++ goto -> bb31; + } + + bb26 (cleanup): { + drop(_4) -> [return: bb27, unwind terminate(cleanup)]; + } + + bb27 (cleanup): { + drop(_2) -> [return: bb28, unwind terminate(cleanup)]; + } + + bb28 (cleanup): { + drop(_1) -> [return: bb29, unwind terminate(cleanup)]; + } + + bb29 (cleanup): { + resume; ++ } ++ ++ bb30 (cleanup): { ++ drop(_6) -> [return: bb26, unwind terminate(cleanup)]; ++ } ++ ++ bb31 (cleanup): { ++ switchInt(_12) -> [0: bb26, otherwise: bb30]; + } + } + diff --git a/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-abort.mir b/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-abort.mir new file mode 100644 index 00000000000..744f1989acc --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-abort.mir @@ -0,0 +1,202 @@ +// MIR for `f_with_arg` after built + +fn f_with_arg(_1: String, _2: String) -> () { + debug _arg1 => _1; + debug _arg2 => _2; + let mut _0: (); + let mut _3: !; + let _4: std::string::String; + let _8: (); + let mut _9: std::string::String; + let mut _10: std::string::String; + let mut _11: std::string::String; + scope 1 { + debug _a => _4; + let _5: i32; + scope 2 { + debug _b => _5; + let _6: std::string::String; + scope 3 { + debug _c => _6; + let _7: std::string::String; + scope 4 { + debug _d => _7; + } + } + } + } + + bb0: { + StorageLive(_4); + _4 = String::new() -> [return: bb1, unwind: bb34]; + } + + bb1: { + FakeRead(ForLet(None), _4); + StorageLive(_5); + _5 = const 12_i32; + FakeRead(ForLet(None), _5); + StorageLive(_6); + _6 = String::new() -> [return: bb2, unwind: bb33]; + } + + bb2: { + FakeRead(ForLet(None), _6); + StorageLive(_7); + _7 = String::new() -> [return: bb3, unwind: bb32]; + } + + bb3: { + FakeRead(ForLet(None), _7); + StorageLive(_8); + StorageLive(_9); + _9 = move _6; + _8 = std::mem::drop::<String>(move _9) -> [return: bb4, unwind: bb30]; + } + + bb4: { + StorageDead(_9); + StorageDead(_8); + StorageLive(_10); + _10 = String::new() -> [return: bb5, unwind: bb31]; + } + + bb5: { + StorageLive(_11); + _11 = String::new() -> [return: bb6, unwind: bb29]; + } + + bb6: { + drop(_7) -> [return: bb7, unwind: bb27]; + } + + bb7: { + StorageDead(_7); + drop(_6) -> [return: bb8, unwind: bb25]; + } + + bb8: { + StorageDead(_6); + StorageDead(_5); + drop(_4) -> [return: bb9, unwind: bb23]; + } + + bb9: { + StorageDead(_4); + drop(_2) -> [return: bb10, unwind: bb21]; + } + + bb10: { + drop(_1) -> [return: bb11, unwind: bb19]; + } + + bb11: { + tailcall g_with_arg(Spanned { node: move _10, span: $DIR/tail_call_drops.rs:36:23: 36:36 (#0) }, Spanned { node: move _11, span: $DIR/tail_call_drops.rs:36:38: 36:51 (#0) }); + } + + bb12: { + StorageDead(_11); + StorageDead(_10); + drop(_7) -> [return: bb13, unwind: bb32]; + } + + bb13: { + StorageDead(_7); + drop(_6) -> [return: bb14, unwind: bb33]; + } + + bb14: { + StorageDead(_6); + StorageDead(_5); + drop(_4) -> [return: bb15, unwind: bb34]; + } + + bb15: { + StorageDead(_4); + unreachable; + } + + bb16: { + drop(_2) -> [return: bb17, unwind: bb35]; + } + + bb17: { + drop(_1) -> [return: bb18, unwind: bb36]; + } + + bb18: { + return; + } + + bb19 (cleanup): { + drop(_10) -> [return: bb20, unwind terminate(cleanup)]; + } + + bb20 (cleanup): { + drop(_11) -> [return: bb36, unwind terminate(cleanup)]; + } + + bb21 (cleanup): { + drop(_10) -> [return: bb22, unwind terminate(cleanup)]; + } + + bb22 (cleanup): { + drop(_11) -> [return: bb35, unwind terminate(cleanup)]; + } + + bb23 (cleanup): { + drop(_10) -> [return: bb24, unwind terminate(cleanup)]; + } + + bb24 (cleanup): { + drop(_11) -> [return: bb34, unwind terminate(cleanup)]; + } + + bb25 (cleanup): { + drop(_10) -> [return: bb26, unwind terminate(cleanup)]; + } + + bb26 (cleanup): { + drop(_11) -> [return: bb33, unwind terminate(cleanup)]; + } + + bb27 (cleanup): { + drop(_10) -> [return: bb28, unwind terminate(cleanup)]; + } + + bb28 (cleanup): { + drop(_11) -> [return: bb32, unwind terminate(cleanup)]; + } + + bb29 (cleanup): { + drop(_10) -> [return: bb31, unwind terminate(cleanup)]; + } + + bb30 (cleanup): { + drop(_9) -> [return: bb31, unwind terminate(cleanup)]; + } + + bb31 (cleanup): { + drop(_7) -> [return: bb32, unwind terminate(cleanup)]; + } + + bb32 (cleanup): { + drop(_6) -> [return: bb33, unwind terminate(cleanup)]; + } + + bb33 (cleanup): { + drop(_4) -> [return: bb34, unwind terminate(cleanup)]; + } + + bb34 (cleanup): { + drop(_2) -> [return: bb35, unwind terminate(cleanup)]; + } + + bb35 (cleanup): { + drop(_1) -> [return: bb36, unwind terminate(cleanup)]; + } + + bb36 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-unwind.mir b/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-unwind.mir new file mode 100644 index 00000000000..744f1989acc --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-unwind.mir @@ -0,0 +1,202 @@ +// MIR for `f_with_arg` after built + +fn f_with_arg(_1: String, _2: String) -> () { + debug _arg1 => _1; + debug _arg2 => _2; + let mut _0: (); + let mut _3: !; + let _4: std::string::String; + let _8: (); + let mut _9: std::string::String; + let mut _10: std::string::String; + let mut _11: std::string::String; + scope 1 { + debug _a => _4; + let _5: i32; + scope 2 { + debug _b => _5; + let _6: std::string::String; + scope 3 { + debug _c => _6; + let _7: std::string::String; + scope 4 { + debug _d => _7; + } + } + } + } + + bb0: { + StorageLive(_4); + _4 = String::new() -> [return: bb1, unwind: bb34]; + } + + bb1: { + FakeRead(ForLet(None), _4); + StorageLive(_5); + _5 = const 12_i32; + FakeRead(ForLet(None), _5); + StorageLive(_6); + _6 = String::new() -> [return: bb2, unwind: bb33]; + } + + bb2: { + FakeRead(ForLet(None), _6); + StorageLive(_7); + _7 = String::new() -> [return: bb3, unwind: bb32]; + } + + bb3: { + FakeRead(ForLet(None), _7); + StorageLive(_8); + StorageLive(_9); + _9 = move _6; + _8 = std::mem::drop::<String>(move _9) -> [return: bb4, unwind: bb30]; + } + + bb4: { + StorageDead(_9); + StorageDead(_8); + StorageLive(_10); + _10 = String::new() -> [return: bb5, unwind: bb31]; + } + + bb5: { + StorageLive(_11); + _11 = String::new() -> [return: bb6, unwind: bb29]; + } + + bb6: { + drop(_7) -> [return: bb7, unwind: bb27]; + } + + bb7: { + StorageDead(_7); + drop(_6) -> [return: bb8, unwind: bb25]; + } + + bb8: { + StorageDead(_6); + StorageDead(_5); + drop(_4) -> [return: bb9, unwind: bb23]; + } + + bb9: { + StorageDead(_4); + drop(_2) -> [return: bb10, unwind: bb21]; + } + + bb10: { + drop(_1) -> [return: bb11, unwind: bb19]; + } + + bb11: { + tailcall g_with_arg(Spanned { node: move _10, span: $DIR/tail_call_drops.rs:36:23: 36:36 (#0) }, Spanned { node: move _11, span: $DIR/tail_call_drops.rs:36:38: 36:51 (#0) }); + } + + bb12: { + StorageDead(_11); + StorageDead(_10); + drop(_7) -> [return: bb13, unwind: bb32]; + } + + bb13: { + StorageDead(_7); + drop(_6) -> [return: bb14, unwind: bb33]; + } + + bb14: { + StorageDead(_6); + StorageDead(_5); + drop(_4) -> [return: bb15, unwind: bb34]; + } + + bb15: { + StorageDead(_4); + unreachable; + } + + bb16: { + drop(_2) -> [return: bb17, unwind: bb35]; + } + + bb17: { + drop(_1) -> [return: bb18, unwind: bb36]; + } + + bb18: { + return; + } + + bb19 (cleanup): { + drop(_10) -> [return: bb20, unwind terminate(cleanup)]; + } + + bb20 (cleanup): { + drop(_11) -> [return: bb36, unwind terminate(cleanup)]; + } + + bb21 (cleanup): { + drop(_10) -> [return: bb22, unwind terminate(cleanup)]; + } + + bb22 (cleanup): { + drop(_11) -> [return: bb35, unwind terminate(cleanup)]; + } + + bb23 (cleanup): { + drop(_10) -> [return: bb24, unwind terminate(cleanup)]; + } + + bb24 (cleanup): { + drop(_11) -> [return: bb34, unwind terminate(cleanup)]; + } + + bb25 (cleanup): { + drop(_10) -> [return: bb26, unwind terminate(cleanup)]; + } + + bb26 (cleanup): { + drop(_11) -> [return: bb33, unwind terminate(cleanup)]; + } + + bb27 (cleanup): { + drop(_10) -> [return: bb28, unwind terminate(cleanup)]; + } + + bb28 (cleanup): { + drop(_11) -> [return: bb32, unwind terminate(cleanup)]; + } + + bb29 (cleanup): { + drop(_10) -> [return: bb31, unwind terminate(cleanup)]; + } + + bb30 (cleanup): { + drop(_9) -> [return: bb31, unwind terminate(cleanup)]; + } + + bb31 (cleanup): { + drop(_7) -> [return: bb32, unwind terminate(cleanup)]; + } + + bb32 (cleanup): { + drop(_6) -> [return: bb33, unwind terminate(cleanup)]; + } + + bb33 (cleanup): { + drop(_4) -> [return: bb34, unwind terminate(cleanup)]; + } + + bb34 (cleanup): { + drop(_2) -> [return: bb35, unwind terminate(cleanup)]; + } + + bb35 (cleanup): { + drop(_1) -> [return: bb36, unwind terminate(cleanup)]; + } + + bb36 (cleanup): { + resume; + } +} diff --git a/tests/mir-opt/tail_call_drops.rs b/tests/mir-opt/tail_call_drops.rs new file mode 100644 index 00000000000..56f4852a95f --- /dev/null +++ b/tests/mir-opt/tail_call_drops.rs @@ -0,0 +1,41 @@ +// skip-filecheck +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +#![allow(incomplete_features)] +#![feature(explicit_tail_calls)] + +// EMIT_MIR tail_call_drops.f.built.after.mir +// Expected result: +// drop(_d) -> drop(_c) -> drop(_a) -> tailcall g() +// +// EMIT_MIR tail_call_drops.f.ElaborateDrops.diff +// Expected result: +// drop(_d) -> drop(_a) -> tailcall g() +fn f() { + let _a = String::new(); + let _b = 12; + let _c = String::new(); + let _d = String::new(); + + drop(_c); + + become g(); +} + +fn g() {} + +// EMIT_MIR tail_call_drops.f_with_arg.built.after.mir +// EMIT_MIR tail_call_drops.f_with_arg.ElaborateDrops.diff +fn f_with_arg(_arg1: String, _arg2: String) { + let _a = String::new(); + let _b = 12; + let _c = String::new(); + let _d = String::new(); + + drop(_c); + + become g_with_arg(String::new(), String::new()); +} + +fn g_with_arg(_arg1: String, _arg2: String) {} + +fn main() {} diff --git a/tests/pretty/issue-4264.pp b/tests/pretty/issue-4264.pp index 018ccf82dae..fa958d9f1e8 100644 --- a/tests/pretty/issue-4264.pp +++ b/tests/pretty/issue-4264.pp @@ -29,16 +29,17 @@ fn bar() ({ - ({ - let res = - ((::alloc::fmt::format as - for<'a> fn(Arguments<'a>) -> String {format})(((format_arguments::new_const - as - fn(&[&'static str; 1]) -> Arguments<'_> {Arguments::<'_>::new_const::<1>})((&([("test" - as &str)] as [&str; 1]) as &[&str; 1])) as Arguments<'_>)) - as String); - (res as String) - } as String); + ((::alloc::__export::must_use as + fn(String) -> String {must_use::<String>})(({ + let res = + ((::alloc::fmt::format as + for<'a> fn(Arguments<'a>) -> String {format})(((format_arguments::new_const + as + fn(&[&'static str; 1]) -> Arguments<'_> {Arguments::<'_>::new_const::<1>})((&([("test" + as &str)] as [&str; 1]) as &[&str; 1])) as Arguments<'_>)) + as String); + (res as String) + } as String)) as String); } as ()) type Foo = [i32; (3 as usize)]; struct Bar { diff --git a/tests/run-make/emit-path-unhashed/Makefile b/tests/run-make/emit-path-unhashed/Makefile deleted file mode 100644 index 611f8578140..00000000000 --- a/tests/run-make/emit-path-unhashed/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -include ../tools.mk - -OUT=$(TMPDIR)/emit - -# --emit KIND=PATH should not affect crate hash vs --emit KIND -all: $(OUT)/a/libfoo.rlib $(OUT)/b/libfoo.rlib $(OUT)/c/libfoo.rlib \ - $(TMPDIR)/libfoo.rlib - $(RUSTC) -Zls=root $(TMPDIR)/libfoo.rlib > $(TMPDIR)/base.txt - $(RUSTC) -Zls=root $(OUT)/a/libfoo.rlib > $(TMPDIR)/a.txt - $(RUSTC) -Zls=root $(OUT)/b/libfoo.rlib > $(TMPDIR)/b.txt - $(RUSTC) -Zls=root $(OUT)/c/libfoo.rlib > $(TMPDIR)/c.txt - - diff $(TMPDIR)/base.txt $(TMPDIR)/a.txt - diff $(TMPDIR)/base.txt $(TMPDIR)/b.txt - - # Different KIND parameters do affect hash. - # diff exits 1 on difference, 2 on trouble - diff $(TMPDIR)/base.txt $(TMPDIR)/c.txt ; test "$$?" -eq 1 - -# Default output name -$(TMPDIR)/libfoo.rlib: foo.rs - $(RUSTC) --emit link foo.rs - -# Output named with -o -$(OUT)/a/libfoo.rlib: foo.rs - mkdir -p $(OUT)/a - $(RUSTC) --emit link -o $@ foo.rs - -# Output named with KIND=PATH -$(OUT)/b/libfoo.rlib: foo.rs - mkdir -p $(OUT)/b - $(RUSTC) --emit link=$@ foo.rs - -# Output multiple kinds -$(OUT)/c/libfoo.rlib: foo.rs - mkdir -p $(OUT)/c - $(RUSTC) --emit link=$@,metadata foo.rs diff --git a/tests/run-make/emit-path-unhashed/rmake.rs b/tests/run-make/emit-path-unhashed/rmake.rs new file mode 100644 index 00000000000..ce56c197588 --- /dev/null +++ b/tests/run-make/emit-path-unhashed/rmake.rs @@ -0,0 +1,34 @@ +// Specifying how rustc outputs a file can be done in different ways, such as +// the output flag or the KIND=NAME syntax. However, some of these methods used +// to result in different hashes on output files even though they yielded the +// exact same result otherwise. This was fixed in #86045, and this test checks +// that the hash is only modified when the output is made different, such as by +// adding a new output type (in this test, metadata). +// See https://github.com/rust-lang/rust/issues/86044 + +use run_make_support::{diff, fs_wrapper, rustc}; + +fn main() { + fs_wrapper::create_dir("emit"); + fs_wrapper::create_dir("emit/a"); + fs_wrapper::create_dir("emit/b"); + fs_wrapper::create_dir("emit/c"); + // The default output name. + rustc().emit("link").input("foo.rs").run(); + // The output is named with the output flag. + rustc().emit("link").output("emit/a/libfoo.rlib").input("foo.rs").run(); + // The output is named with link=NAME. + rustc().emit("link=emit/b/libfoo.rlib").input("foo.rs").run(); + // The output is named with link=NAME, with an additional kind tacked on. + rustc().emit("link=emit/c/libfoo.rlib,metadata").input("foo.rs").run(); + + let base = rustc().arg("-Zls=root").input("libfoo.rlib").run().stdout_utf8(); + let a = rustc().arg("-Zls=root").input("emit/a/libfoo.rlib").run().stdout_utf8(); + let b = rustc().arg("-Zls=root").input("emit/b/libfoo.rlib").run().stdout_utf8(); + let c = rustc().arg("-Zls=root").input("emit/c/libfoo.rlib").run().stdout_utf8(); + // Both the output flag and link=NAME methods do not modify the hash of the output file. + diff().expected_text("base", &base).actual_text("a", a).run(); + diff().expected_text("base", &base).actual_text("b", b).run(); + // However, having multiple types of outputs does modify the hash. + diff().expected_text("base", &base).actual_text("c", c).run_fail(); +} diff --git a/tests/run-make/emit-shared-files/Makefile b/tests/run-make/emit-shared-files/Makefile deleted file mode 100644 index 27c72b00368..00000000000 --- a/tests/run-make/emit-shared-files/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -include ../tools.mk - -INVOCATION_ONLY = $(TMPDIR)/invocation-only -TOOLCHAIN_ONLY = $(TMPDIR)/toolchain-only -ALL_SHARED = $(TMPDIR)/all-shared - -all: invocation-only toolchain-only all-shared - -invocation-only: - $(RUSTDOC) -Z unstable-options --emit=invocation-specific --output $(INVOCATION_ONLY) --resource-suffix=-xxx --theme y.css --extend-css z.css x.rs - [ -e $(INVOCATION_ONLY)/search-index-xxx.js ] - [ -e $(INVOCATION_ONLY)/settings.html ] - [ -e $(INVOCATION_ONLY)/x/all.html ] - [ -e $(INVOCATION_ONLY)/x/index.html ] - [ -e $(INVOCATION_ONLY)/theme-xxx.css ] # generated from z.css - ! [ -e $(INVOCATION_ONLY)/storage-xxx.js ] - ! [ -e $(INVOCATION_ONLY)/SourceSerif4-It.ttf.woff2 ] - - # FIXME: this probably shouldn't have a suffix - [ -e $(INVOCATION_ONLY)/y-xxx.css ] - # FIXME: this is technically incorrect (see `write_shared`) - ! [ -e $(INVOCATION_ONLY)/main-xxx.js ] - -toolchain-only: - $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources --output $(TOOLCHAIN_ONLY) --resource-suffix=-xxx --extend-css z.css x.rs - [ -e $(TOOLCHAIN_ONLY)/static.files/storage-*.js ] - [ -e $(TOOLCHAIN_ONLY)/static.files/SourceSerif4-It-*.ttf.woff2 ] - ! [ -e $(TOOLCHAIN_ONLY)/search-index-xxx.js ] - ! [ -e $(TOOLCHAIN_ONLY)/x/index.html ] - ! [ -e $(TOOLCHAIN_ONLY)/theme.css ] - - [ -e $(TOOLCHAIN_ONLY)/static.files/main-*.js ] - ! [ -e $(TOOLCHAIN_ONLY)/y-xxx.css ] - -all-shared: - $(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources,unversioned-shared-resources --output $(ALL_SHARED) --resource-suffix=-xxx --extend-css z.css x.rs - [ -e $(ALL_SHARED)/static.files/storage-*.js ] - [ -e $(ALL_SHARED)/static.files/SourceSerif4-It-*.ttf.woff2 ] - ! [ -e $(ALL_SHARED)/search-index-xxx.js ] - ! [ -e $(ALL_SHARED)/settings.html ] - ! [ -e $(ALL_SHARED)/x ] - ! [ -e $(ALL_SHARED)/src ] - ! [ -e $(ALL_SHARED)/theme.css ] - - [ -e $(ALL_SHARED)/static.files/main-*.js ] - ! [ -e $(ALL_SHARED)/y-xxx.css ] diff --git a/tests/run-make/emit-shared-files/rmake.rs b/tests/run-make/emit-shared-files/rmake.rs new file mode 100644 index 00000000000..33c12310246 --- /dev/null +++ b/tests/run-make/emit-shared-files/rmake.rs @@ -0,0 +1,102 @@ +// This test checks the functionality of one of rustdoc's unstable options, +// the ability to specify emit restrictions with `--emit`. +// `invocation-only` should only emit crate-specific files. +// `toolchain-only` should only emit toolchain-specific files. +// `all-shared` should only emit files that can be shared between crates. +// See https://github.com/rust-lang/rust/pull/83478 + +use run_make_support::{has_extension, has_prefix, rustdoc, shallow_find_files}; +use std::path::Path; + +fn main() { + rustdoc() + .arg("-Zunstable-options") + .arg("--emit=invocation-specific") + .output("invocation-only") + .arg("--resource-suffix=-xxx") + .args(&["--theme", "y.css"]) + .args(&["--extend-css", "z.css"]) + .input("x.rs") + .run(); + assert!(Path::new("invocation-only/search-index-xxx.js").exists()); + assert!(Path::new("invocation-only/settings.html").exists()); + assert!(Path::new("invocation-only/x/all.html").exists()); + assert!(Path::new("invocation-only/x/index.html").exists()); + assert!(Path::new("invocation-only/theme-xxx.css").exists()); // generated from z.css + assert!(!Path::new("invocation-only/storage-xxx.js").exists()); + assert!(!Path::new("invocation-only/SourceSerif4-It.ttf.woff2").exists()); + // FIXME: this probably shouldn't have a suffix + assert!(Path::new("invocation-only/y-xxx.css").exists()); + // FIXME: this is technically incorrect (see `write_shared`) + assert!(!Path::new("invocation-only/main-xxx.js").exists()); + + rustdoc() + .arg("-Zunstable-options") + .arg("--emit=toolchain-shared-resources") + .output("toolchain-only") + .arg("--resource-suffix=-xxx") + .args(&["--extend-css", "z.css"]) + .input("x.rs") + .run(); + assert_eq!( + shallow_find_files("toolchain-only/static.files", |path| { + has_prefix(path, "storage-") && has_extension(path, "js") + }) + .len(), + 1 + ); + assert_eq!( + shallow_find_files("toolchain-only/static.files", |path| { + has_prefix(path, "SourceSerif4-It-") && has_extension(path, "woff2") + }) + .len(), + 1 + ); + assert_eq!( + shallow_find_files("toolchain-only/static.files", |path| { + has_prefix(path, "main-") && has_extension(path, "js") + }) + .len(), + 1 + ); + assert!(!Path::new("toolchain-only/search-index-xxx.js").exists()); + assert!(!Path::new("toolchain-only/x/index.html").exists()); + assert!(!Path::new("toolchain-only/theme.css").exists()); + assert!(!Path::new("toolchain-only/y-xxx.css").exists()); + + rustdoc() + .arg("-Zunstable-options") + .arg("--emit=toolchain-shared-resources,unversioned-shared-resources") + .output("all-shared") + .arg("--resource-suffix=-xxx") + .args(&["--extend-css", "z.css"]) + .input("x.rs") + .run(); + assert_eq!( + shallow_find_files("all-shared/static.files", |path| { + has_prefix(path, "storage-") && has_extension(path, "js") + }) + .len(), + 1 + ); + assert_eq!( + shallow_find_files("all-shared/static.files", |path| { + has_prefix(path, "SourceSerif4-It-") && has_extension(path, "woff2") + }) + .len(), + 1 + ); + assert!(!Path::new("all-shared/search-index-xxx.js").exists()); + assert!(!Path::new("all-shared/settings.html").exists()); + assert!(!Path::new("all-shared/x").exists()); + assert!(!Path::new("all-shared/src").exists()); + assert!(!Path::new("all-shared/theme.css").exists()); + assert_eq!( + shallow_find_files("all-shared/static.files", |path| { + has_prefix(path, "main-") && has_extension(path, "js") + }) + .len(), + 1 + ); + assert!(!Path::new("all-shared/y-xxx.css").exists()); +} diff --git a/tests/run-make/intrinsic-unreachable/Makefile b/tests/run-make/intrinsic-unreachable/Makefile deleted file mode 100644 index ff9cc57098c..00000000000 --- a/tests/run-make/intrinsic-unreachable/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -include ../tools.mk - -# needs-asm-support -# ignore-windows-msvc -# -# Because of Windows exception handling, the code is not necessarily any shorter. -# https://github.com/llvm-mirror/llvm/commit/64b2297786f7fd6f5fa24cdd4db0298fbf211466 - -all: - $(RUSTC) -O --emit asm exit-ret.rs - $(RUSTC) -O --emit asm exit-unreachable.rs - test `wc -l < $(TMPDIR)/exit-unreachable.s` -lt `wc -l < $(TMPDIR)/exit-ret.s` diff --git a/tests/run-make/intrinsic-unreachable/rmake.rs b/tests/run-make/intrinsic-unreachable/rmake.rs new file mode 100644 index 00000000000..7e78c8288b8 --- /dev/null +++ b/tests/run-make/intrinsic-unreachable/rmake.rs @@ -0,0 +1,20 @@ +// intrinsics::unreachable tells the compiler that a certain point in the code +// is not reachable by any means, which enables some useful optimizations. +// In this test, exit-unreachable contains this instruction and exit-ret does not, +// which means the emitted artifacts should be shorter in length. +// See https://github.com/rust-lang/rust/pull/16970 + +//@ needs-asm-support +//@ ignore-windows +// Reason: Because of Windows exception handling, the code is not necessarily any shorter. + +use run_make_support::{fs_wrapper, rustc}; + +fn main() { + rustc().opt().emit("asm").input("exit-ret.rs").run(); + rustc().opt().emit("asm").input("exit-unreachable.rs").run(); + assert!( + fs_wrapper::read_to_string("exit-unreachable.s").lines().count() + < fs_wrapper::read_to_string("exit-ret.s").lines().count() + ); +} diff --git a/tests/run-make/issue-37839/Makefile b/tests/run-make/issue-37839/Makefile deleted file mode 100644 index 6bad27b7bdc..00000000000 --- a/tests/run-make/issue-37839/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: - $(RUSTC) a.rs && $(RUSTC) b.rs - $(BARE_RUSTC) c.rs -L dependency=$(TMPDIR) --extern b=$(TMPDIR)/libb.rlib \ - --out-dir=$(TMPDIR) diff --git a/tests/run-make/llvm-ident/rmake.rs b/tests/run-make/llvm-ident/rmake.rs index 6934a4b36d0..9699d0579f6 100644 --- a/tests/run-make/llvm-ident/rmake.rs +++ b/tests/run-make/llvm-ident/rmake.rs @@ -2,9 +2,9 @@ //@ ignore-cross-compile use run_make_support::llvm::llvm_bin_dir; -use run_make_support::{cmd, env_var, llvm_filecheck, read_dir, rustc, source_root}; - -use std::ffi::OsStr; +use run_make_support::{ + cmd, env_var, has_extension, llvm_filecheck, rustc, shallow_find_files, source_root, +}; fn main() { // `-Ccodegen-units=16 -Copt-level=2` is used here to trigger thin LTO @@ -22,20 +22,14 @@ fn main() { // `llvm-dis` is used here since `--emit=llvm-ir` does not emit LLVM IR // for temporary outputs. - let mut files = Vec::new(); - read_dir(".", |path| { - if path.is_file() && path.extension().is_some_and(|ext| ext == OsStr::new("bc")) { - files.push(path.to_path_buf()); - } - }); + let files = shallow_find_files(".", |path| has_extension(path, "bc")); cmd(llvm_bin_dir().join("llvm-dis")).args(files).run(); // Check LLVM IR files (including temporary outputs) have `!llvm.ident` // named metadata, reusing the related codegen test. let llvm_ident_path = source_root().join("tests/codegen/llvm-ident.rs"); - read_dir(".", |path| { - if path.is_file() && path.extension().is_some_and(|ext| ext == OsStr::new("ll")) { - llvm_filecheck().input_file(path).arg(&llvm_ident_path).run(); - } - }); + let files = shallow_find_files(".", |path| has_extension(path, "ll")); + for file in files { + llvm_filecheck().input_file(file).arg(&llvm_ident_path).run(); + } } diff --git a/tests/run-make/output-type-permutations/rmake.rs b/tests/run-make/output-type-permutations/rmake.rs index 30036dc7eea..1d1637a744e 100644 --- a/tests/run-make/output-type-permutations/rmake.rs +++ b/tests/run-make/output-type-permutations/rmake.rs @@ -17,9 +17,8 @@ use std::path::PathBuf; // `rustc_invocation`: the rustc command being tested // Any unexpected output files not listed in `must_exist` or `can_exist` will cause a failure. fn assert_expected_output_files(expectations: Expectations, rustc_invocation: impl Fn()) { - let must_exist = expectations.expected_files; - let can_exist = expectations.allowed_files; - let dir = expectations.test_dir; + let Expectations { expected_files: must_exist, allowed_files: can_exist, test_dir: dir } = + expectations; fs_wrapper::create_dir(&dir); rustc_invocation(); diff --git a/tests/run-make/pass-linker-flags-flavor/Makefile b/tests/run-make/pass-linker-flags-flavor/Makefile deleted file mode 100644 index 1bb05d0f974..00000000000 --- a/tests/run-make/pass-linker-flags-flavor/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# only-linux - -include ../tools.mk - -all: - $(RUSTC) empty.rs -Z unstable-options -C linker-flavor=gnu-cc -l static=l1 -l link-arg=a1 -l static=l2 -l link-arg=a2 -l dylib=d1 -l link-arg=a3 --print link-args | $(CGREP) -e 'l1.*-Wl,a1.*l2.*-Wl,a2.*d1.*-Wl,a3' - $(RUSTC) empty.rs -Z unstable-options -C linker-flavor=gnu-cc -l static=l1 -l link-arg:+verbatim=a1 -l static=l2 -l link-arg=a2 -l dylib=d1 -l link-arg=a3 --print link-args | $(CGREP) -e 'l1.*"a1".*l2.*-Wl,a2.*d1.*-Wl,a3' - $(RUSTC) empty.rs -Z unstable-options -C linker-flavor=ld -l static=l1 -l link-arg=a1 -l static=l2 -l link-arg=a2 -l dylib=d1 -l link-arg=a3 --print link-args | $(CGREP) -e 'l1.*"a1".*l2.*"a2".*d1.*"a3"' - $(RUSTC) attribute.rs -Z unstable-options -C linker-flavor=gnu-cc --print link-args | $(CGREP) -e 'l1.*-Wl,a1.*l2.*-Wl,a2.*d1.*-Wl,a3' - $(RUSTC) --cfg 'feature="verbatim"' attribute.rs -Z unstable-options -C linker-flavor=gnu-cc --print link-args | $(CGREP) -e 'l1.*"a1".*l2.*-Wl,a2.*d1.*-Wl,a3' - $(RUSTC) attribute.rs -C linker-flavor=ld --print link-args | $(CGREP) -e 'l1.*"a1".*l2.*"a2".*d1.*"a3"' diff --git a/tests/run-make/pass-linker-flags-flavor/rmake.rs b/tests/run-make/pass-linker-flags-flavor/rmake.rs new file mode 100644 index 00000000000..e36d68cc884 --- /dev/null +++ b/tests/run-make/pass-linker-flags-flavor/rmake.rs @@ -0,0 +1,84 @@ +// Setting the linker flavor as a C compiler should cause the output of the -l flags to be +// prefixed by -Wl, except when a flag is requested to be verbatim. A bare linker (ld) should +// never cause prefixes to appear in the output. This test checks this ruleset twice, once with +// explicit flags and then with those flags passed inside the rust source code. +// See https://github.com/rust-lang/rust/pull/118202 + +//@ only-linux +// Reason: the `gnu-cc` linker is only available on linux + +use run_make_support::{regex, rustc}; + +fn main() { + let out_gnu = rustc() + .input("empty.rs") + .linker_flavor("gnu-cc") + .arg("-Zunstable-options") + .arg("-lstatic=l1") + .arg("-llink-arg=a1") + .arg("-lstatic=l2") + .arg("-llink-arg=a2") + .arg("-ldylib=d1") + .arg("-llink-arg=a3") + .print("link-args") + .run_unchecked() + .stdout_utf8(); + let out_gnu_verbatim = rustc() + .input("empty.rs") + .linker_flavor("gnu-cc") + .arg("-Zunstable-options") + .arg("-lstatic=l1") + .arg("-llink-arg:+verbatim=a1") + .arg("-lstatic=l2") + .arg("-llink-arg=a2") + .arg("-ldylib=d1") + .arg("-llink-arg=a3") + .print("link-args") + .run_unchecked() + .stdout_utf8(); + let out_ld = rustc() + .input("empty.rs") + .linker_flavor("ld") + .arg("-Zunstable-options") + .arg("-lstatic=l1") + .arg("-llink-arg=a1") + .arg("-lstatic=l2") + .arg("-llink-arg=a2") + .arg("-ldylib=d1") + .arg("-llink-arg=a3") + .print("link-args") + .run_unchecked() + .stdout_utf8(); + let out_att_gnu = rustc() + .arg("-Zunstable-options") + .linker_flavor("gnu-cc") + .input("attribute.rs") + .print("link-args") + .run_unchecked() + .stdout_utf8(); + let out_att_gnu_verbatim = rustc() + .cfg(r#"feature="verbatim""#) + .arg("-Zunstable-options") + .linker_flavor("gnu-cc") + .input("attribute.rs") + .print("link-args") + .run_unchecked() + .stdout_utf8(); + let out_att_ld = rustc() + .linker_flavor("ld") + .input("attribute.rs") + .print("link-args") + .run_unchecked() + .stdout_utf8(); + + let no_verbatim = regex::Regex::new("l1.*-Wl,a1.*l2.*-Wl,a2.*d1.*-Wl,a3").unwrap(); + let one_verbatim = regex::Regex::new(r#"l1.*"a1".*l2.*-Wl,a2.*d1.*-Wl,a3"#).unwrap(); + let ld = regex::Regex::new(r#"l1.*"a1".*l2.*"a2".*d1.*"a3""#).unwrap(); + + assert!(no_verbatim.is_match(&out_gnu)); + assert!(no_verbatim.is_match(&out_att_gnu)); + assert!(one_verbatim.is_match(&out_gnu_verbatim)); + assert!(one_verbatim.is_match(&out_att_gnu_verbatim)); + assert!(ld.is_match(&out_ld)); + assert!(ld.is_match(&out_att_ld)); +} diff --git a/tests/run-make/pass-linker-flags-from-dep/Makefile b/tests/run-make/pass-linker-flags-from-dep/Makefile deleted file mode 100644 index 48b3b26ce81..00000000000 --- a/tests/run-make/pass-linker-flags-from-dep/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -include ../tools.mk - -all: - # Build deps - $(RUSTC) native_dep_1.rs --crate-type=staticlib - $(RUSTC) native_dep_2.rs --crate-type=staticlib - $(RUSTC) rust_dep_flag.rs -l static:-bundle=native_dep_1 -l link-arg=some_flag -l static:-bundle=native_dep_2 --crate-type=lib -Z unstable-options - $(RUSTC) rust_dep_attr.rs --crate-type=lib - - # Check sequence of linker args - $(RUSTC) main.rs --extern lib=$(TMPDIR)/librust_dep_flag.rlib --crate-type=bin --print link-args | $(CGREP) -e 'native_dep_1.*some_flag.*native_dep_2' - $(RUSTC) main.rs --extern lib=$(TMPDIR)/librust_dep_attr.rlib --crate-type=bin --print link-args | $(CGREP) -e 'native_dep_1.*some_flag.*native_dep_2' diff --git a/tests/run-make/pass-linker-flags-from-dep/rmake.rs b/tests/run-make/pass-linker-flags-from-dep/rmake.rs new file mode 100644 index 00000000000..4b8e0486e14 --- /dev/null +++ b/tests/run-make/pass-linker-flags-from-dep/rmake.rs @@ -0,0 +1,43 @@ +// A similar test to pass-linker-flags, testing that the `-l link-arg` flag +// respects the order relative to other `-l` flags, but this time, the flags +// are passed on the compilation of a dependency. This test checks that the +// downstream compiled binary contains the linker arguments of the dependency, +// and in the correct order. +// See https://github.com/rust-lang/rust/issues/99427 + +use run_make_support::{regex, rust_lib_name, rustc}; + +fn main() { + // Build dependencies + rustc().input("native_dep_1.rs").crate_type("staticlib").run(); + rustc().input("native_dep_2.rs").crate_type("staticlib").run(); + rustc() + .input("rust_dep_flag.rs") + .arg("-lstatic:-bundle=native_dep_1") + .arg("-llink-arg=some_flag") + .arg("-lstatic:-bundle=native_dep_2") + .crate_type("lib") + .arg("-Zunstable-options") + .run(); + rustc().input("rust_dep_attr.rs").crate_type("lib").run(); + + // Check sequence of linker arguments + let out_flag = rustc() + .input("main.rs") + .extern_("lib", rust_lib_name("rust_dep_flag")) + .crate_type("bin") + .print("link-args") + .run_unchecked() + .stdout_utf8(); + let out_attr = rustc() + .input("main.rs") + .extern_("lib", rust_lib_name("rust_dep_attr")) + .crate_type("bin") + .print("link-args") + .run_unchecked() + .stdout_utf8(); + + let re = regex::Regex::new("native_dep_1.*some_flag.*native_dep_2").unwrap(); + assert!(re.is_match(&out_flag)); + assert!(re.is_match(&out_attr)); +} diff --git a/tests/run-make/pass-linker-flags/Makefile b/tests/run-make/pass-linker-flags/Makefile deleted file mode 100644 index 226943e93bd..00000000000 --- a/tests/run-make/pass-linker-flags/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -include ../tools.mk - -all: - $(RUSTC) empty.rs -Z unstable-options -l static=l1 -l link-arg=a1 -l static=l2 -l link-arg=a2 -l dylib=d1 -l link-arg=a3 --print link-args | $(CGREP) -e 'l1.*a1.*l2.*a2.*d1.*a3' - $(RUSTC) attribute.rs --print link-args | $(CGREP) -e 'l1.*a1.*l2.*a2.*d1.*a3' diff --git a/tests/run-make/pass-linker-flags/rmake.rs b/tests/run-make/pass-linker-flags/rmake.rs new file mode 100644 index 00000000000..de69567a6e6 --- /dev/null +++ b/tests/run-make/pass-linker-flags/rmake.rs @@ -0,0 +1,28 @@ +// This test checks the proper function of `-l link-arg=NAME`, which, unlike +// -C link-arg, is supposed to guarantee that the order relative to other -l +// options will be respected. In this test, compilation fails (because none of the +// link arguments like `a1` exist), but it is still checked if the output contains the +// link arguments in the exact order they were passed in. `attribute.rs` is a variant +// of the test where the flags are defined in the rust file itself. +// See https://github.com/rust-lang/rust/issues/99427 + +use run_make_support::{regex, rustc}; + +fn main() { + let out = rustc() + .input("empty.rs") + .arg("-Zunstable-options") + .arg("-lstatic=l1") + .arg("-llink-arg=a1") + .arg("-lstatic=l2") + .arg("-llink-arg=a2") + .arg("-ldylib=d1") + .arg("-llink-arg=a3") + .print("link-args") + .run_unchecked() + .stdout_utf8(); + let out2 = rustc().input("attribute.rs").print("link-args").run_unchecked().stdout_utf8(); + let re = regex::Regex::new("l1.*a1.*l2.*a2.*d1.*a3").unwrap(); + assert!(re.is_match(&out)); + assert!(re.is_match(&out2)); +} diff --git a/tests/run-make/issue-37839/a.rs b/tests/run-make/proc-macro-three-crates/a.rs index b5dffac3ff6..b5dffac3ff6 100644 --- a/tests/run-make/issue-37839/a.rs +++ b/tests/run-make/proc-macro-three-crates/a.rs diff --git a/tests/run-make/issue-37839/b.rs b/tests/run-make/proc-macro-three-crates/b.rs index 067f47c1b7a..067f47c1b7a 100644 --- a/tests/run-make/issue-37839/b.rs +++ b/tests/run-make/proc-macro-three-crates/b.rs diff --git a/tests/run-make/issue-37839/c.rs b/tests/run-make/proc-macro-three-crates/c.rs index 4c7ce01b6a0..4c7ce01b6a0 100644 --- a/tests/run-make/issue-37839/c.rs +++ b/tests/run-make/proc-macro-three-crates/c.rs diff --git a/tests/run-make/proc-macro-three-crates/rmake.rs b/tests/run-make/proc-macro-three-crates/rmake.rs new file mode 100644 index 00000000000..d3735540fdd --- /dev/null +++ b/tests/run-make/proc-macro-three-crates/rmake.rs @@ -0,0 +1,20 @@ +// A compiler bug caused the following issue: +// If a crate A depends on crate B, and crate B +// depends on crate C, and crate C contains a procedural +// macro, compiling crate A would fail. +// This was fixed in #37846, and this test checks +// that this bug does not make a resurgence. + +use run_make_support::{bare_rustc, cwd, rust_lib_name, rustc}; + +fn main() { + rustc().input("a.rs").run(); + rustc().input("b.rs").run(); + let curr_dir = cwd().display().to_string(); + bare_rustc() + .input("c.rs") + .arg(format!("-Ldependency={curr_dir}")) + .extern_("b", cwd().join(rust_lib_name("b"))) + .out_dir(cwd()) + .run(); +} diff --git a/tests/run-make/sepcomp-cci-copies/Makefile b/tests/run-make/sepcomp-cci-copies/Makefile deleted file mode 100644 index df289d0b0b1..00000000000 --- a/tests/run-make/sepcomp-cci-copies/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -include ../tools.mk - -# Check that cross-crate inlined items are inlined in all compilation units -# that refer to them, and not in any other compilation units. -# Note that we have to pass `-C codegen-units=6` because up to two CGUs may be -# created for each source module (see `rustc_const_eval::monomorphize::partitioning`). - -all: - $(RUSTC) cci_lib.rs - $(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=6 \ - -Z inline-in-all-cgus - [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ .*cci_fn)" -eq "2" ] diff --git a/tests/run-make/sepcomp-cci-copies/rmake.rs b/tests/run-make/sepcomp-cci-copies/rmake.rs new file mode 100644 index 00000000000..612a73977fe --- /dev/null +++ b/tests/run-make/sepcomp-cci-copies/rmake.rs @@ -0,0 +1,17 @@ +// Check that cross-crate inlined items are inlined in all compilation units +// that refer to them, and not in any other compilation units. +// Note that we have to pass `-C codegen-units=6` because up to two CGUs may be +// created for each source module (see `rustc_const_eval::monomorphize::partitioning`). +// See https://github.com/rust-lang/rust/pull/16367 + +use run_make_support::{ + count_regex_matches_in_files_with_extension, cwd, fs_wrapper, has_extension, regex, rustc, + shallow_find_files, +}; + +fn main() { + rustc().input("cci_lib.rs").run(); + rustc().input("foo.rs").emit("llvm-ir").codegen_units(6).arg("-Zinline-in-all-cgus").run(); + let re = regex::Regex::new(r#"define\ .*cci_fn"#).unwrap(); + assert_eq!(count_regex_matches_in_files_with_extension(&re, "ll"), 2); +} diff --git a/tests/run-make/sepcomp-inlining/Makefile b/tests/run-make/sepcomp-inlining/Makefile deleted file mode 100644 index 327aeb75e5e..00000000000 --- a/tests/run-make/sepcomp-inlining/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -include ../tools.mk - -# Test that #[inline] functions still get inlined across compilation unit -# boundaries. Compilation should produce three IR files, but only the two -# compilation units that have a usage of the #[inline] function should -# contain a definition. Also, the non-#[inline] function should be defined -# in only one compilation unit. - -all: - $(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=3 \ - -Z inline-in-all-cgus - [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ i32\ .*inlined)" -eq "0" ] - [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ internal\ i32\ .*inlined)" -eq "2" ] - [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ hidden\ i32\ .*normal)" -eq "1" ] - [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c declare\ hidden\ i32\ .*normal)" -eq "2" ] diff --git a/tests/run-make/sepcomp-inlining/rmake.rs b/tests/run-make/sepcomp-inlining/rmake.rs new file mode 100644 index 00000000000..de7551b9a51 --- /dev/null +++ b/tests/run-make/sepcomp-inlining/rmake.rs @@ -0,0 +1,23 @@ +// Test that #[inline] functions still get inlined across compilation unit +// boundaries. Compilation should produce three IR files, but only the two +// compilation units that have a usage of the #[inline] function should +// contain a definition. Also, the non-#[inline] function should be defined +// in only one compilation unit. +// See https://github.com/rust-lang/rust/pull/16367 + +use run_make_support::{ + count_regex_matches_in_files_with_extension, cwd, fs_wrapper, has_extension, regex, rustc, + shallow_find_files, +}; + +fn main() { + rustc().input("foo.rs").emit("llvm-ir").codegen_units(3).arg("-Zinline-in-all-cgus").run(); + let re = regex::Regex::new(r#"define\ i32\ .*inlined"#).unwrap(); + assert_eq!(count_regex_matches_in_files_with_extension(&re, "ll"), 0); + let re = regex::Regex::new(r#"define\ internal\ .*inlined"#).unwrap(); + assert_eq!(count_regex_matches_in_files_with_extension(&re, "ll"), 2); + let re = regex::Regex::new(r#"define\ hidden\ i32\ .*normal"#).unwrap(); + assert_eq!(count_regex_matches_in_files_with_extension(&re, "ll"), 1); + let re = regex::Regex::new(r#"declare\ hidden\ i32\ .*normal"#).unwrap(); + assert_eq!(count_regex_matches_in_files_with_extension(&re, "ll"), 2); +} diff --git a/tests/run-make/sepcomp-separate/Makefile b/tests/run-make/sepcomp-separate/Makefile deleted file mode 100644 index 62cf54a88fb..00000000000 --- a/tests/run-make/sepcomp-separate/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -include ../tools.mk - -# Test that separate compilation actually puts code into separate compilation -# units. `foo.rs` defines `magic_fn` in three different modules, which should -# wind up in three different compilation units. - -all: - $(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=3 - [ "$$(cat "$(TMPDIR)"/foo.*.ll | grep -c define\ .*magic_fn)" -eq "3" ] diff --git a/tests/run-make/sepcomp-separate/rmake.rs b/tests/run-make/sepcomp-separate/rmake.rs new file mode 100644 index 00000000000..6f1d22424b5 --- /dev/null +++ b/tests/run-make/sepcomp-separate/rmake.rs @@ -0,0 +1,15 @@ +// Test that separate compilation actually puts code into separate compilation +// units. `foo.rs` defines `magic_fn` in three different modules, which should +// wind up in three different compilation units. +// See https://github.com/rust-lang/rust/pull/16367 + +use run_make_support::{ + count_regex_matches_in_files_with_extension, cwd, fs_wrapper, has_extension, regex, rustc, + shallow_find_files, +}; + +fn main() { + rustc().input("foo.rs").emit("llvm-ir").codegen_units(3).run(); + let re = regex::Regex::new(r#"define\ .*magic_fn"#).unwrap(); + assert_eq!(count_regex_matches_in_files_with_extension(&re, "ll"), 3); +} diff --git a/tests/run-make/target-cpu-native/Makefile b/tests/run-make/target-cpu-native/Makefile deleted file mode 100644 index eb3ca1e13aa..00000000000 --- a/tests/run-make/target-cpu-native/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -include ../tools.mk - -# only-linux -# only-x86_64 -# -# I *really* don't want to deal with a cross-platform way to compare file sizes, -# tests in `make` sort of are awful - -all: $(TMPDIR)/out.log - # Make sure no warnings about "unknown CPU `native`" were emitted - if [ "$$(wc -c $(TMPDIR)/out.log | cut -d' ' -f 1)" = "0" ]; then \ - echo no warnings generated; \ - else \ - exit 1; \ - fi - - -$(TMPDIR)/out.log: - $(RUSTC) foo.rs -C target-cpu=native 2>&1 | tee $(TMPDIR)/out.log - $(call RUN,foo) diff --git a/tests/run-make/target-cpu-native/rmake.rs b/tests/run-make/target-cpu-native/rmake.rs new file mode 100644 index 00000000000..fd5fb6193fe --- /dev/null +++ b/tests/run-make/target-cpu-native/rmake.rs @@ -0,0 +1,14 @@ +// target-cpu is a codegen flag that generates code for the processor of the host machine +// running the compilation. This test is a sanity test that this flag does not cause any +// warnings when used, and that binaries produced by it can also be successfully executed. +// See https://github.com/rust-lang/rust/pull/23238 + +use run_make_support::{run, rustc}; + +fn main() { + let out = rustc().input("foo.rs").arg("-Ctarget-cpu=native").run().stderr_utf8(); + run("foo"); + // There should be zero warnings emitted - the bug would cause "unknown CPU `native`" + // to be printed out. + assert!(out.is_empty()); +} diff --git a/tests/run-make/target-specs/Makefile b/tests/run-make/target-specs/Makefile deleted file mode 100644 index 161b6602185..00000000000 --- a/tests/run-make/target-specs/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -include ../tools.mk -all: - $(RUSTC) foo.rs --target=my-awesome-platform.json --crate-type=lib --emit=asm - $(CGREP) -v morestack < $(TMPDIR)/foo.s - $(RUSTC) foo.rs --target=my-invalid-platform.json 2>&1 | $(CGREP) "Error loading target specification" - $(RUSTC) foo.rs --target=my-incomplete-platform.json 2>&1 | $(CGREP) 'Field llvm-target' - RUST_TARGET_PATH=. $(RUSTC) foo.rs --target=my-awesome-platform --crate-type=lib --emit=asm - RUST_TARGET_PATH=. $(RUSTC) foo.rs --target=my-x86_64-unknown-linux-gnu-platform --crate-type=lib --emit=asm - $(RUSTC) -Z unstable-options --target=my-awesome-platform.json --print target-spec-json > $(TMPDIR)/test-platform.json && $(RUSTC) -Z unstable-options --target=$(TMPDIR)/test-platform.json --print target-spec-json | diff -q $(TMPDIR)/test-platform.json - - $(RUSTC) foo.rs --target=definitely-not-builtin-target 2>&1 | $(CGREP) 'may not set is_builtin' - $(RUSTC) foo.rs --target=endianness-mismatch 2>&1 | $(CGREP) '"data-layout" claims architecture is little-endian' - $(RUSTC) foo.rs --target=mismatching-data-layout --crate-type=lib 2>&1 | $(CGREP) 'data-layout for target' diff --git a/tests/run-make/target-specs/rmake.rs b/tests/run-make/target-specs/rmake.rs new file mode 100644 index 00000000000..d2b5f650838 --- /dev/null +++ b/tests/run-make/target-specs/rmake.rs @@ -0,0 +1,71 @@ +// Target-specific compilation in rustc used to have case-by-case peculiarities in 2014, +// with the compiler having redundant target types and unspecific names. An overarching rework +// in #16156 changed the way the target flag functions, and this test attempts compilation +// with the target flag's bundle of new features to check that compilation either succeeds while +// using them correctly, or fails with the right error message when using them improperly. +// See https://github.com/rust-lang/rust/pull/16156 + +use run_make_support::{diff, fs_wrapper, rustc}; + +fn main() { + rustc().input("foo.rs").target("my-awesome-platform.json").crate_type("lib").emit("asm").run(); + assert!(!fs_wrapper::read_to_string("foo.s").contains("morestack")); + rustc() + .input("foo.rs") + .target("my-invalid-platform.json") + .run_fail() + .assert_stderr_contains("Error loading target specification"); + rustc() + .input("foo.rs") + .target("my-incomplete-platform.json") + .run_fail() + .assert_stderr_contains("Field llvm-target"); + rustc() + .env("RUST_TARGET_PATH", ".") + .input("foo.rs") + .target("my-awesome-platform") + .crate_type("lib") + .emit("asm") + .run(); + rustc() + .env("RUST_TARGET_PATH", ".") + .input("foo.rs") + .target("my-x86_64-unknown-linux-gnu-platform") + .crate_type("lib") + .emit("asm") + .run(); + let test_platform = rustc() + .arg("-Zunstable-options") + .target("my-awesome-platform.json") + .print("target-spec-json") + .run() + .stdout_utf8(); + fs_wrapper::create_file("test-platform.json"); + fs_wrapper::write("test-platform.json", test_platform.as_bytes()); + let test_platform_2 = rustc() + .arg("-Zunstable-options") + .target("test-platform.json") + .print("target-spec-json") + .run() + .stdout_utf8(); + diff() + .expected_file("test-platform.json") + .actual_text("test-platform-2", test_platform_2) + .run(); + rustc() + .input("foo.rs") + .target("definitely-not-builtin-target") + .run_fail() + .assert_stderr_contains("may not set is_builtin"); + rustc() + .input("foo.rs") + .target("endianness-mismatch") + .run_fail() + .assert_stderr_contains(r#""data-layout" claims architecture is little-endian"#); + rustc() + .input("foo.rs") + .target("mismatching-data-layout") + .crate_type("lib") + .run_fail() + .assert_stderr_contains("data-layout for target"); +} diff --git a/tests/run-make/target-without-atomic-cas/Makefile b/tests/run-make/target-without-atomic-cas/Makefile deleted file mode 100644 index 451f03d66cd..00000000000 --- a/tests/run-make/target-without-atomic-cas/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -include ../tools.mk - -# The target used below doesn't support atomic CAS operations. Verify that's the case -all: - $(RUSTC) --print cfg --target thumbv6m-none-eabi | $(CGREP) -v 'target_has_atomic="ptr"' diff --git a/tests/run-make/target-without-atomic-cas/rmake.rs b/tests/run-make/target-without-atomic-cas/rmake.rs new file mode 100644 index 00000000000..c8782b6d1a5 --- /dev/null +++ b/tests/run-make/target-without-atomic-cas/rmake.rs @@ -0,0 +1,16 @@ +// ARM Cortex-M are a class of processors supported by the rust compiler. However, +// they cannot support any atomic features, such as Arc. This test simply prints +// the configuration details of one Cortex target, and checks that the compiler +// does not falsely list atomic support. +// See https://github.com/rust-lang/rust/pull/36874 + +use run_make_support::rustc; + +// The target used below doesn't support atomic CAS operations. Verify that's the case +fn main() { + rustc() + .print("cfg") + .target("thumbv6m-none-eabi") + .run() + .assert_stdout_not_contains(r#"target_has_atomic="ptr""#); +} diff --git a/tests/run-make/track-path-dep-info/Makefile b/tests/run-make/track-path-dep-info/Makefile deleted file mode 100644 index 0d6c9b1d2f0..00000000000 --- a/tests/run-make/track-path-dep-info/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -include ../tools.mk - -# FIXME(eddyb) provide `HOST_RUSTC` and `TARGET_RUSTC` -# instead of hardcoding them everywhere they're needed. -ifeq ($(IS_MUSL_HOST),1) -ADDITIONAL_ARGS := $(RUSTFLAGS) -endif - -all: - # Proc macro - $(BARE_RUSTC) $(ADDITIONAL_ARGS) --out-dir $(TMPDIR) macro_def.rs - EXISTING_PROC_MACRO_ENV=1 $(RUSTC) --emit dep-info macro_use.rs - $(CGREP) "emojis.txt:" < $(TMPDIR)/macro_use.d diff --git a/tests/run-make/track-path-dep-info/rmake.rs b/tests/run-make/track-path-dep-info/rmake.rs new file mode 100644 index 00000000000..f108dc66051 --- /dev/null +++ b/tests/run-make/track-path-dep-info/rmake.rs @@ -0,0 +1,13 @@ +// This test checks the functionality of `tracked_path::path`, a procedural macro +// feature that adds a dependency to another file inside the procmacro. In this case, +// the text file is added through this method, and the test checks that the compilation +// output successfully added the file as a dependency. +// See https://github.com/rust-lang/rust/pull/84029 + +use run_make_support::{fs_wrapper, rustc}; + +fn main() { + rustc().input("macro_def.rs").run(); + rustc().env("EXISTING_PROC_MACRO_ENV", "1").emit("dep-info").input("macro_use.rs").run(); + assert!(fs_wrapper::read_to_string("macro_use.d").contains("emojis.txt:")); +} diff --git a/tests/run-make/track-pgo-dep-info/Makefile b/tests/run-make/track-pgo-dep-info/Makefile deleted file mode 100644 index 3afe3662fa7..00000000000 --- a/tests/run-make/track-pgo-dep-info/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -# needs-profiler-support - -include ../tools.mk - -# FIXME(eddyb) provide `HOST_RUSTC` and `TARGET_RUSTC` -# instead of hardcoding them everywhere they're needed. -ifeq ($(IS_MUSL_HOST),1) -ADDITIONAL_ARGS := $(RUSTFLAGS) -endif - -all: - # Generate PGO profiles - $(BARE_RUSTC) $(ADDITIONAL_ARGS) -Cprofile-generate=$(TMPDIR)/profiles --out-dir $(TMPDIR) main.rs - $(TMPDIR)/main - - # Merge profiles - "$(LLVM_BIN_DIR)/llvm-profdata" merge \ - -o "$(TMPDIR)/merged.profdata" \ - "$(TMPDIR)/profiles" || exit 1 - - # Use the profile - $(RUSTC) -Cprofile-use=$(TMPDIR)/merged.profdata --emit dep-info main.rs - - # Check that profile file is in depinfo - $(CGREP) "merged.profdata" < $(TMPDIR)/main.d diff --git a/tests/run-make/track-pgo-dep-info/rmake.rs b/tests/run-make/track-pgo-dep-info/rmake.rs new file mode 100644 index 00000000000..acfe05cf8ea --- /dev/null +++ b/tests/run-make/track-pgo-dep-info/rmake.rs @@ -0,0 +1,23 @@ +// Emitting dep-info files used to not have any mention of PGO profiles used +// in compilation, which meant these profiles could be changed without consequence. +// After changing this in #100801, this test checks that the profile data is successfully +// included in dep-info emit files. +// See https://github.com/rust-lang/rust/pull/100801 + +//@ ignore-cross-compile +// Reason: the binary is executed +//@ needs-profiler-support + +use run_make_support::{fs_wrapper, llvm_profdata, run, rustc}; + +fn main() { + // Generate the profile-guided-optimization (PGO) profiles + rustc().profile_generate("profiles").input("main.rs").run(); + // Merge the profiles + run("main"); + llvm_profdata().merge().output("merged.profdata").input("profiles").run(); + // Use the profiles in compilation + rustc().profile_use("merged.profdata").emit("dep-info").input("main.rs").run(); + // Check that the profile file is in the dep-info emit file + assert!(fs_wrapper::read_to_string("main.d").contains("merged.profdata")); +} diff --git a/tests/rustdoc-json/trait_alias.rs b/tests/rustdoc-json/trait_alias.rs new file mode 100644 index 00000000000..dc930550ef1 --- /dev/null +++ b/tests/rustdoc-json/trait_alias.rs @@ -0,0 +1,18 @@ +// ignore-tidy-linelength +#![feature(trait_alias)] + +// @set StrLike = "$.index[*][?(@.name=='StrLike')].id" +// @is "$.index[*][?(@.name=='StrLike')].visibility" \"public\" +// @has "$.index[*][?(@.name=='StrLike')].inner.trait_alias" +// @is "$.index[*][?(@.name=='StrLike')].span.filename" $FILE +pub trait StrLike = AsRef<str>; + +// @is "$.index[*][?(@.name=='f')].inner.function.decl.output.impl_trait[0].trait_bound.trait.id" $StrLike +pub fn f() -> impl StrLike { + "heya" +} + +// @!is "$.index[*][?(@.name=='g')].inner.function.decl.output.impl_trait[0].trait_bound.trait.id" $StrLike +pub fn g() -> impl AsRef<str> { + "heya" +} diff --git a/tests/rustdoc-json/type_alias.rs b/tests/rustdoc-json/type_alias.rs new file mode 100644 index 00000000000..7a938c50ba0 --- /dev/null +++ b/tests/rustdoc-json/type_alias.rs @@ -0,0 +1,15 @@ +// @set IntVec = "$.index[*][?(@.name=='IntVec')].id" +// @is "$.index[*][?(@.name=='IntVec')].visibility" \"public\" +// @has "$.index[*][?(@.name=='IntVec')].inner.type_alias" +// @is "$.index[*][?(@.name=='IntVec')].span.filename" $FILE +pub type IntVec = Vec<u32>; + +// @is "$.index[*][?(@.name=='f')].inner.function.decl.output.resolved_path.id" $IntVec +pub fn f() -> IntVec { + vec![0; 32] +} + +// @!is "$.index[*][?(@.name=='g')].inner.function.decl.output.resolved_path.id" $IntVec +pub fn g() -> Vec<u32> { + vec![0; 32] +} diff --git a/tests/ui/async-await/async-closures/signature-inference-from-two-part-bound.rs b/tests/ui/async-await/async-closures/signature-inference-from-two-part-bound.rs new file mode 100644 index 00000000000..0e2d1ef1208 --- /dev/null +++ b/tests/ui/async-await/async-closures/signature-inference-from-two-part-bound.rs @@ -0,0 +1,27 @@ +//@ edition: 2021 +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +#![feature(async_closure)] + +use std::future::Future; +use std::any::Any; + +struct Struct; +impl Struct { + fn method(&self) {} +} + +fn fake_async_closure<F, Fut>(_: F) +where + F: Fn(Struct) -> Fut, + Fut: Future<Output = ()>, +{} + +fn main() { + fake_async_closure(async |s| { + s.method(); + }) +} diff --git a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs index 0d8f92f013f..5704a33cc87 100644 --- a/tests/ui/cast/cast-rfc0401-vtable-kinds.rs +++ b/tests/ui/cast/cast-rfc0401-vtable-kinds.rs @@ -4,25 +4,12 @@ #![feature(unsized_tuple_coercion)] -trait Foo<T> { - fn foo(&self, _: T) -> u32 { 42 } -} - trait Bar { //~ WARN trait `Bar` is never used fn bar(&self) { println!("Bar!"); } } -impl<T> Foo<T> for () {} -impl Foo<u32> for u32 { fn foo(&self, _: u32) -> u32 { self+43 } } impl Bar for () {} -unsafe fn round_trip_and_call<'a>(t: *const (dyn Foo<u32>+'a)) -> u32 { - let foo_e : *const dyn Foo<u32> = t as *const _; - let r_1 = foo_e as *mut dyn Foo<u32>; - - (&*r_1).foo(0) -} - #[repr(C)] struct FooS<T:?Sized>(T); #[repr(C)] @@ -38,11 +25,6 @@ fn tuple_i32_to_u32<T:?Sized>(u: *const (i32, T)) -> *const (u32, T) { fn main() { - let x = 4u32; - let y : &dyn Foo<u32> = &x; - let fl = unsafe { round_trip_and_call(y as *const dyn Foo<u32>) }; - assert_eq!(fl, (43+4)); - let s = FooS([0,1,2]); let u: &FooS<[u32]> = &s; let u: *const FooS<[u32]> = u; diff --git a/tests/ui/cast/cast-rfc0401-vtable-kinds.stderr b/tests/ui/cast/cast-rfc0401-vtable-kinds.stderr index 952687e98d0..4f57e2e7df7 100644 --- a/tests/ui/cast/cast-rfc0401-vtable-kinds.stderr +++ b/tests/ui/cast/cast-rfc0401-vtable-kinds.stderr @@ -1,5 +1,5 @@ warning: trait `Bar` is never used - --> $DIR/cast-rfc0401-vtable-kinds.rs:11:7 + --> $DIR/cast-rfc0401-vtable-kinds.rs:7:7 | LL | trait Bar { | ^^^ diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.rs b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs new file mode 100644 index 00000000000..46e72ea0877 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.rs @@ -0,0 +1,18 @@ +//@ check-pass + +trait Trait<'a> {} + +fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) { + x as _ + //~^ warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on + //~| warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +} + +// (to test diagnostic list formatting) +fn add_multiple_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send + Sync + Unpin) { + x as _ + //~^ warning: adding auto traits `Send`, `Sync`, and `Unpin` to a trait object in a pointer cast may cause UB later on + //~| warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +} + +fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr new file mode 100644 index 00000000000..e5ef8bf76b4 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-add-auto.stderr @@ -0,0 +1,43 @@ +warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on + --> $DIR/ptr-to-trait-obj-add-auto.rs:6:5 + | +LL | x as _ + | ^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #127323 <https://github.com/rust-lang/rust/issues/127323> + = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default + +warning: adding auto traits `Send`, `Sync`, and `Unpin` to a trait object in a pointer cast may cause UB later on + --> $DIR/ptr-to-trait-obj-add-auto.rs:13:5 + | +LL | x as _ + | ^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #127323 <https://github.com/rust-lang/rust/issues/127323> + +warning: 2 warnings emitted + +Future incompatibility report: Future breakage diagnostic: +warning: adding an auto trait `Send` to a trait object in a pointer cast may cause UB later on + --> $DIR/ptr-to-trait-obj-add-auto.rs:6:5 + | +LL | x as _ + | ^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #127323 <https://github.com/rust-lang/rust/issues/127323> + = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default + +Future breakage diagnostic: +warning: adding auto traits `Send`, `Sync`, and `Unpin` to a trait object in a pointer cast may cause UB later on + --> $DIR/ptr-to-trait-obj-add-auto.rs:13:5 + | +LL | x as _ + | ^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #127323 <https://github.com/rust-lang/rust/issues/127323> + = note: `#[warn(ptr_cast_add_auto_to_object)]` on by default + diff --git a/tests/ui/cast/ptr-to-trait-obj-add-super-auto.rs b/tests/ui/cast/ptr-to-trait-obj-add-super-auto.rs new file mode 100644 index 00000000000..ac8108d8ec4 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-add-super-auto.rs @@ -0,0 +1,9 @@ +//@ check-pass + +trait Trait: Send {} +impl Trait for () {} + +fn main() { + // This is OK: `Trait` has `Send` super trait. + &() as *const dyn Trait as *const (dyn Trait + Send); +} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.rs b/tests/ui/cast/ptr-to-trait-obj-different-args.rs new file mode 100644 index 00000000000..c6038cfe864 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.rs @@ -0,0 +1,37 @@ +//@ check-fail +// +// issue: <https://github.com/rust-lang/rust/issues/120222> + +trait A {} +impl<T> A for T {} +trait B {} +impl<T> B for T {} + +trait Trait<G> {} +struct X; +impl<T> Trait<X> for T {} +struct Y; +impl<T> Trait<Y> for T {} + +fn main() { + let a: *const dyn A = &(); + let b: *const dyn B = a as _; //~ error: casting `*const dyn A` as `*const dyn B` is invalid + + let x: *const dyn Trait<X> = &(); + let y: *const dyn Trait<Y> = x as _; //~ error: mismatched types + + _ = (b, y); +} + +fn generic<T>(x: *const dyn Trait<X>, t: *const dyn Trait<T>) { + let _: *const dyn Trait<T> = x as _; //~ error: mismatched types + let _: *const dyn Trait<X> = t as _; //~ error: mismatched types +} + +trait Assocked { + type Assoc: ?Sized; +} + +fn change_assoc(x: *mut dyn Assocked<Assoc = u8>) -> *mut dyn Assocked<Assoc = u32> { + x as _ //~ error: mismatched types +} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-args.stderr b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr new file mode 100644 index 00000000000..b04289ae747 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-args.stderr @@ -0,0 +1,53 @@ +error[E0606]: casting `*const dyn A` as `*const dyn B` is invalid + --> $DIR/ptr-to-trait-obj-different-args.rs:18:27 + | +LL | let b: *const dyn B = a as _; + | ^^^^^^ + | + = note: vtable kinds may not match + +error[E0308]: mismatched types + --> $DIR/ptr-to-trait-obj-different-args.rs:21:34 + | +LL | let y: *const dyn Trait<Y> = x as _; + | ^^^^^^ expected `X`, found `Y` + | + = note: expected trait object `dyn Trait<X>` + found trait object `dyn Trait<Y>` + +error[E0308]: mismatched types + --> $DIR/ptr-to-trait-obj-different-args.rs:27:34 + | +LL | fn generic<T>(x: *const dyn Trait<X>, t: *const dyn Trait<T>) { + | - found this type parameter +LL | let _: *const dyn Trait<T> = x as _; + | ^^^^^^ expected `X`, found type parameter `T` + | + = note: expected trait object `dyn Trait<X>` + found trait object `dyn Trait<T>` + +error[E0308]: mismatched types + --> $DIR/ptr-to-trait-obj-different-args.rs:28:34 + | +LL | fn generic<T>(x: *const dyn Trait<X>, t: *const dyn Trait<T>) { + | - expected this type parameter +LL | let _: *const dyn Trait<T> = x as _; +LL | let _: *const dyn Trait<X> = t as _; + | ^^^^^^ expected type parameter `T`, found `X` + | + = note: expected trait object `dyn Trait<T>` + found trait object `dyn Trait<X>` + +error[E0308]: mismatched types + --> $DIR/ptr-to-trait-obj-different-args.rs:36:5 + | +LL | x as _ + | ^^^^^^ expected `u8`, found `u32` + | + = note: expected trait object `dyn Assocked<Assoc = u8>` + found trait object `dyn Assocked<Assoc = u32>` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0308, E0606. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.rs new file mode 100644 index 00000000000..cdd55e24392 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.rs @@ -0,0 +1,27 @@ +//@ check-fail +// +// Make sure we can't trick the compiler by using a projection. + +trait Cat<'a> {} +impl Cat<'_> for () {} + +trait Id { + type Id: ?Sized; +} +impl<T: ?Sized> Id for T { + type Id = T; +} + +struct S<T: ?Sized> { + tail: <T as Id>::Id, +} + +fn m<'a>() { + let unsend: *const dyn Cat<'a> = &(); + let _send = unsend as *const S<dyn Cat<'static>>; + //~^ error: lifetime may not live long enough +} + +fn main() { + m(); +} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.stderr new file mode 100644 index 00000000000..d1d598e603f --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-id-trait.stderr @@ -0,0 +1,15 @@ +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-id-trait.rs:21:17 + | +LL | fn m<'a>() { + | -- lifetime `'a` defined here +LL | let unsend: *const dyn Cat<'a> = &(); +LL | let _send = unsend as *const S<dyn Cat<'static>>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` + | + = note: requirement occurs because of the type `S<dyn Cat<'_>>`, which makes the generic argument `dyn Cat<'_>` invariant + = note: the struct `S<T>` is invariant over the parameter `T` + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: aborting due to 1 previous error + diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs new file mode 100644 index 00000000000..96345de01c9 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.rs @@ -0,0 +1,31 @@ +//@ check-fail +// +// issue: <https://github.com/rust-lang/rust/issues/120217> + +#![feature(arbitrary_self_types)] + +trait Static<'a> { + fn proof(self: *const Self, s: &'a str) -> &'static str; +} + +fn bad_cast<'a>(x: *const dyn Static<'static>) -> *const dyn Static<'a> { + x as _ //~ error: lifetime may not live long enough +} + +impl Static<'static> for () { + fn proof(self: *const Self, s: &'static str) -> &'static str { + s + } +} + +fn extend_lifetime(s: &str) -> &'static str { + bad_cast(&()).proof(s) +} + +fn main() { + let s = String::from("Hello World"); + let slice = extend_lifetime(&s); + println!("Now it exists: {slice}"); + drop(s); + println!("Now it’s gone: {slice}"); +} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.stderr new file mode 100644 index 00000000000..b7319e3356b --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-lt-ext.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-lt-ext.rs:12:5 + | +LL | fn bad_cast<'a>(x: *const dyn Static<'static>) -> *const dyn Static<'a> { + | -- lifetime `'a` defined here +LL | x as _ + | ^^^^^^ returning this value requires that `'a` must outlive `'static` + +error: aborting due to 1 previous error + diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs new file mode 100644 index 00000000000..01c347bfae5 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs @@ -0,0 +1,37 @@ +//@ check-fail + +trait Trait<'a> {} + +fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + x as _ //~ error: lifetime may not live long enough + //~| error: lifetime may not live long enough +} + +fn change_lt_ab<'a: 'b, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + x as _ //~ error: lifetime may not live long enough +} + +fn change_lt_ba<'a, 'b: 'a>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + x as _ //~ error: lifetime may not live long enough +} + +trait Assocked { + type Assoc: ?Sized; +} + +fn change_assoc_0<'a, 'b>( + x: *mut dyn Assocked<Assoc = dyn Send + 'a>, +) -> *mut dyn Assocked<Assoc = dyn Send + 'b> { + x as _ //~ error: lifetime may not live long enough + //~| error: lifetime may not live long enough +} + +fn change_assoc_1<'a, 'b>( + x: *mut dyn Assocked<Assoc = dyn Trait<'a>>, +) -> *mut dyn Assocked<Assoc = dyn Trait<'b>> { + x as _ //~ error: lifetime may not live long enough + //~| error: lifetime may not live long enough +} + + +fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr new file mode 100644 index 00000000000..7044e4dec1f --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr @@ -0,0 +1,136 @@ +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:6:5 + | +LL | fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable pointer to `dyn Trait<'_>` + = note: mutable pointers are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:6:5 + | +LL | fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a mutable pointer to `dyn Trait<'_>` + = note: mutable pointers are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +help: `'b` and `'a` must be the same: replace one with the other + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:11:5 + | +LL | fn change_lt_ab<'a: 'b, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable pointer to `dyn Trait<'_>` + = note: mutable pointers are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:15:5 + | +LL | fn change_lt_ba<'a, 'b: 'a>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a mutable pointer to `dyn Trait<'_>` + = note: mutable pointers are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:25:5 + | +LL | fn change_assoc_0<'a, 'b>( + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable pointer to `dyn Assocked<Assoc = dyn Send>` + = note: mutable pointers are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:25:5 + | +LL | fn change_assoc_0<'a, 'b>( + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a mutable pointer to `dyn Assocked<Assoc = dyn Send>` + = note: mutable pointers are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +help: `'b` and `'a` must be the same: replace one with the other + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:32:5 + | +LL | fn change_assoc_1<'a, 'b>( + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` + | + = help: consider adding the following bound: `'b: 'a` + = note: requirement occurs because of a mutable pointer to `dyn Assocked<Assoc = dyn Trait<'_>>` + = note: mutable pointers are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +error: lifetime may not live long enough + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:32:5 + | +LL | fn change_assoc_1<'a, 'b>( + | -- -- lifetime `'b` defined here + | | + | lifetime `'a` defined here +... +LL | x as _ + | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` + | + = help: consider adding the following bound: `'a: 'b` + = note: requirement occurs because of a mutable pointer to `dyn Assocked<Assoc = dyn Trait<'_>>` + = note: mutable pointers are invariant over their type parameter + = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance + +help: `'b` and `'a` must be the same: replace one with the other + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 8 previous errors + diff --git a/tests/ui/cast/ptr-to-trait-obj-ok.rs b/tests/ui/cast/ptr-to-trait-obj-ok.rs new file mode 100644 index 00000000000..656c99c58dc --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-ok.rs @@ -0,0 +1,17 @@ +//@ check-pass + +trait Trait<'a> {} + +fn remove_auto<'a>(x: *mut (dyn Trait<'a> + Send)) -> *mut dyn Trait<'a> { + x as _ +} + +fn cast_inherent_lt<'a, 'b>(x: *mut (dyn Trait<'static> + 'a)) -> *mut (dyn Trait<'static> + 'b) { + x as _ +} + +fn unprincipled<'a, 'b>(x: *mut (dyn Send + 'a)) -> *mut (dyn Sync + 'b) { + x as _ +} + +fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.rs b/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.rs new file mode 100644 index 00000000000..ff2c4cacfb1 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.rs @@ -0,0 +1,14 @@ +trait Super {} +trait Sub: Super {} + +struct Wrapper<T: ?Sized>(T); + +// This cast should not compile. +// Upcasting can't work here, because we are also changing the type (`Wrapper`), +// and reinterpreting would be confusing/surprising. +// See <https://github.com/rust-lang/rust/pull/120248#discussion_r1487739518> +fn cast(ptr: *const dyn Sub) -> *const Wrapper<dyn Super> { + ptr as _ //~ error: casting `*const (dyn Sub + 'static)` as `*const Wrapper<dyn Super>` is invalid +} + +fn main() {} diff --git a/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.stderr b/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.stderr new file mode 100644 index 00000000000..38c8ba96bc5 --- /dev/null +++ b/tests/ui/cast/ptr-to-trait-obj-wrap-upcast.stderr @@ -0,0 +1,11 @@ +error[E0606]: casting `*const (dyn Sub + 'static)` as `*const Wrapper<dyn Super>` is invalid + --> $DIR/ptr-to-trait-obj-wrap-upcast.rs:11:5 + | +LL | ptr as _ + | ^^^^^^^^ + | + = note: vtable kinds may not match + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0606`. diff --git a/tests/ui/deriving/deriving-in-fn.rs b/tests/ui/deriving/deriving-in-fn.rs index 72da2148350..13f3d39597c 100644 --- a/tests/ui/deriving/deriving-in-fn.rs +++ b/tests/ui/deriving/deriving-in-fn.rs @@ -9,5 +9,5 @@ pub fn main() { } let f = Foo { foo: 10 }; - format!("{:?}", f); + let _ = format!("{:?}", f); } diff --git a/tests/ui/explicit-tail-calls/constck.rs b/tests/ui/explicit-tail-calls/constck.rs new file mode 100644 index 00000000000..938f15f12c0 --- /dev/null +++ b/tests/ui/explicit-tail-calls/constck.rs @@ -0,0 +1,22 @@ +#![allow(incomplete_features)] +#![feature(explicit_tail_calls)] + +const fn f() { + if false { + become not_const(); + //~^ error: cannot call non-const fn `not_const` in constant functions + } +} + +const fn g((): ()) { + if false { + become yes_const(not_const()); + //~^ error: cannot call non-const fn `not_const` in constant functions + } +} + +fn not_const() {} + +const fn yes_const((): ()) {} + +fn main() {} diff --git a/tests/ui/explicit-tail-calls/constck.stderr b/tests/ui/explicit-tail-calls/constck.stderr new file mode 100644 index 00000000000..d9967c45fa0 --- /dev/null +++ b/tests/ui/explicit-tail-calls/constck.stderr @@ -0,0 +1,19 @@ +error[E0015]: cannot call non-const fn `not_const` in constant functions + --> $DIR/constck.rs:6:16 + | +LL | become not_const(); + | ^^^^^^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error[E0015]: cannot call non-const fn `not_const` in constant functions + --> $DIR/constck.rs:13:26 + | +LL | become yes_const(not_const()); + | ^^^^^^^^^^^ + | + = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.rs b/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.rs new file mode 100644 index 00000000000..5a105ee4eb5 --- /dev/null +++ b/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.rs @@ -0,0 +1,14 @@ +#![allow(incomplete_features)] +#![feature(explicit_tail_calls)] + +pub const fn test(_: &Type) { + const fn takes_borrow(_: &Type) {} + + let local = Type; + become takes_borrow(&local); + //~^ error: `local` does not live long enough +} + +struct Type; + +fn main() {} diff --git a/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr b/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr new file mode 100644 index 00000000000..75fb13c378c --- /dev/null +++ b/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr @@ -0,0 +1,14 @@ +error[E0597]: `local` does not live long enough + --> $DIR/ctfe-arg-bad-borrow.rs:8:25 + | +LL | let local = Type; + | ----- binding `local` declared here +LL | become takes_borrow(&local); + | ^^^^^^ borrowed value does not live long enough +LL | +LL | } + | - `local` dropped here while still borrowed + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/explicit-tail-calls/ctfe-arg-good-borrow.rs b/tests/ui/explicit-tail-calls/ctfe-arg-good-borrow.rs new file mode 100644 index 00000000000..50bf6c946ca --- /dev/null +++ b/tests/ui/explicit-tail-calls/ctfe-arg-good-borrow.rs @@ -0,0 +1,13 @@ +//@ check-pass +#![allow(incomplete_features)] +#![feature(explicit_tail_calls)] + +pub const fn test(x: &Type) { + const fn takes_borrow(_: &Type) {} + + become takes_borrow(x); +} + +pub struct Type; + +fn main() {} diff --git a/tests/ui/explicit-tail-calls/ctfe-arg-move.rs b/tests/ui/explicit-tail-calls/ctfe-arg-move.rs new file mode 100644 index 00000000000..88ff3a4a5ad --- /dev/null +++ b/tests/ui/explicit-tail-calls/ctfe-arg-move.rs @@ -0,0 +1,15 @@ +//@ check-pass +#![allow(incomplete_features)] +#![feature(explicit_tail_calls)] + +pub const fn test(s: String) -> String { + const fn takes_string(s: String) -> String { + s + } + + become takes_string(s); +} + +struct Type; + +fn main() {} diff --git a/tests/ui/explicit-tail-calls/ctfe-collatz-multi-rec.rs b/tests/ui/explicit-tail-calls/ctfe-collatz-multi-rec.rs new file mode 100644 index 00000000000..86041b66960 --- /dev/null +++ b/tests/ui/explicit-tail-calls/ctfe-collatz-multi-rec.rs @@ -0,0 +1,43 @@ +//@ run-pass +#![allow(incomplete_features)] +#![feature(explicit_tail_calls)] + +/// A very unnecessarily complicated "implementation" of the Collatz conjecture. +/// Returns the number of steps to reach `1`. +/// +/// This is just a test for tail calls, which involves multiple functions calling each other. +/// +/// Panics if `x == 0`. +const fn collatz(x: u32) -> u32 { + assert!(x > 0); + + const fn switch(x: u32, steps: u32) -> u32 { + match x { + 1 => steps, + _ if x & 1 == 0 => become div2(x, steps + 1), + _ => become mul3plus1(x, steps + 1), + } + } + + const fn div2(x: u32, steps: u32) -> u32 { + become switch(x >> 1, steps) + } + + const fn mul3plus1(x: u32, steps: u32) -> u32 { + become switch(3 * x + 1, steps) + } + + switch(x, 0) +} + +const ASSERTS: () = { + assert!(collatz(1) == 0); + assert!(collatz(2) == 1); + assert!(collatz(3) == 7); + assert!(collatz(4) == 2); + assert!(collatz(6171) == 261); +}; + +fn main() { + let _ = ASSERTS; +} diff --git a/tests/ui/explicit-tail-calls/ctfe-id-unlimited.return.stderr b/tests/ui/explicit-tail-calls/ctfe-id-unlimited.return.stderr new file mode 100644 index 00000000000..4a1e50b4111 --- /dev/null +++ b/tests/ui/explicit-tail-calls/ctfe-id-unlimited.return.stderr @@ -0,0 +1,36 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/ctfe-id-unlimited.rs:17:42 + | +LL | #[cfg(r#return)] _ => return inner(acc + 1, n - 1), + | ^^^^^^^^^^^^^^^^^^^^^ reached the configured maximum number of stack frames + | +note: inside `inner` + --> $DIR/ctfe-id-unlimited.rs:17:42 + | +LL | #[cfg(r#return)] _ => return inner(acc + 1, n - 1), + | ^^^^^^^^^^^^^^^^^^^^^ +note: [... 125 additional calls inside `inner` ...] + --> $DIR/ctfe-id-unlimited.rs:17:42 + | +LL | #[cfg(r#return)] _ => return inner(acc + 1, n - 1), + | ^^^^^^^^^^^^^^^^^^^^^ +note: inside `rec_id` + --> $DIR/ctfe-id-unlimited.rs:22:5 + | +LL | inner(0, n) + | ^^^^^^^^^^^ +note: inside `ID_ED` + --> $DIR/ctfe-id-unlimited.rs:29:20 + | +LL | const ID_ED: u32 = rec_id(ORIGINAL); + | ^^^^^^^^^^^^^^^^ + +note: erroneous constant encountered + --> $DIR/ctfe-id-unlimited.rs:31:40 + | +LL | const ASSERT: () = assert!(ORIGINAL == ID_ED); + | ^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/explicit-tail-calls/ctfe-id-unlimited.rs b/tests/ui/explicit-tail-calls/ctfe-id-unlimited.rs new file mode 100644 index 00000000000..54e68b2b7f7 --- /dev/null +++ b/tests/ui/explicit-tail-calls/ctfe-id-unlimited.rs @@ -0,0 +1,35 @@ +//@ revisions: become return +//@ [become] run-pass +#![allow(incomplete_features)] +#![feature(explicit_tail_calls)] + +// This is an identity function (`|x| x`), but implemented using recursion. +// Each step we increment accumulator and decrement the number. +// +// With normal calls this fails compilation because of the recursion limit, +// but with tail calls/`become` we don't grow the stack/spend recursion limit +// so this should compile. +const fn rec_id(n: u32) -> u32 { + const fn inner(acc: u32, n: u32) -> u32 { + match n { + 0 => acc, + #[cfg(r#become)] _ => become inner(acc + 1, n - 1), + #[cfg(r#return)] _ => return inner(acc + 1, n - 1), + //[return]~^ error: evaluation of constant value failed + } + } + + inner(0, n) +} + +// Some relatively big number that is higher than recursion limit +const ORIGINAL: u32 = 12345; +// Original number, but with identity function applied +// (this is the same, but requires execution of the recursion) +const ID_ED: u32 = rec_id(ORIGINAL); +// Assert to make absolutely sure the computation actually happens +const ASSERT: () = assert!(ORIGINAL == ID_ED); + +fn main() { + let _ = ASSERT; +} diff --git a/tests/ui/explicit-tail-calls/ctfe-tail-call-panic.rs b/tests/ui/explicit-tail-calls/ctfe-tail-call-panic.rs new file mode 100644 index 00000000000..3d69cde2989 --- /dev/null +++ b/tests/ui/explicit-tail-calls/ctfe-tail-call-panic.rs @@ -0,0 +1,19 @@ +#![allow(incomplete_features)] +#![feature(explicit_tail_calls)] + +pub const fn f() { + become g(); +} + +const fn g() { + panic!() + //~^ error: evaluation of constant value failed + //~| note: in this expansion of panic! + //~| note: inside `g` + //~| note: in this expansion of panic! +} + +const _: () = f(); +//~^ note: inside `_` + +fn main() {} diff --git a/tests/ui/explicit-tail-calls/ctfe-tail-call-panic.stderr b/tests/ui/explicit-tail-calls/ctfe-tail-call-panic.stderr new file mode 100644 index 00000000000..8c070512105 --- /dev/null +++ b/tests/ui/explicit-tail-calls/ctfe-tail-call-panic.stderr @@ -0,0 +1,21 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/ctfe-tail-call-panic.rs:9:5 + | +LL | panic!() + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/ctfe-tail-call-panic.rs:9:5 + | +note: inside `g` + --> $DIR/ctfe-tail-call-panic.rs:9:5 + | +LL | panic!() + | ^^^^^^^^ +note: inside `_` + --> $DIR/ctfe-tail-call-panic.rs:16:15 + | +LL | const _: () = f(); + | ^^^ + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/explicit-tail-calls/drop-order.rs b/tests/ui/explicit-tail-calls/drop-order.rs new file mode 100644 index 00000000000..e20730446ec --- /dev/null +++ b/tests/ui/explicit-tail-calls/drop-order.rs @@ -0,0 +1,70 @@ +// FIXME(explicit_tail_calls): enable this test once rustc_codegen_ssa supports tail calls +//@ ignore-test: tail calls are not implemented in rustc_codegen_ssa yet, so this causes 🧊 +//@ run-pass +#![allow(incomplete_features)] +#![feature(explicit_tail_calls)] +use std::cell::RefCell; + +fn main() { + let tail_counter = Default::default(); + tail_recursive(0, &tail_counter); + assert_eq!(tail_counter.into_inner(), (0..128).collect::<Vec<u8>>()); + + let simply_counter = Default::default(); + simply_recursive(0, &simply_counter); + assert_eq!(simply_counter.into_inner(), (0..128).rev().collect::<Vec<u8>>()); + + let scope_counter = Default::default(); + out_of_inner_scope(&scope_counter); + assert_eq!(scope_counter.into_inner(), (0..8).collect::<Vec<u8>>()); +} + +fn tail_recursive(n: u8, order: &RefCell<Vec<u8>>) { + if n >= 128 { + return; + } + + let _local = DropCounter(n, order); + + become tail_recursive(n + 1, order) +} + +fn simply_recursive(n: u8, order: &RefCell<Vec<u8>>) { + if n >= 128 { + return; + } + + let _local = DropCounter(n, order); + + return simply_recursive(n + 1, order); +} + +fn out_of_inner_scope(order: &RefCell<Vec<u8>>) { + fn inner(order: &RefCell<Vec<u8>>) { + let _7 = DropCounter(7, order); + let _6 = DropCounter(6, order); + } + + let _5 = DropCounter(5, order); + let _4 = DropCounter(4, order); + + if true { + let _3 = DropCounter(3, order); + let _2 = DropCounter(2, order); + loop { + let _1 = DropCounter(1, order); + let _0 = DropCounter(0, order); + + become inner(order); + } + } +} + +struct DropCounter<'a>(u8, &'a RefCell<Vec<u8>>); + +impl Drop for DropCounter<'_> { + #[track_caller] + fn drop(&mut self) { + self.1.borrow_mut().push(self.0); + } +} diff --git a/tests/ui/explicit-tail-calls/return-mismatches.rs b/tests/ui/explicit-tail-calls/return-mismatches.rs index 8094a192913..935a1a1d28b 100644 --- a/tests/ui/explicit-tail-calls/return-mismatches.rs +++ b/tests/ui/explicit-tail-calls/return-mismatches.rs @@ -13,7 +13,7 @@ fn _f1() { become _g1(); //~ error: mismatched types } -fn _g1() -> ! { //~ WARN: cannot return without recursing +fn _g1() -> ! { become _g1(); } diff --git a/tests/ui/explicit-tail-calls/return-mismatches.stderr b/tests/ui/explicit-tail-calls/return-mismatches.stderr index 31c7a46ded9..1dcc35797c1 100644 --- a/tests/ui/explicit-tail-calls/return-mismatches.stderr +++ b/tests/ui/explicit-tail-calls/return-mismatches.stderr @@ -22,17 +22,6 @@ error[E0308]: mismatched types LL | become _g2(); | ^^^^^^^^^^^^ expected `u32`, found `u16` -warning: function cannot return without recursing - --> $DIR/return-mismatches.rs:16:1 - | -LL | fn _g1() -> ! { - | ^^^^^^^^^^^^^ cannot return without recursing -LL | become _g1(); - | ----- recursive call site - | - = help: a `loop` may express intention better if this is on purpose - = note: `#[warn(unconditional_recursion)]` on by default - -error: aborting due to 3 previous errors; 1 warning emitted +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/explicit-tail-calls/unsafeck.rs b/tests/ui/explicit-tail-calls/unsafeck.rs new file mode 100644 index 00000000000..872a70ca3a0 --- /dev/null +++ b/tests/ui/explicit-tail-calls/unsafeck.rs @@ -0,0 +1,11 @@ +#![allow(incomplete_features)] +#![feature(explicit_tail_calls)] + +const fn f() { + become dangerous(); + //~^ error: call to unsafe function `dangerous` is unsafe and requires unsafe function or block +} + +const unsafe fn dangerous() {} + +fn main() {} diff --git a/tests/ui/explicit-tail-calls/unsafeck.stderr b/tests/ui/explicit-tail-calls/unsafeck.stderr new file mode 100644 index 00000000000..25b8967e17b --- /dev/null +++ b/tests/ui/explicit-tail-calls/unsafeck.stderr @@ -0,0 +1,11 @@ +error[E0133]: call to unsafe function `dangerous` is unsafe and requires unsafe function or block + --> $DIR/unsafeck.rs:5:12 + | +LL | become dangerous(); + | ^^^^^^^^^^^ call to unsafe function + | + = note: consult the function's documentation for information on how to avoid undefined behavior + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/fmt/struct-field-as-captured-argument.fixed b/tests/ui/fmt/struct-field-as-captured-argument.fixed index e13af744ec8..0da40737354 100644 --- a/tests/ui/fmt/struct-field-as-captured-argument.fixed +++ b/tests/ui/fmt/struct-field-as-captured-argument.fixed @@ -8,11 +8,11 @@ struct Foo { fn main() { let foo = Foo { field: 0 }; let bar = 3; - format!("{0}", foo.field); //~ ERROR invalid format string: field access isn't supported - format!("{1} {} {bar}", "aa", foo.field); //~ ERROR invalid format string: field access isn't supported - format!("{2} {} {1} {bar}", "aa", "bb", foo.field); //~ ERROR invalid format string: field access isn't supported - format!("{1} {} {baz}", "aa", foo.field, baz = 3); //~ ERROR invalid format string: field access isn't supported - format!("{1:?} {} {baz}", "aa", foo.field, baz = 3); //~ ERROR invalid format string: field access isn't supported - format!("{1:#?} {} {baz}", "aa", foo.field, baz = 3); //~ ERROR invalid format string: field access isn't supported - format!("{1:.3} {} {baz}", "aa", foo.field, baz = 3); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{0}", foo.field); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{1} {} {bar}", "aa", foo.field); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{2} {} {1} {bar}", "aa", "bb", foo.field); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{1} {} {baz}", "aa", foo.field, baz = 3); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{1:?} {} {baz}", "aa", foo.field, baz = 3); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{1:#?} {} {baz}", "aa", foo.field, baz = 3); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{1:.3} {} {baz}", "aa", foo.field, baz = 3); //~ ERROR invalid format string: field access isn't supported } diff --git a/tests/ui/fmt/struct-field-as-captured-argument.rs b/tests/ui/fmt/struct-field-as-captured-argument.rs index 6a875a85848..325b4e3a218 100644 --- a/tests/ui/fmt/struct-field-as-captured-argument.rs +++ b/tests/ui/fmt/struct-field-as-captured-argument.rs @@ -8,11 +8,11 @@ struct Foo { fn main() { let foo = Foo { field: 0 }; let bar = 3; - format!("{foo.field}"); //~ ERROR invalid format string: field access isn't supported - format!("{foo.field} {} {bar}", "aa"); //~ ERROR invalid format string: field access isn't supported - format!("{foo.field} {} {1} {bar}", "aa", "bb"); //~ ERROR invalid format string: field access isn't supported - format!("{foo.field} {} {baz}", "aa", baz = 3); //~ ERROR invalid format string: field access isn't supported - format!("{foo.field:?} {} {baz}", "aa", baz = 3); //~ ERROR invalid format string: field access isn't supported - format!("{foo.field:#?} {} {baz}", "aa", baz = 3); //~ ERROR invalid format string: field access isn't supported - format!("{foo.field:.3} {} {baz}", "aa", baz = 3); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{foo.field}"); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{foo.field} {} {bar}", "aa"); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{foo.field} {} {1} {bar}", "aa", "bb"); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{foo.field} {} {baz}", "aa", baz = 3); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{foo.field:?} {} {baz}", "aa", baz = 3); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{foo.field:#?} {} {baz}", "aa", baz = 3); //~ ERROR invalid format string: field access isn't supported + let _ = format!("{foo.field:.3} {} {baz}", "aa", baz = 3); //~ ERROR invalid format string: field access isn't supported } diff --git a/tests/ui/fmt/struct-field-as-captured-argument.stderr b/tests/ui/fmt/struct-field-as-captured-argument.stderr index 7ea8b4068f2..4ef022cecb0 100644 --- a/tests/ui/fmt/struct-field-as-captured-argument.stderr +++ b/tests/ui/fmt/struct-field-as-captured-argument.stderr @@ -1,79 +1,79 @@ error: invalid format string: field access isn't supported - --> $DIR/struct-field-as-captured-argument.rs:11:15 + --> $DIR/struct-field-as-captured-argument.rs:11:23 | -LL | format!("{foo.field}"); - | ^^^^^^^^^ not supported in format string +LL | let _ = format!("{foo.field}"); + | ^^^^^^^^^ not supported in format string | help: consider using a positional formatting argument instead | -LL | format!("{0}", foo.field); - | ~ +++++++++++ +LL | let _ = format!("{0}", foo.field); + | ~ +++++++++++ error: invalid format string: field access isn't supported - --> $DIR/struct-field-as-captured-argument.rs:12:15 + --> $DIR/struct-field-as-captured-argument.rs:12:23 | -LL | format!("{foo.field} {} {bar}", "aa"); - | ^^^^^^^^^ not supported in format string +LL | let _ = format!("{foo.field} {} {bar}", "aa"); + | ^^^^^^^^^ not supported in format string | help: consider using a positional formatting argument instead | -LL | format!("{1} {} {bar}", "aa", foo.field); - | ~ +++++++++++ +LL | let _ = format!("{1} {} {bar}", "aa", foo.field); + | ~ +++++++++++ error: invalid format string: field access isn't supported - --> $DIR/struct-field-as-captured-argument.rs:13:15 + --> $DIR/struct-field-as-captured-argument.rs:13:23 | -LL | format!("{foo.field} {} {1} {bar}", "aa", "bb"); - | ^^^^^^^^^ not supported in format string +LL | let _ = format!("{foo.field} {} {1} {bar}", "aa", "bb"); + | ^^^^^^^^^ not supported in format string | help: consider using a positional formatting argument instead | -LL | format!("{2} {} {1} {bar}", "aa", "bb", foo.field); - | ~ +++++++++++ +LL | let _ = format!("{2} {} {1} {bar}", "aa", "bb", foo.field); + | ~ +++++++++++ error: invalid format string: field access isn't supported - --> $DIR/struct-field-as-captured-argument.rs:14:15 + --> $DIR/struct-field-as-captured-argument.rs:14:23 | -LL | format!("{foo.field} {} {baz}", "aa", baz = 3); - | ^^^^^^^^^ not supported in format string +LL | let _ = format!("{foo.field} {} {baz}", "aa", baz = 3); + | ^^^^^^^^^ not supported in format string | help: consider using a positional formatting argument instead | -LL | format!("{1} {} {baz}", "aa", foo.field, baz = 3); - | ~ +++++++++++ +LL | let _ = format!("{1} {} {baz}", "aa", foo.field, baz = 3); + | ~ +++++++++++ error: invalid format string: field access isn't supported - --> $DIR/struct-field-as-captured-argument.rs:15:15 + --> $DIR/struct-field-as-captured-argument.rs:15:23 | -LL | format!("{foo.field:?} {} {baz}", "aa", baz = 3); - | ^^^^^^^^^ not supported in format string +LL | let _ = format!("{foo.field:?} {} {baz}", "aa", baz = 3); + | ^^^^^^^^^ not supported in format string | help: consider using a positional formatting argument instead | -LL | format!("{1:?} {} {baz}", "aa", foo.field, baz = 3); - | ~ +++++++++++ +LL | let _ = format!("{1:?} {} {baz}", "aa", foo.field, baz = 3); + | ~ +++++++++++ error: invalid format string: field access isn't supported - --> $DIR/struct-field-as-captured-argument.rs:16:15 + --> $DIR/struct-field-as-captured-argument.rs:16:23 | -LL | format!("{foo.field:#?} {} {baz}", "aa", baz = 3); - | ^^^^^^^^^ not supported in format string +LL | let _ = format!("{foo.field:#?} {} {baz}", "aa", baz = 3); + | ^^^^^^^^^ not supported in format string | help: consider using a positional formatting argument instead | -LL | format!("{1:#?} {} {baz}", "aa", foo.field, baz = 3); - | ~ +++++++++++ +LL | let _ = format!("{1:#?} {} {baz}", "aa", foo.field, baz = 3); + | ~ +++++++++++ error: invalid format string: field access isn't supported - --> $DIR/struct-field-as-captured-argument.rs:17:15 + --> $DIR/struct-field-as-captured-argument.rs:17:23 | -LL | format!("{foo.field:.3} {} {baz}", "aa", baz = 3); - | ^^^^^^^^^ not supported in format string +LL | let _ = format!("{foo.field:.3} {} {baz}", "aa", baz = 3); + | ^^^^^^^^^ not supported in format string | help: consider using a positional formatting argument instead | -LL | format!("{1:.3} {} {baz}", "aa", foo.field, baz = 3); - | ~ +++++++++++ +LL | let _ = format!("{1:.3} {} {baz}", "aa", foo.field, baz = 3); + | ~ +++++++++++ error: aborting due to 7 previous errors diff --git a/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.next.stderr b/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.next.stderr index 9b5d84b5b09..c5c4f2c4d23 100644 --- a/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.next.stderr +++ b/tests/ui/generic-associated-types/assume-gat-normalization-for-nested-goals.next.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `i32: Baz<Self>` is not satisfied +error[E0277]: the trait bound `<Self as Foo>::Bar<()>: Eq<i32>` is not satisfied --> $DIR/assume-gat-normalization-for-nested-goals.rs:9:30 | LL | type Bar<T>: Baz<Self> = i32; diff --git a/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr new file mode 100644 index 00000000000..d71c1768a6a --- /dev/null +++ b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.current.stderr @@ -0,0 +1,173 @@ +error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied + --> $DIR/false-positive-predicate-entailment-error.rs:36:5 + | +LL | / fn autobatch<F>(self) -> impl Trait +LL | | +LL | | +LL | | +... | +LL | | where +LL | | F: Callback<Self::CallbackArg>, + | |_______________________________________^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>` + | +note: required for `F` to implement `Callback<i32>` + --> $DIR/false-positive-predicate-entailment-error.rs:14:21 + | +LL | impl<A, F: MyFn<A>> Callback<A> for F { + | ------- ^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here +help: consider further restricting this bound + | +LL | F: Callback<Self::CallbackArg> + MyFn<i32>, + | +++++++++++ + +error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied + --> $DIR/false-positive-predicate-entailment-error.rs:36:30 + | +LL | fn autobatch<F>(self) -> impl Trait + | ^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>` + | +note: required for `F` to implement `Callback<i32>` + --> $DIR/false-positive-predicate-entailment-error.rs:14:21 + | +LL | impl<A, F: MyFn<A>> Callback<A> for F { + | ------- ^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here +note: required by a bound in `<Sender as ChannelSender>::autobatch` + --> $DIR/false-positive-predicate-entailment-error.rs:43:12 + | +LL | fn autobatch<F>(self) -> impl Trait + | --------- required by a bound in this associated function +... +LL | F: Callback<Self::CallbackArg>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `<Sender as ChannelSender>::autobatch` +help: consider further restricting this bound + | +LL | F: Callback<Self::CallbackArg> + MyFn<i32>, + | +++++++++++ + +error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied + --> $DIR/false-positive-predicate-entailment-error.rs:36:5 + | +LL | / fn autobatch<F>(self) -> impl Trait +LL | | +LL | | +LL | | +... | +LL | | where +LL | | F: Callback<Self::CallbackArg>, + | |_______________________________________^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>` + | +note: required for `F` to implement `Callback<i32>` + --> $DIR/false-positive-predicate-entailment-error.rs:14:21 + | +LL | impl<A, F: MyFn<A>> Callback<A> for F { + | ------- ^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider further restricting this bound + | +LL | F: Callback<Self::CallbackArg> + MyFn<i32>, + | +++++++++++ + +error[E0277]: the trait bound `F: Callback<i32>` is not satisfied + --> $DIR/false-positive-predicate-entailment-error.rs:43:12 + | +LL | F: Callback<Self::CallbackArg>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>` + | +note: required for `F` to implement `Callback<i32>` + --> $DIR/false-positive-predicate-entailment-error.rs:14:21 + | +LL | impl<A, F: MyFn<A>> Callback<A> for F { + | ------- ^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here +note: the requirement `F: Callback<i32>` appears on the `impl`'s method `autobatch` but not on the corresponding trait's method + --> $DIR/false-positive-predicate-entailment-error.rs:25:8 + | +LL | trait ChannelSender { + | ------------- in this trait +... +LL | fn autobatch<F>(self) -> impl Trait + | ^^^^^^^^^ this trait's method doesn't have the requirement `F: Callback<i32>` +help: consider further restricting this bound + | +LL | F: Callback<Self::CallbackArg> + MyFn<i32>, + | +++++++++++ + +error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied + --> $DIR/false-positive-predicate-entailment-error.rs:36:30 + | +LL | fn autobatch<F>(self) -> impl Trait + | ^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>` + | +note: required for `F` to implement `Callback<i32>` + --> $DIR/false-positive-predicate-entailment-error.rs:14:21 + | +LL | impl<A, F: MyFn<A>> Callback<A> for F { + | ------- ^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + +error[E0277]: the trait bound `F: Callback<i32>` is not satisfied + --> $DIR/false-positive-predicate-entailment-error.rs:27:12 + | +LL | F: Callback<Self::CallbackArg>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>` + | +note: required for `F` to implement `Callback<i32>` + --> $DIR/false-positive-predicate-entailment-error.rs:14:21 + | +LL | impl<A, F: MyFn<A>> Callback<A> for F { + | ------- ^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + +error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied + --> $DIR/false-positive-predicate-entailment-error.rs:36:5 + | +LL | / fn autobatch<F>(self) -> impl Trait +LL | | +LL | | +LL | | +... | +LL | | where +LL | | F: Callback<Self::CallbackArg>, + | |_______________________________________^ the trait `MyFn<i32>` is not implemented for `F`, which is required by `F: Callback<i32>` + | +note: required for `F` to implement `Callback<i32>` + --> $DIR/false-positive-predicate-entailment-error.rs:14:21 + | +LL | impl<A, F: MyFn<A>> Callback<A> for F { + | ------- ^^^^^^^^^^^ ^ + | | + | unsatisfied trait bound introduced here + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider further restricting this bound + | +LL | F: Callback<Self::CallbackArg> + MyFn<i32>, + | +++++++++++ + +error[E0277]: the trait bound `F: MyFn<i32>` is not satisfied + --> $DIR/false-positive-predicate-entailment-error.rs:43:12 + | +LL | F: Callback<Self::CallbackArg>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `MyFn<i32>` is not implemented for `F` + | +note: required by a bound in `Callback` + --> $DIR/false-positive-predicate-entailment-error.rs:10:20 + | +LL | trait Callback<A>: MyFn<A, Output = Self::Ret> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Callback` +help: consider further restricting this bound + | +LL | F: Callback<Self::CallbackArg> + MyFn<i32>, + | +++++++++++ + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs new file mode 100644 index 00000000000..59fdeab9e0a --- /dev/null +++ b/tests/ui/impl-trait/in-trait/false-positive-predicate-entailment-error.rs @@ -0,0 +1,51 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@[next] check-pass + +trait MyFn<T> { + type Output; +} + +trait Callback<A>: MyFn<A, Output = Self::Ret> { + type Ret; +} + +impl<A, F: MyFn<A>> Callback<A> for F { + type Ret = F::Output; +} + +struct Thing; +trait Trait {} +impl Trait for Thing {} + +trait ChannelSender { + type CallbackArg; + + fn autobatch<F>(self) -> impl Trait + where + F: Callback<Self::CallbackArg>; + //[current]~^ ERROR the trait bound `F: Callback<i32>` is not satisfied +} + +struct Sender; + +impl ChannelSender for Sender { + type CallbackArg = i32; + + fn autobatch<F>(self) -> impl Trait + //[current]~^ ERROR the trait bound `F: MyFn<i32>` is not satisfied + //[current]~| ERROR the trait bound `F: MyFn<i32>` is not satisfied + //[current]~| ERROR the trait bound `F: MyFn<i32>` is not satisfied + //[current]~| ERROR the trait bound `F: MyFn<i32>` is not satisfied + //[current]~| ERROR the trait bound `F: MyFn<i32>` is not satisfied + where + F: Callback<Self::CallbackArg>, + //[current]~^ ERROR the trait bound `F: Callback<i32>` is not satisfied + //[current]~| ERROR the trait bound `F: MyFn<i32>` is not satisfied + { + Thing + } +} + +fn main() {} diff --git a/tests/ui/impl-trait/nested_impl_trait.rs b/tests/ui/impl-trait/nested_impl_trait.rs index 502b2af2bc6..760102794c3 100644 --- a/tests/ui/impl-trait/nested_impl_trait.rs +++ b/tests/ui/impl-trait/nested_impl_trait.rs @@ -5,7 +5,7 @@ fn fine(x: impl Into<u32>) -> impl Into<u32> { x } fn bad_in_ret_position(x: impl Into<u32>) -> impl Into<impl Debug> { x } //~^ ERROR nested `impl Trait` is not allowed -//~| ERROR the trait bound `impl Into<u32>: Into<impl Debug>` is not satisfied +//~| ERROR the trait bound `impl Debug: From<impl Into<u32>>` is not satisfied fn bad_in_fn_syntax(x: fn() -> impl Into<impl Debug>) {} //~^ ERROR nested `impl Trait` is not allowed @@ -18,7 +18,7 @@ struct X; impl X { fn bad(x: impl Into<u32>) -> impl Into<impl Debug> { x } //~^ ERROR nested `impl Trait` is not allowed - //~| ERROR the trait bound `impl Into<u32>: Into<impl Debug>` is not satisfied + //~| ERROR the trait bound `impl Debug: From<impl Into<u32>>` is not satisfied } fn allowed_in_assoc_type() -> impl Iterator<Item=impl Fn()> { diff --git a/tests/ui/impl-trait/nested_impl_trait.stderr b/tests/ui/impl-trait/nested_impl_trait.stderr index a53312e5c0b..83d1347aff4 100644 --- a/tests/ui/impl-trait/nested_impl_trait.stderr +++ b/tests/ui/impl-trait/nested_impl_trait.stderr @@ -42,7 +42,7 @@ LL | fn bad_in_fn_syntax(x: fn() -> impl Into<impl Debug>) {} | = note: `impl Trait` is only allowed in arguments and return types of functions and methods -error[E0277]: the trait bound `impl Into<u32>: Into<impl Debug>` is not satisfied +error[E0277]: the trait bound `impl Debug: From<impl Into<u32>>` is not satisfied --> $DIR/nested_impl_trait.rs:6:46 | LL | fn bad_in_ret_position(x: impl Into<u32>) -> impl Into<impl Debug> { x } @@ -51,7 +51,7 @@ LL | fn bad_in_ret_position(x: impl Into<u32>) -> impl Into<impl Debug> { x } = help: the trait `Into<U>` is implemented for `T` = note: required for `impl Into<u32>` to implement `Into<impl Debug>` -error[E0277]: the trait bound `impl Into<u32>: Into<impl Debug>` is not satisfied +error[E0277]: the trait bound `impl Debug: From<impl Into<u32>>` is not satisfied --> $DIR/nested_impl_trait.rs:19:34 | LL | fn bad(x: impl Into<u32>) -> impl Into<impl Debug> { x } diff --git a/tests/ui/intrinsics/not-overridden.rs b/tests/ui/intrinsics/not-overridden.rs index a53071e304d..93b408331b8 100644 --- a/tests/ui/intrinsics/not-overridden.rs +++ b/tests/ui/intrinsics/not-overridden.rs @@ -1,6 +1,6 @@ //! Check that intrinsics that do not get overridden, but are marked as such, //! cause an error instead of silently invoking the body. -#![feature(rustc_attrs/* , effects*/)] // FIXME(effects) +#![feature(rustc_attrs)] //@ build-fail //@ failure-status:101 //@ normalize-stderr-test ".*note: .*\n\n" -> "" diff --git a/tests/ui/intrinsics/safe-intrinsic-mismatch.effects.stderr b/tests/ui/intrinsics/safe-intrinsic-mismatch.effects.stderr new file mode 100644 index 00000000000..d9a4960feec --- /dev/null +++ b/tests/ui/intrinsics/safe-intrinsic-mismatch.effects.stderr @@ -0,0 +1,52 @@ +error: using `#![feature(effects)]` without enabling next trait solver globally + | + = note: the next trait solver must be enabled globally for the effects feature to work correctly + = help: use `-Znext-solver` to enable + +error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `size_of` + --> $DIR/safe-intrinsic-mismatch.rs:11:5 + | +LL | fn size_of<T>() -> usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `size_of` + --> $DIR/safe-intrinsic-mismatch.rs:11:5 + | +LL | fn size_of<T>() -> usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `assume` + --> $DIR/safe-intrinsic-mismatch.rs:16:1 + | +LL | const fn assume(_b: bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: intrinsic has wrong type + --> $DIR/safe-intrinsic-mismatch.rs:16:16 + | +LL | const fn assume(_b: bool) {} + | ^ expected unsafe fn, found safe fn + | + = note: expected signature `unsafe fn(_)` + found signature `fn(_)` + +error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `const_deallocate` + --> $DIR/safe-intrinsic-mismatch.rs:20:1 + | +LL | const fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0308]: intrinsic has wrong type + --> $DIR/safe-intrinsic-mismatch.rs:20:26 + | +LL | const fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} + | ^ expected unsafe fn, found safe fn + | + = note: expected signature `unsafe fn(_, _, _)` + found signature `fn(_, _, _)` + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/intrinsics/safe-intrinsic-mismatch.rs b/tests/ui/intrinsics/safe-intrinsic-mismatch.rs index c116ba7a62e..af563e996c1 100644 --- a/tests/ui/intrinsics/safe-intrinsic-mismatch.rs +++ b/tests/ui/intrinsics/safe-intrinsic-mismatch.rs @@ -1,6 +1,11 @@ +//@ revisions: stock effects #![feature(intrinsics)] #![feature(rustc_attrs)] -// FIXME(effects) do this with revisions #![feature(effects)] +// as effects insert a const generic param to const intrinsics, +// check here that it doesn't report a const param mismatch either +// enabling or disabling effects. +#![cfg_attr(effects, feature(effects))] +#![allow(incomplete_features)] extern "rust-intrinsic" { fn size_of<T>() -> usize; //~ ERROR intrinsic safety mismatch diff --git a/tests/ui/intrinsics/safe-intrinsic-mismatch.stderr b/tests/ui/intrinsics/safe-intrinsic-mismatch.stock.stderr index 7f37e0f8211..6864c0f36de 100644 --- a/tests/ui/intrinsics/safe-intrinsic-mismatch.stderr +++ b/tests/ui/intrinsics/safe-intrinsic-mismatch.stock.stderr @@ -1,11 +1,11 @@ error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `size_of` - --> $DIR/safe-intrinsic-mismatch.rs:6:5 + --> $DIR/safe-intrinsic-mismatch.rs:11:5 | LL | fn size_of<T>() -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^ error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `size_of` - --> $DIR/safe-intrinsic-mismatch.rs:6:5 + --> $DIR/safe-intrinsic-mismatch.rs:11:5 | LL | fn size_of<T>() -> usize; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -13,13 +13,13 @@ LL | fn size_of<T>() -> usize; = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `assume` - --> $DIR/safe-intrinsic-mismatch.rs:11:1 + --> $DIR/safe-intrinsic-mismatch.rs:16:1 | LL | const fn assume(_b: bool) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: intrinsic has wrong type - --> $DIR/safe-intrinsic-mismatch.rs:11:16 + --> $DIR/safe-intrinsic-mismatch.rs:16:16 | LL | const fn assume(_b: bool) {} | ^ expected unsafe fn, found safe fn @@ -28,13 +28,13 @@ LL | const fn assume(_b: bool) {} found signature `fn(_)` error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `const_deallocate` - --> $DIR/safe-intrinsic-mismatch.rs:15:1 + --> $DIR/safe-intrinsic-mismatch.rs:20:1 | LL | const fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: intrinsic has wrong type - --> $DIR/safe-intrinsic-mismatch.rs:15:26 + --> $DIR/safe-intrinsic-mismatch.rs:20:26 | LL | const fn const_deallocate(_ptr: *mut u8, _size: usize, _align: usize) {} | ^ expected unsafe fn, found safe fn diff --git a/tests/ui/issues/issue-20676.rs b/tests/ui/issues/issue-20676.rs index b3319950b42..2059365c7d6 100644 --- a/tests/ui/issues/issue-20676.rs +++ b/tests/ui/issues/issue-20676.rs @@ -8,5 +8,5 @@ use std::fmt; fn main() { let a: &dyn fmt::Debug = &1; - format!("{:?}", a); + let _ = format!("{:?}", a); } diff --git a/tests/ui/lint/lint-unconditional-recursion-tail-calls.rs b/tests/ui/lint/lint-unconditional-recursion-tail-calls.rs new file mode 100644 index 00000000000..c94bf032579 --- /dev/null +++ b/tests/ui/lint/lint-unconditional-recursion-tail-calls.rs @@ -0,0 +1,24 @@ +#![allow(incomplete_features, dead_code)] +#![deny(unconditional_recursion)] //~ note: the lint level is defined here +#![feature(explicit_tail_calls)] + +fn f(x: bool) { + //~^ error: function cannot return without recursing + //~| note: cannot return without recursing + if x { + become f(!x) + } else { + f(!x) //~ note: recursive call site + } +} + +// This should *not* lint, tail-recursive functions which never return is a reasonable thing +fn g(x: bool) { + if x { + become g(!x) + } else { + become g(!x) + } +} + +fn main() {} diff --git a/tests/ui/lint/lint-unconditional-recursion-tail-calls.stderr b/tests/ui/lint/lint-unconditional-recursion-tail-calls.stderr new file mode 100644 index 00000000000..52f9740d027 --- /dev/null +++ b/tests/ui/lint/lint-unconditional-recursion-tail-calls.stderr @@ -0,0 +1,18 @@ +error: function cannot return without recursing + --> $DIR/lint-unconditional-recursion-tail-calls.rs:5:1 + | +LL | fn f(x: bool) { + | ^^^^^^^^^^^^^ cannot return without recursing +... +LL | f(!x) + | ----- recursive call site + | + = help: a `loop` may express intention better if this is on purpose +note: the lint level is defined here + --> $DIR/lint-unconditional-recursion-tail-calls.rs:2:9 + | +LL | #![deny(unconditional_recursion)] + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs b/tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs index e44eeffb01b..1acefa314aa 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs +++ b/tests/ui/macros/macro-metavar-expr-concat/allowed-operations.rs @@ -37,6 +37,16 @@ macro_rules! without_dollar_sign_is_an_ident { }; } +macro_rules! literals { + ($ident:ident) => {{ + let ${concat(_a, "_b")}: () = (); + let ${concat("_b", _a)}: () = (); + + let ${concat($ident, "_b")}: () = (); + let ${concat("_b", $ident)}: () = (); + }}; +} + fn main() { create_things!(behold); behold_separated_idents_in_a_fn(); @@ -55,4 +65,6 @@ fn main() { without_dollar_sign_is_an_ident!(_123); assert_eq!(VARident, 1); assert_eq!(VAR_123, 2); + + literals!(_hello); } diff --git a/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.rs b/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.rs index f72b9baca89..b1cb2141cc4 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.rs +++ b/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.rs @@ -26,14 +26,14 @@ macro_rules! idents_11 { macro_rules! no_params { () => { let ${concat(r#abc, abc)}: () = (); - //~^ ERROR `${concat(..)}` currently does not support raw identifiers + //~^ ERROR expected identifier or string literal //~| ERROR expected pattern, found `$` let ${concat(abc, r#abc)}: () = (); - //~^ ERROR `${concat(..)}` currently does not support raw identifiers + //~^ ERROR expected identifier or string literal let ${concat(r#abc, r#abc)}: () = (); - //~^ ERROR `${concat(..)}` currently does not support raw identifiers + //~^ ERROR expected identifier or string literal }; } diff --git a/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.stderr b/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.stderr index dd525cf0801..4e11e20acc5 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.stderr +++ b/tests/ui/macros/macro-metavar-expr-concat/raw-identifiers.stderr @@ -1,16 +1,16 @@ -error: `${concat(..)}` currently does not support raw identifiers +error: expected identifier or string literal --> $DIR/raw-identifiers.rs:28:22 | LL | let ${concat(r#abc, abc)}: () = (); | ^^^^^ -error: `${concat(..)}` currently does not support raw identifiers +error: expected identifier or string literal --> $DIR/raw-identifiers.rs:32:27 | LL | let ${concat(abc, r#abc)}: () = (); | ^^^^^ -error: `${concat(..)}` currently does not support raw identifiers +error: expected identifier or string literal --> $DIR/raw-identifiers.rs:35:22 | LL | let ${concat(r#abc, r#abc)}: () = (); diff --git a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs b/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs index bf47442ea76..b2845c8d1c1 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs +++ b/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.rs @@ -11,9 +11,6 @@ macro_rules! wrong_concat_declarations { ${concat(aaaa,)} //~^ ERROR expected identifier - ${concat(aaaa, 1)} - //~^ ERROR expected identifier - ${concat(_, aaaa)} ${concat(aaaa aaaa)} @@ -30,9 +27,6 @@ macro_rules! wrong_concat_declarations { ${concat($ex, aaaa,)} //~^ ERROR expected identifier - - ${concat($ex, aaaa, 123)} - //~^ ERROR expected identifier }; } @@ -43,8 +37,80 @@ macro_rules! dollar_sign_without_referenced_ident { }; } +macro_rules! starting_number { + ($ident:ident) => {{ + let ${concat("1", $ident)}: () = (); + //~^ ERROR `${concat(..)}` is not generating a valid identifier + }}; +} + +macro_rules! starting_valid_unicode { + ($ident:ident) => {{ + let ${concat("Ý", $ident)}: () = (); + }}; +} + +macro_rules! starting_invalid_unicode { + ($ident:ident) => {{ + let ${concat("\u{00BD}", $ident)}: () = (); + //~^ ERROR `${concat(..)}` is not generating a valid identifier + }}; +} + +macro_rules! ending_number { + ($ident:ident) => {{ + let ${concat($ident, "1")}: () = (); + }}; +} + +macro_rules! ending_valid_unicode { + ($ident:ident) => {{ + let ${concat($ident, "Ý")}: () = (); + }}; +} + +macro_rules! ending_invalid_unicode { + ($ident:ident) => {{ + let ${concat($ident, "\u{00BD}")}: () = (); + //~^ ERROR `${concat(..)}` is not generating a valid identifier + }}; +} + +macro_rules! empty { + () => {{ + let ${concat("", "")}: () = (); + //~^ ERROR `${concat(..)}` is not generating a valid identifier + }}; +} + +macro_rules! unsupported_literals { + ($ident:ident) => {{ + let ${concat(_a, 'b')}: () = (); + //~^ ERROR expected identifier or string literal + //~| ERROR expected pattern + let ${concat(_a, 1)}: () = (); + //~^ ERROR expected identifier or string literal + + let ${concat($ident, 'b')}: () = (); + //~^ ERROR expected identifier or string literal + let ${concat($ident, 1)}: () = (); + //~^ ERROR expected identifier or string literal + }}; +} + fn main() { wrong_concat_declarations!(1); dollar_sign_without_referenced_ident!(VAR); + + starting_number!(_abc); + starting_valid_unicode!(_abc); + starting_invalid_unicode!(_abc); + + ending_number!(_abc); + ending_valid_unicode!(_abc); + ending_invalid_unicode!(_abc); + unsupported_literals!(_abc); + + empty!(); } diff --git a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr b/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr index b216a86d59a..2fe5842b39e 100644 --- a/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr +++ b/tests/ui/macros/macro-metavar-expr-concat/syntax-errors.stderr @@ -1,4 +1,4 @@ -error: expected identifier +error: expected identifier or string literal --> $DIR/syntax-errors.rs:5:10 | LL | ${concat()} @@ -10,59 +10,126 @@ error: `concat` must have at least two elements LL | ${concat(aaaa)} | ^^^^^^ -error: expected identifier +error: expected identifier or string literal --> $DIR/syntax-errors.rs:11:10 | LL | ${concat(aaaa,)} | ^^^^^^^^^^^^^^^ -error: expected identifier, found `1` - --> $DIR/syntax-errors.rs:14:24 - | -LL | ${concat(aaaa, 1)} - | ^ help: try removing `1` - error: expected comma - --> $DIR/syntax-errors.rs:19:10 + --> $DIR/syntax-errors.rs:16:10 | LL | ${concat(aaaa aaaa)} | ^^^^^^^^^^^^^^^^^^^ error: `concat` must have at least two elements - --> $DIR/syntax-errors.rs:22:11 + --> $DIR/syntax-errors.rs:19:11 | LL | ${concat($ex)} | ^^^^^^ error: expected comma - --> $DIR/syntax-errors.rs:28:10 + --> $DIR/syntax-errors.rs:25:10 | LL | ${concat($ex, aaaa 123)} | ^^^^^^^^^^^^^^^^^^^^^^^ -error: expected identifier - --> $DIR/syntax-errors.rs:31:10 +error: expected identifier or string literal + --> $DIR/syntax-errors.rs:28:10 | LL | ${concat($ex, aaaa,)} | ^^^^^^^^^^^^^^^^^^^^ -error: expected identifier, found `123` - --> $DIR/syntax-errors.rs:34:29 +error: expected identifier or string literal + --> $DIR/syntax-errors.rs:88:26 | -LL | ${concat($ex, aaaa, 123)} - | ^^^ help: try removing `123` +LL | let ${concat(_a, 'b')}: () = (); + | ^^^ + +error: expected identifier or string literal + --> $DIR/syntax-errors.rs:91:26 + | +LL | let ${concat(_a, 1)}: () = (); + | ^ + +error: expected identifier or string literal + --> $DIR/syntax-errors.rs:94:30 + | +LL | let ${concat($ident, 'b')}: () = (); + | ^^^ + +error: expected identifier or string literal + --> $DIR/syntax-errors.rs:96:30 + | +LL | let ${concat($ident, 1)}: () = (); + | ^ error: `${concat(..)}` currently only accepts identifiers or meta-variables as parameters - --> $DIR/syntax-errors.rs:25:19 + --> $DIR/syntax-errors.rs:22:19 | LL | ${concat($ex, aaaa)} | ^^ error: variable `foo` is not recognized in meta-variable expression - --> $DIR/syntax-errors.rs:41:30 + --> $DIR/syntax-errors.rs:35:30 | LL | const ${concat(FOO, $foo)}: i32 = 2; | ^^^ -error: aborting due to 11 previous errors +error: `${concat(..)}` is not generating a valid identifier + --> $DIR/syntax-errors.rs:42:14 + | +LL | let ${concat("1", $ident)}: () = (); + | ^^^^^^^^^^^^^^^^^^^^^ +... +LL | starting_number!(_abc); + | ---------------------- in this macro invocation + | + = note: this error originates in the macro `starting_number` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `${concat(..)}` is not generating a valid identifier + --> $DIR/syntax-errors.rs:55:14 + | +LL | let ${concat("\u{00BD}", $ident)}: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | starting_invalid_unicode!(_abc); + | ------------------------------- in this macro invocation + | + = note: this error originates in the macro `starting_invalid_unicode` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `${concat(..)}` is not generating a valid identifier + --> $DIR/syntax-errors.rs:74:14 + | +LL | let ${concat($ident, "\u{00BD}")}: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | ending_invalid_unicode!(_abc); + | ----------------------------- in this macro invocation + | + = note: this error originates in the macro `ending_invalid_unicode` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected pattern, found `$` + --> $DIR/syntax-errors.rs:88:13 + | +LL | let ${concat(_a, 'b')}: () = (); + | ^ expected pattern +... +LL | unsupported_literals!(_abc); + | --------------------------- in this macro invocation + | + = note: this error originates in the macro `unsupported_literals` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: `${concat(..)}` is not generating a valid identifier + --> $DIR/syntax-errors.rs:81:14 + | +LL | let ${concat("", "")}: () = (); + | ^^^^^^^^^^^^^^^^ +... +LL | empty!(); + | -------- in this macro invocation + | + = note: this error originates in the macro `empty` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 18 previous errors diff --git a/tests/ui/macros/macro-metavar-expr-concat/unicode-expansion.rs b/tests/ui/macros/macro-metavar-expr-concat/unicode-expansion.rs new file mode 100644 index 00000000000..b2cfb211e2d --- /dev/null +++ b/tests/ui/macros/macro-metavar-expr-concat/unicode-expansion.rs @@ -0,0 +1,14 @@ +//@ run-pass + +#![feature(macro_metavar_expr_concat)] + +macro_rules! turn_to_page { + ($ident:ident) => { + const ${concat("Ḧ", $ident)}: i32 = 394; + }; +} + +fn main() { + turn_to_page!(P); + assert_eq!(ḦP, 394); +} diff --git a/tests/ui/macros/out-of-scope-calls-false-positives.rs b/tests/ui/macros/out-of-scope-calls-false-positives.rs new file mode 100644 index 00000000000..8d696c177e4 --- /dev/null +++ b/tests/ui/macros/out-of-scope-calls-false-positives.rs @@ -0,0 +1,10 @@ +//@ check-pass +//@ needs-asm-support + +macro_rules! mac { () => { "" } } +macro_rules! mac2 { () => { "auxiliary/issue-40469.rs" } } + +std::arch::global_asm!(mac!()); // OK +include!(mac2!()); // OK + +fn main() {} diff --git a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr index 8e4ba192d79..2c44ad2e0a4 100644 --- a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr +++ b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr @@ -190,7 +190,7 @@ error: unrecognized meta-variable expression LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; | ^^^^^^^^^^^^^^ help: supported expressions are count, ignore, index and len -error: expected identifier +error: expected identifier or string literal --> $DIR/syntax-errors.rs:118:33 | LL | ( $( $i:ident ),* ) => { ${ {} } }; diff --git a/tests/ui/mir/alignment/misaligned-constant-gvn.rs b/tests/ui/mir/alignment/misaligned-constant-gvn.rs new file mode 100644 index 00000000000..363d5c0ed34 --- /dev/null +++ b/tests/ui/mir/alignment/misaligned-constant-gvn.rs @@ -0,0 +1,8 @@ +//@ build-pass +//@ compile-flags: -Zmir-opt-level=0 -Zmir-enable-passes=+GVN + +fn main() { + let variant: Option<u32> = None; + let transmuted: u64 = unsafe { std::mem::transmute(variant) }; + println!("{transmuted}"); +} diff --git a/tests/ui/mismatched_types/cast-rfc0401.rs b/tests/ui/mismatched_types/cast-rfc0401.rs index 57222f45947..b2ff5b4a0c0 100644 --- a/tests/ui/mismatched_types/cast-rfc0401.rs +++ b/tests/ui/mismatched_types/cast-rfc0401.rs @@ -66,7 +66,7 @@ fn main() let cf: *const dyn Foo = &0; let _ = cf as *const [u16]; //~ ERROR is invalid - let _ = cf as *const dyn Bar; //~ ERROR is invalid + let _ = cf as *const dyn Bar; //~ ERROR casting `*const dyn Foo` as `*const dyn Bar` is invalid vec![0.0].iter().map(|s| s as f32).collect::<Vec<f32>>(); //~ ERROR is invalid } diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr index fbd92f8f662..a75039b8237 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.stderr +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2015.stderr @@ -1,5 +1,5 @@ warning: never type fallback affects this call to an `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:8:18 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:13:18 | LL | unsafe { mem::zeroed() } | ^^^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | unsafe { mem::zeroed() } = note: `#[warn(never_type_fallback_flowing_into_unsafe)]` on by default warning: never type fallback affects this call to an `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:23:13 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:30:13 | LL | core::mem::transmute(Zst) | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | core::mem::transmute(Zst) = help: specify the type explicitly warning: never type fallback affects this union access - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:39:18 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:47:18 | LL | unsafe { Union { a: () }.b } | ^^^^^^^^^^^^^^^^^ @@ -30,7 +30,7 @@ LL | unsafe { Union { a: () }.b } = help: specify the type explicitly warning: never type fallback affects this raw pointer dereference - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:49:18 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:58:18 | LL | unsafe { *ptr::from_ref(&()).cast() } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | unsafe { *ptr::from_ref(&()).cast() } = help: specify the type explicitly warning: never type fallback affects this call to an `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:67:18 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:79:18 | LL | unsafe { internally_create(x) } | ^^^^^^^^^^^^^^^^^^^^ @@ -50,7 +50,7 @@ LL | unsafe { internally_create(x) } = help: specify the type explicitly warning: never type fallback affects this call to an `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:83:18 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:97:18 | LL | unsafe { zeroed() } | ^^^^^^^^ @@ -60,7 +60,7 @@ LL | unsafe { zeroed() } = help: specify the type explicitly warning: never type fallback affects this `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:79:22 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:92:22 | LL | let zeroed = mem::zeroed; | ^^^^^^^^^^^ @@ -70,7 +70,7 @@ LL | let zeroed = mem::zeroed; = help: specify the type explicitly warning: never type fallback affects this `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:98:17 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:115:17 | LL | let f = internally_create; | ^^^^^^^^^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let f = internally_create; = help: specify the type explicitly warning: never type fallback affects this call to an `unsafe` method - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:122:13 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:140:13 | LL | S(marker::PhantomData).create_out_of_thin_air() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -90,7 +90,7 @@ LL | S(marker::PhantomData).create_out_of_thin_air() = help: specify the type explicitly warning: never type fallback affects this call to an `unsafe` function - --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:139:19 + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:158:19 | LL | match send_message::<_ /* ?0 */>() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr new file mode 100644 index 00000000000..4138e9f8c86 --- /dev/null +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.e2024.stderr @@ -0,0 +1,116 @@ +error: never type fallback affects this call to an `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:13:18 + | +LL | unsafe { mem::zeroed() } + | ^^^^^^^^^^^^^ + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the type explicitly + = note: `#[deny(never_type_fallback_flowing_into_unsafe)]` on by default + +error: never type fallback affects this call to an `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:30:13 + | +LL | core::mem::transmute(Zst) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the type explicitly + +error: never type fallback affects this union access + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:47:18 + | +LL | unsafe { Union { a: () }.b } + | ^^^^^^^^^^^^^^^^^ + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the type explicitly + +error: never type fallback affects this raw pointer dereference + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:58:18 + | +LL | unsafe { *ptr::from_ref(&()).cast() } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the type explicitly + +error: never type fallback affects this call to an `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:79:18 + | +LL | unsafe { internally_create(x) } + | ^^^^^^^^^^^^^^^^^^^^ + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the type explicitly + +error: never type fallback affects this call to an `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:97:18 + | +LL | unsafe { zeroed() } + | ^^^^^^^^ + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the type explicitly + +error: never type fallback affects this `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:92:22 + | +LL | let zeroed = mem::zeroed; + | ^^^^^^^^^^^ + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the type explicitly + +error: never type fallback affects this `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:115:17 + | +LL | let f = internally_create; + | ^^^^^^^^^^^^^^^^^ + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the type explicitly + +error: never type fallback affects this call to an `unsafe` method + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:140:13 + | +LL | S(marker::PhantomData).create_out_of_thin_air() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the type explicitly + +error: never type fallback affects this call to an `unsafe` function + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:158:19 + | +LL | match send_message::<_ /* ?0 */>() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | msg_send!(); + | ----------- in this macro invocation + | + = warning: this will change its meaning in a future release! + = note: for more information, see issue #123748 <https://github.com/rust-lang/rust/issues/123748> + = help: specify the type explicitly + = note: this error originates in the macro `msg_send` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: the type `!` does not permit zero-initialization + --> $DIR/lint-never-type-fallback-flowing-into-unsafe.rs:13:18 + | +LL | unsafe { mem::zeroed() } + | ^^^^^^^^^^^^^ this code causes undefined behavior when executed + | + = note: the `!` type has no valid value + = note: `#[warn(invalid_value)]` on by default + +error: aborting due to 10 previous errors; 1 warning emitted + diff --git a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs index d65bfee843e..c96f4dda3f8 100644 --- a/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs +++ b/tests/ui/never_type/lint-never-type-fallback-flowing-into-unsafe.rs @@ -1,4 +1,9 @@ -//@ check-pass +//@ revisions: e2015 e2024 +//@[e2015] check-pass +//@[e2024] check-fail +//@[e2024] edition:2024 +//@[e2024] compile-flags: -Zunstable-options + use std::{marker, mem, ptr}; fn main() {} @@ -6,8 +11,10 @@ fn main() {} fn _zero() { if false { unsafe { mem::zeroed() } - //~^ warn: never type fallback affects this call to an `unsafe` function + //[e2015]~^ warn: never type fallback affects this call to an `unsafe` function + //[e2024]~^^ error: never type fallback affects this call to an `unsafe` function //~| warn: this will change its meaning in a future release! + //[e2024]~| warning: the type `!` does not permit zero-initialization } else { return; }; @@ -21,7 +28,8 @@ fn _trans() { unsafe { struct Zst; core::mem::transmute(Zst) - //~^ warn: never type fallback affects this call to an `unsafe` function + //[e2015]~^ warn: never type fallback affects this call to an `unsafe` function + //[e2024]~^^ error: never type fallback affects this call to an `unsafe` function //~| warn: this will change its meaning in a future release! } } else { @@ -37,7 +45,8 @@ fn _union() { } unsafe { Union { a: () }.b } - //~^ warn: never type fallback affects this union access + //[e2015]~^ warn: never type fallback affects this union access + //[e2024]~^^ error: never type fallback affects this union access //~| warn: this will change its meaning in a future release! } else { return; @@ -47,7 +56,8 @@ fn _union() { fn _deref() { if false { unsafe { *ptr::from_ref(&()).cast() } - //~^ warn: never type fallback affects this raw pointer dereference + //[e2015]~^ warn: never type fallback affects this raw pointer dereference + //[e2024]~^^ error: never type fallback affects this raw pointer dereference //~| warn: this will change its meaning in a future release! } else { return; @@ -57,7 +67,9 @@ fn _deref() { fn _only_generics() { if false { unsafe fn internally_create<T>(_: Option<T>) { - let _ = mem::zeroed::<T>(); + unsafe { + let _ = mem::zeroed::<T>(); + } } // We need the option (and unwrap later) to call a function in a way, @@ -65,7 +77,8 @@ fn _only_generics() { let x = None; unsafe { internally_create(x) } - //~^ warn: never type fallback affects this call to an `unsafe` function + //[e2015]~^ warn: never type fallback affects this call to an `unsafe` function + //[e2024]~^^ error: never type fallback affects this call to an `unsafe` function //~| warn: this will change its meaning in a future release! x.unwrap() @@ -77,11 +90,13 @@ fn _only_generics() { fn _stored_function() { if false { let zeroed = mem::zeroed; - //~^ warn: never type fallback affects this `unsafe` function + //[e2015]~^ warn: never type fallback affects this `unsafe` function + //[e2024]~^^ error: never type fallback affects this `unsafe` function //~| warn: this will change its meaning in a future release! unsafe { zeroed() } - //~^ warn: never type fallback affects this call to an `unsafe` function + //[e2015]~^ warn: never type fallback affects this call to an `unsafe` function + //[e2024]~^^ error: never type fallback affects this call to an `unsafe` function //~| warn: this will change its meaning in a future release! } else { return; @@ -91,12 +106,15 @@ fn _stored_function() { fn _only_generics_stored_function() { if false { unsafe fn internally_create<T>(_: Option<T>) { - let _ = mem::zeroed::<T>(); + unsafe { + let _ = mem::zeroed::<T>(); + } } let x = None; let f = internally_create; - //~^ warn: never type fallback affects this `unsafe` function + //[e2015]~^ warn: never type fallback affects this `unsafe` function + //[e2024]~^^ error: never type fallback affects this `unsafe` function //~| warn: this will change its meaning in a future release! unsafe { f(x) } @@ -120,7 +138,8 @@ fn _method() { if false { unsafe { S(marker::PhantomData).create_out_of_thin_air() - //~^ warn: never type fallback affects this call to an `unsafe` method + //[e2015]~^ warn: never type fallback affects this call to an `unsafe` method + //[e2024]~^^ error: never type fallback affects this call to an `unsafe` method //~| warn: this will change its meaning in a future release! } } else { @@ -137,7 +156,8 @@ fn _objc() { macro_rules! msg_send { () => { match send_message::<_ /* ?0 */>() { - //~^ warn: never type fallback affects this call to an `unsafe` function + //[e2015]~^ warn: never type fallback affects this call to an `unsafe` function + //[e2024]~^^ error: never type fallback affects this call to an `unsafe` function //~| warn: this will change its meaning in a future release! Ok(x) => x, Err(_) => loop {}, diff --git a/tests/ui/parser/bad-let-else-statement.rs b/tests/ui/parser/bad-let-else-statement.rs index ff6619cbc98..3ede26dbcd0 100644 --- a/tests/ui/parser/bad-let-else-statement.rs +++ b/tests/ui/parser/bad-let-else-statement.rs @@ -147,14 +147,14 @@ fn o() -> Result<(), ()> { }; } -fn p() { - let 0 = become { - () - } else { - //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed - return; - }; -} +// fn p() { // FIXME(explicit_tail_calls): this currently trips an assertion... +// let 0 = become { +// () +// } else { +// // ~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed +// return; +// }; +// } fn q() { let foo = |x: i32| { diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr index 0bf6a346dfb..79d722bb7ac 100644 --- a/tests/ui/parser/bad-let-else-statement.stderr +++ b/tests/ui/parser/bad-let-else-statement.stderr @@ -204,19 +204,6 @@ LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:153:5 - | -LL | } else { - | ^ - | -help: wrap the expression in parentheses - | -LL ~ let 0 = become ({ -LL | () -LL ~ }) else { - | - -error: right curly brace `}` before `else` in a `let...else` statement not allowed --> $DIR/bad-let-else-statement.rs:163:5 | LL | } else { @@ -325,5 +312,5 @@ LL | | } else { = note: this pattern will always match, so the `else` clause is useless = help: consider removing the `else` clause -error: aborting due to 20 previous errors; 5 warnings emitted +error: aborting due to 19 previous errors; 5 warnings emitted diff --git a/tests/ui/resolve/field-and-method-in-self-not-available-in-assoc-fn.stderr b/tests/ui/resolve/field-and-method-in-self-not-available-in-assoc-fn.stderr index 3c44c1c249c..2f624f24804 100644 --- a/tests/ui/resolve/field-and-method-in-self-not-available-in-assoc-fn.stderr +++ b/tests/ui/resolve/field-and-method-in-self-not-available-in-assoc-fn.stderr @@ -2,7 +2,7 @@ error[E0425]: cannot find value `field` in this scope --> $DIR/field-and-method-in-self-not-available-in-assoc-fn.rs:11:9 | LL | field: u32, - | ---------- a field by that name exists in `Self` + | ----- a field by that name exists in `Self` ... LL | fn field(&self) -> u32 { | ----- a method by that name is available on `Self` here @@ -14,7 +14,7 @@ error[E0425]: cannot find value `field` in this scope --> $DIR/field-and-method-in-self-not-available-in-assoc-fn.rs:12:15 | LL | field: u32, - | ---------- a field by that name exists in `Self` + | ----- a field by that name exists in `Self` ... LL | fn field(&self) -> u32 { | ----- a method by that name is available on `Self` here diff --git a/tests/ui/resolve/issue-2356.stderr b/tests/ui/resolve/issue-2356.stderr index 5f75ae98870..74a2c9268a2 100644 --- a/tests/ui/resolve/issue-2356.stderr +++ b/tests/ui/resolve/issue-2356.stderr @@ -2,7 +2,7 @@ error[E0425]: cannot find value `whiskers` in this scope --> $DIR/issue-2356.rs:39:5 | LL | whiskers: isize, - | --------------- a field by that name exists in `Self` + | -------- a field by that name exists in `Self` ... LL | whiskers -= other; | ^^^^^^^^ @@ -35,7 +35,7 @@ error[E0425]: cannot find value `whiskers` in this scope --> $DIR/issue-2356.rs:84:5 | LL | whiskers: isize, - | --------------- a field by that name exists in `Self` + | -------- a field by that name exists in `Self` ... LL | whiskers = 4; | ^^^^^^^^ diff --git a/tests/ui/resolve/issue-60057.stderr b/tests/ui/resolve/issue-60057.stderr index a2ab8644353..8737cf77001 100644 --- a/tests/ui/resolve/issue-60057.stderr +++ b/tests/ui/resolve/issue-60057.stderr @@ -2,7 +2,7 @@ error[E0425]: cannot find value `banana` in this scope --> $DIR/issue-60057.rs:8:21 | LL | banana: u8, - | ---------- a field by that name exists in `Self` + | ------ a field by that name exists in `Self` ... LL | banana: banana | ^^^^^^ diff --git a/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr b/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr index 0306c8af87d..5662021a2d5 100644 --- a/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr +++ b/tests/ui/resolve/typo-suggestion-for-variable-with-name-similar-to-struct-field.stderr @@ -2,7 +2,7 @@ error[E0425]: cannot find value `config` in this scope --> $DIR/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs:7:16 | LL | config: String, - | -------------- a field by that name exists in `Self` + | ------ a field by that name exists in `Self` ... LL | Self { config } | ^^^^^^ help: a local variable with a similar name exists: `cofig` @@ -11,7 +11,7 @@ error[E0425]: cannot find value `config` in this scope --> $DIR/typo-suggestion-for-variable-with-name-similar-to-struct-field.rs:11:20 | LL | config: String, - | -------------- a field by that name exists in `Self` + | ------ a field by that name exists in `Self` ... LL | println!("{config}"); | ^^^^^^ help: a local variable with a similar name exists: `cofig` diff --git a/tests/ui/resolve/unresolved_static_type_field.stderr b/tests/ui/resolve/unresolved_static_type_field.stderr index e3de0a3fb74..f039eef2e06 100644 --- a/tests/ui/resolve/unresolved_static_type_field.stderr +++ b/tests/ui/resolve/unresolved_static_type_field.stderr @@ -2,7 +2,7 @@ error[E0425]: cannot find value `cx` in this scope --> $DIR/unresolved_static_type_field.rs:9:11 | LL | cx: bool, - | -------- a field by that name exists in `Self` + | -- a field by that name exists in `Self` ... LL | f(cx); | ^^ diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs index af88a73b4d6..63c353c7d66 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.rs @@ -1,12 +1,16 @@ //@ known-bug: #110395 +//@ failure-status: 101 +//@ normalize-stderr-test ".*note: .*\n\n" -> "" +//@ normalize-stderr-test "thread 'rustc' panicked.*:\n.*\n" -> "" +//@ rustc-env:RUST_BACKTRACE=0 // FIXME(effects) check-pass -// FIXME(effects) fix intrinsics const parameter counting +//@ compile-flags: -Znext-solver #![crate_type = "lib"] #![feature(no_core, lang_items, unboxed_closures, auto_traits, intrinsics, rustc_attrs, staged_api)] -#![feature(fundamental)] +#![feature(fundamental, marker_trait_attr)] #![feature(const_trait_impl, effects, const_mut_refs)] -#![allow(internal_features)] +#![allow(internal_features, incomplete_features)] #![no_std] #![no_core] #![stable(feature = "minicore", since = "1.0.0")] @@ -532,3 +536,35 @@ fn test_const_eval_select() { const_eval_select((), const_fn, rt_fn); } + +mod effects { + use super::Sized; + + #[lang = "EffectsNoRuntime"] + pub struct NoRuntime; + #[lang = "EffectsMaybe"] + pub struct Maybe; + #[lang = "EffectsRuntime"] + pub struct Runtime; + + #[lang = "EffectsCompat"] + pub trait Compat<#[rustc_runtime] const RUNTIME: bool> {} + + impl Compat<false> for NoRuntime {} + impl Compat<true> for Runtime {} + impl<#[rustc_runtime] const RUNTIME: bool> Compat<RUNTIME> for Maybe {} + + #[lang = "EffectsTyCompat"] + #[marker] + pub trait TyCompat<T: ?Sized> {} + + impl<T: ?Sized> TyCompat<T> for T {} + impl<T: ?Sized> TyCompat<T> for Maybe {} + impl<T: ?Sized> TyCompat<Maybe> for T {} + + #[lang = "EffectsIntersection"] + pub trait Intersection { + #[lang = "EffectsIntersectionOutput"] + type Output: ?Sized; + } +} diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.stderr index 1963332b856..823ab69df9c 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/effects/minicore.stderr @@ -1,17 +1,13 @@ -warning: the feature `effects` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/minicore.rs:8:30 - | -LL | #![feature(const_trait_impl, effects, const_mut_refs)] - | ^^^^^^^ - | - = note: see issue #102090 <https://github.com/rust-lang/rust/issues/102090> for more information - = note: `#[warn(incomplete_features)]` on by default +error: the compiler unexpectedly panicked. this is a bug. -error: requires `EffectsCompat` lang_item - --> $DIR/minicore.rs:455:9 - | -LL | impl<T: Clone> Clone for RefCell<T> { - | ^^^^^ +query stack during panic: +#0 [check_well_formed] checking that `<impl at $DIR/minicore.rs:459:1: 459:36>` is well-formed +#1 [check_mod_type_wf] checking that types are well-formed in top-level module +end of query stack -error: aborting due to 1 previous error; 1 warning emitted +error: the compiler unexpectedly panicked. this is a bug. +query stack during panic: +#0 [check_well_formed] checking that `drop` is well-formed +#1 [check_mod_type_wf] checking that types are well-formed in top-level module +end of query stack diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.nn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.nn.stderr deleted file mode 100644 index 03536dca1e8..00000000000 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.nn.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0271]: type mismatch resolving `<() as Index>::Output == &mut <() as Index>::Output` - --> $DIR/issue-100222.rs:34:12 - | -LL | fn foo(&mut self, x: <Self as Index>::Output) -> <Self as Index>::Output - | ^^^^^^^^^ expected `()`, found `&mut <() as Index>::Output` - | - = note: expected unit type `()` - found mutable reference `&mut <() as Index>::Output` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.ny.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.ny.stderr deleted file mode 100644 index 6a70a503606..00000000000 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.ny.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0271]: type mismatch resolving `<() as Index>::Output == &mut <() as Index>::Output` - --> $DIR/issue-100222.rs:25:12 - | -LL | fn foo(&mut self, x: <Self as Index>::Output) -> <Self as Index>::Output - | ^^^^^^^^^ expected `()`, found `&mut <() as Index>::Output` - | - = note: expected unit type `()` - found mutable reference `&mut <() as Index>::Output` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.rs index 7949772a2b4..47f9fc664ce 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.rs @@ -1,6 +1,7 @@ //@ revisions: nn ny yn yy -//@ known-bug: #110395 //@ compile-flags: -Znext-solver +//@ check-pass + #![allow(incomplete_features)] #![feature(const_trait_impl, effects, associated_type_defaults, const_mut_refs)] diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yn.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yn.stderr deleted file mode 100644 index 03536dca1e8..00000000000 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yn.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0271]: type mismatch resolving `<() as Index>::Output == &mut <() as Index>::Output` - --> $DIR/issue-100222.rs:34:12 - | -LL | fn foo(&mut self, x: <Self as Index>::Output) -> <Self as Index>::Output - | ^^^^^^^^^ expected `()`, found `&mut <() as Index>::Output` - | - = note: expected unit type `()` - found mutable reference `&mut <() as Index>::Output` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yy.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yy.stderr deleted file mode 100644 index 6a70a503606..00000000000 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/issue-100222.yy.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0271]: type mismatch resolving `<() as Index>::Output == &mut <() as Index>::Output` - --> $DIR/issue-100222.rs:25:12 - | -LL | fn foo(&mut self, x: <Self as Index>::Output) -> <Self as Index>::Output - | ^^^^^^^^^ expected `()`, found `&mut <() as Index>::Output` - | - = note: expected unit type `()` - found mutable reference `&mut <() as Index>::Output` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed index e072c476c6b..4e562193f0d 100644 --- a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed +++ b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.fixed @@ -3,9 +3,9 @@ trait Trait {} -fn assert_send(ptr: *mut dyn Trait) -> *mut (dyn Trait + Send) { +fn assert_send() -> *mut (dyn Trait + Send) { //~^ ERROR incorrect parentheses around trait bounds - ptr as _ + loop {} } fn foo2(_: &(dyn Trait + Send)) {} diff --git a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs index 88995141426..4a00059400c 100644 --- a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs +++ b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.rs @@ -3,9 +3,9 @@ trait Trait {} -fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) { +fn assert_send() -> *mut dyn (Trait + Send) { //~^ ERROR incorrect parentheses around trait bounds - ptr as _ + loop {} } fn foo2(_: &dyn (Trait + Send)) {} diff --git a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.stderr b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.stderr index 2d1abe91a1e..c67557fa14f 100644 --- a/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.stderr +++ b/tests/ui/suggestions/issue-114797-bad-parentheses-dyn-trait.stderr @@ -1,13 +1,13 @@ error: incorrect parentheses around trait bounds - --> $DIR/issue-114797-bad-parentheses-dyn-trait.rs:6:49 + --> $DIR/issue-114797-bad-parentheses-dyn-trait.rs:6:30 | -LL | fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) { - | ^ ^ +LL | fn assert_send() -> *mut dyn (Trait + Send) { + | ^ ^ | help: fix the parentheses | -LL - fn assert_send(ptr: *mut dyn Trait) -> *mut dyn (Trait + Send) { -LL + fn assert_send(ptr: *mut dyn Trait) -> *mut (dyn Trait + Send) { +LL - fn assert_send() -> *mut dyn (Trait + Send) { +LL + fn assert_send() -> *mut (dyn Trait + Send) { | error: incorrect parentheses around trait bounds diff --git a/tests/ui/traits/next-solver/auto-with-drop_tracking_mir.fail.stderr b/tests/ui/traits/next-solver/auto-with-drop_tracking_mir.fail.stderr index e0b23bd8110..c2029a5a1c8 100644 --- a/tests/ui/traits/next-solver/auto-with-drop_tracking_mir.fail.stderr +++ b/tests/ui/traits/next-solver/auto-with-drop_tracking_mir.fail.stderr @@ -17,10 +17,6 @@ note: required by a bound in `is_send` | LL | fn is_send(_: impl Send) {} | ^^^^ required by this bound in `is_send` -help: consider dereferencing here - | -LL | is_send(*foo()); - | + error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/typeck/receiver-self-ty-check-eq.rs b/tests/ui/traits/next-solver/typeck/receiver-self-ty-check-eq.rs new file mode 100644 index 00000000000..f8c8a17b7e5 --- /dev/null +++ b/tests/ui/traits/next-solver/typeck/receiver-self-ty-check-eq.rs @@ -0,0 +1,23 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +// Fixes a regression in `receiver_is_valid` in wfcheck where we were using +// `InferCtxt::can_eq` instead of processing alias-relate goals, leading to false +// positives, not deref'ing enough steps to check the receiver is valid. + +trait Mirror { + type Mirror: ?Sized; +} +impl<T: ?Sized> Mirror for T { + type Mirror = T; +} + +trait Foo { + fn foo(&self) {} +} + +impl Foo for <() as Mirror>::Mirror { + fn foo(&self) {} +} + +fn main() {} diff --git a/tests/ui/traits/upcast_soundness_bug.rs b/tests/ui/traits/upcast_soundness_bug.rs index 95b48cdf379..5eaa58f7efe 100644 --- a/tests/ui/traits/upcast_soundness_bug.rs +++ b/tests/ui/traits/upcast_soundness_bug.rs @@ -1,7 +1,8 @@ #![feature(trait_upcasting)] -//@ known-bug: #120222 -//@ check-pass -//! This will segfault at runtime. +//@ check-fail +// +// issue: <https://github.com/rust-lang/rust/pull/120222> +//! This would segfault at runtime. pub trait SupSupA { fn method(&self) {} @@ -56,6 +57,7 @@ pub fn user2() -> &'static dyn Trait<u8, u16> { fn main() { let p: *const dyn Trait<u8, u8> = &(); let p = p as *const dyn Trait<u8, u16>; // <- this is bad! + //~^ error: mismatched types let p = p as *const dyn Super<u16>; // <- this upcast accesses improper vtable entry // accessing from L__unnamed_2 the position for the 'Super<u16> vtable (pointer)', // thus reading 'null pointer for missing_method' diff --git a/tests/ui/traits/upcast_soundness_bug.stderr b/tests/ui/traits/upcast_soundness_bug.stderr new file mode 100644 index 00000000000..5864abcdb41 --- /dev/null +++ b/tests/ui/traits/upcast_soundness_bug.stderr @@ -0,0 +1,13 @@ +error[E0308]: mismatched types + --> $DIR/upcast_soundness_bug.rs:59:13 + | +LL | let p = p as *const dyn Trait<u8, u16>; // <- this is bad! + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `u16` + | + = note: expected trait object `dyn Trait<u8, u8>` + found trait object `dyn Trait<u8, u16>` + = help: `dyn Trait<u8, u16>` implements `Trait` so you could box the found value and coerce it to the trait object `Box<dyn Trait>`, you will have to change the expected type as well + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/method_resolution3.current.stderr b/tests/ui/type-alias-impl-trait/method_resolution3.current.stderr index e992d059daf..09efd7a9e7e 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution3.current.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution3.current.stderr @@ -8,7 +8,7 @@ LL | fn bar(self: Bar<u32>) { = 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`) error[E0307]: invalid `self` parameter type: `&Bar<u32>` - --> $DIR/method_resolution3.rs:21:18 + --> $DIR/method_resolution3.rs:20:18 | LL | fn baz(self: &Bar<u32>) { | ^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/method_resolution3.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution3.next.stderr index 9272017cdf5..09efd7a9e7e 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution3.next.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution3.next.stderr @@ -1,15 +1,21 @@ -error[E0271]: type mismatch resolving `Foo == u32` +error[E0307]: invalid `self` parameter type: `Bar<u32>` --> $DIR/method_resolution3.rs:16:18 | LL | fn bar(self: Bar<u32>) { - | ^^^^^^^^ types differ + | ^^^^^^^^ + | + = note: type of `self` must be `Self` or a type that dereferences to it + = 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`) -error[E0271]: type mismatch resolving `Foo == u32` - --> $DIR/method_resolution3.rs:21:18 +error[E0307]: invalid `self` parameter type: `&Bar<u32>` + --> $DIR/method_resolution3.rs:20:18 | LL | fn baz(self: &Bar<u32>) { - | ^^^^^^^^^ types differ + | ^^^^^^^^^ + | + = note: type of `self` must be `Self` or a type that dereferences to it + = 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`) error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0307`. diff --git a/tests/ui/type-alias-impl-trait/method_resolution3.rs b/tests/ui/type-alias-impl-trait/method_resolution3.rs index 447f3144b82..0e6176bfe03 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution3.rs +++ b/tests/ui/type-alias-impl-trait/method_resolution3.rs @@ -14,13 +14,11 @@ struct Bar<T>(T); impl Bar<Foo> { fn bar(self: Bar<u32>) { - //[current]~^ ERROR: invalid `self` parameter - //[next]~^^ ERROR: type mismatch resolving `Foo == u32` + //~^ ERROR: invalid `self` parameter self.foo() } fn baz(self: &Bar<u32>) { - //[current]~^ ERROR: invalid `self` parameter - //[next]~^^ ERROR: type mismatch resolving `Foo == u32` + //~^ ERROR: invalid `self` parameter self.foo() } } diff --git a/tests/ui/type-alias-impl-trait/method_resolution4.current.stderr b/tests/ui/type-alias-impl-trait/method_resolution4.current.stderr index 3a2ca18f890..8ffdb21f251 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution4.current.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution4.current.stderr @@ -8,7 +8,7 @@ LL | fn foo(self: Bar<Foo>) { = 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`) error[E0307]: invalid `self` parameter type: `&Bar<Foo>` - --> $DIR/method_resolution4.rs:32:20 + --> $DIR/method_resolution4.rs:31:20 | LL | fn foomp(self: &Bar<Foo>) { | ^^^^^^^^^ diff --git a/tests/ui/type-alias-impl-trait/method_resolution4.next.stderr b/tests/ui/type-alias-impl-trait/method_resolution4.next.stderr index 33ed2800ebe..8ffdb21f251 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution4.next.stderr +++ b/tests/ui/type-alias-impl-trait/method_resolution4.next.stderr @@ -1,15 +1,21 @@ -error[E0271]: type mismatch resolving `u32 == Foo` +error[E0307]: invalid `self` parameter type: `Bar<Foo>` --> $DIR/method_resolution4.rs:27:18 | LL | fn foo(self: Bar<Foo>) { - | ^^^^^^^^ types differ + | ^^^^^^^^ + | + = note: type of `self` must be `Self` or a type that dereferences to it + = 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`) -error[E0271]: type mismatch resolving `u32 == Foo` - --> $DIR/method_resolution4.rs:32:20 +error[E0307]: invalid `self` parameter type: `&Bar<Foo>` + --> $DIR/method_resolution4.rs:31:20 | LL | fn foomp(self: &Bar<Foo>) { - | ^^^^^^^^^ types differ + | ^^^^^^^^^ + | + = note: type of `self` must be `Self` or a type that dereferences to it + = 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`) error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0307`. diff --git a/tests/ui/type-alias-impl-trait/method_resolution4.rs b/tests/ui/type-alias-impl-trait/method_resolution4.rs index 42ed04b3c30..f33b4e473ae 100644 --- a/tests/ui/type-alias-impl-trait/method_resolution4.rs +++ b/tests/ui/type-alias-impl-trait/method_resolution4.rs @@ -25,13 +25,11 @@ impl Bar<Foo> { impl Bar<u32> { fn foo(self: Bar<Foo>) { - //[current]~^ ERROR: invalid `self` parameter - //[next]~^^ ERROR: type mismatch resolving `u32 == Foo` + //~^ ERROR: invalid `self` parameter self.bar() } fn foomp(self: &Bar<Foo>) { - //[current]~^ ERROR: invalid `self` parameter - //[next]~^^ ERROR: type mismatch resolving `u32 == Foo` + //~^ ERROR: invalid `self` parameter self.bar() } } diff --git a/tests/ui/typeck/ice-with-expr-not-struct-127332.rs b/tests/ui/typeck/ice-with-expr-not-struct-127332.rs new file mode 100644 index 00000000000..f3ea360e7e9 --- /dev/null +++ b/tests/ui/typeck/ice-with-expr-not-struct-127332.rs @@ -0,0 +1,15 @@ +// Regression test for ICE #127332 + +// Tests that we do not ICE when a with expr is +// not a struct but something else like an enum + +fn main() { + let x = || { + enum Foo { + A { x: u32 }, + } + let orig = Foo::A { x: 5 }; + Foo::A { x: 6, ..orig }; + //~^ ERROR functional record update syntax requires a struct + }; +} diff --git a/tests/ui/typeck/ice-with-expr-not-struct-127332.stderr b/tests/ui/typeck/ice-with-expr-not-struct-127332.stderr new file mode 100644 index 00000000000..446f49e8639 --- /dev/null +++ b/tests/ui/typeck/ice-with-expr-not-struct-127332.stderr @@ -0,0 +1,9 @@ +error[E0436]: functional record update syntax requires a struct + --> $DIR/ice-with-expr-not-struct-127332.rs:12:26 + | +LL | Foo::A { x: 6, ..orig }; + | ^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0436`. |
