diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c650df6a0ec2..7a46d123c5d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -109,7 +109,7 @@ jobs: # intensive jobs to run on free runners, which however also have # less disk space. - name: free up disk space - uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be + run: src/ci/scripts/free-disk-space.sh if: matrix.free_disk # Rust Log Analyzer can't currently detect the PR number of a GitHub diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 8e73df63ef54..727fd59c6b30 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -28,6 +28,7 @@ use rustc_data_structures::packed::Pu128; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::Lrc; +use rustc_data_structures::tagged_ptr::Tag; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; pub use rustc_span::AttrId; use rustc_span::source_map::{Spanned, respan}; @@ -287,6 +288,7 @@ impl ParenthesizedArgs { } } +use crate::AstDeref; pub use crate::node_id::{CRATE_NODE_ID, DUMMY_NODE_ID, NodeId}; /// Modifiers on a trait bound like `~const`, `?` and `!`. @@ -2165,6 +2167,14 @@ impl Ty { } final_ty } + + pub fn is_maybe_parenthesised_infer(&self) -> bool { + match &self.kind { + TyKind::Infer => true, + TyKind::Paren(inner) => inner.ast_deref().is_maybe_parenthesised_infer(), + _ => false, + } + } } #[derive(Clone, Encodable, Decodable, Debug)] @@ -2269,10 +2279,32 @@ impl TyKind { /// Syntax used to declare a trait object. #[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)] +#[repr(u8)] pub enum TraitObjectSyntax { - Dyn, - DynStar, - None, + // SAFETY: When adding new variants make sure to update the `Tag` impl. + Dyn = 0, + DynStar = 1, + None = 2, +} + +/// SAFETY: `TraitObjectSyntax` only has 3 data-less variants which means +/// it can be represented with a `u2`. We use `repr(u8)` to guarantee the +/// discriminants of the variants are no greater than `3`. +unsafe impl Tag for TraitObjectSyntax { + const BITS: u32 = 2; + + fn into_usize(self) -> usize { + self as u8 as usize + } + + unsafe fn from_usize(tag: usize) -> Self { + match tag { + 0 => TraitObjectSyntax::Dyn, + 1 => TraitObjectSyntax::DynStar, + 2 => TraitObjectSyntax::None, + _ => unreachable!(), + } + } } #[derive(Clone, Encodable, Decodable, Debug)] diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 9cfdbc47495b..f8fb21d5cd09 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -1,3 +1,4 @@ +use intravisit::InferKind; use rustc_data_structures::sorted_map::SortedMap; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LocalDefIdMap}; @@ -265,14 +266,6 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } - fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir>) { - self.insert(const_arg.span(), const_arg.hir_id, Node::ConstArg(const_arg)); - - self.with_parent(const_arg.hir_id, |this| { - intravisit::walk_const_arg(this, const_arg); - }); - } - fn visit_expr(&mut self, expr: &'hir Expr<'hir>) { self.insert(expr.span, expr.hir_id, Node::Expr(expr)); @@ -302,22 +295,41 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { intravisit::walk_path_segment(self, path_segment); } - fn visit_ty(&mut self, ty: &'hir Ty<'hir>) { - self.insert(ty.span, ty.hir_id, Node::Ty(ty)); + fn visit_ty(&mut self, ty: &'hir Ty<'hir, AmbigArg>) { + self.insert(ty.span, ty.hir_id, Node::Ty(ty.as_unambig_ty())); self.with_parent(ty.hir_id, |this| { intravisit::walk_ty(this, ty); }); } - fn visit_infer(&mut self, inf: &'hir InferArg) { - self.insert(inf.span, inf.hir_id, Node::Infer(inf)); + fn visit_const_arg(&mut self, const_arg: &'hir ConstArg<'hir, AmbigArg>) { + self.insert( + const_arg.as_unambig_ct().span(), + const_arg.hir_id, + Node::ConstArg(const_arg.as_unambig_ct()), + ); - self.with_parent(inf.hir_id, |this| { - intravisit::walk_inf(this, inf); + self.with_parent(const_arg.hir_id, |this| { + intravisit::walk_ambig_const_arg(this, const_arg); }); } + fn visit_infer( + &mut self, + inf_id: HirId, + inf_span: Span, + kind: InferKind<'hir>, + ) -> Self::Result { + match kind { + InferKind::Ty(ty) => self.insert(inf_span, inf_id, Node::Ty(ty)), + InferKind::Const(ct) => self.insert(inf_span, inf_id, Node::ConstArg(ct)), + InferKind::Ambig(inf) => self.insert(inf_span, inf_id, Node::Infer(inf)), + } + + self.visit_id(inf_id); + } + fn visit_trait_ref(&mut self, tr: &'hir TraitRef<'hir>) { self.insert(tr.path.span, tr.hir_ref_id, Node::TraitRef(tr)); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0e28590bd660..b9f1a4220b80 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -48,6 +48,7 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::Lrc; +use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId}; @@ -1083,17 +1084,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { match arg { ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(lt)), ast::GenericArg::Type(ty) => { + // We cannot just match on `TyKind::Infer` as `(_)` is represented as + // `TyKind::Paren(TyKind::Infer)` and should also be lowered to `GenericArg::Infer` + if ty.is_maybe_parenthesised_infer() { + return GenericArg::Infer(hir::InferArg { + hir_id: self.lower_node_id(ty.id), + span: self.lower_span(ty.span), + }); + } + match &ty.kind { - TyKind::Infer if self.tcx.features().generic_arg_infer() => { - return GenericArg::Infer(hir::InferArg { - hir_id: self.lower_node_id(ty.id), - span: self.lower_span(ty.span), - }); - } // We parse const arguments as path types as we cannot distinguish them during // parsing. We try to resolve that ambiguity by attempting resolution in both the // type and value namespaces. If we resolved the path in the value namespace, we // transform it into a generic const argument. + // + // FIXME: Should we be handling `(PATH_TO_CONST)`? TyKind::Path(None, path) => { if let Some(res) = self .resolver @@ -1110,15 +1116,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let ct = self.lower_const_path_to_const_arg(path, res, ty.id, ty.span); - return GenericArg::Const(ct); + return GenericArg::Const(ct.try_as_ambig_ct().unwrap()); } } } _ => {} } - GenericArg::Type(self.lower_ty(ty, itctx)) + GenericArg::Type(self.lower_ty(ty, itctx).try_as_ambig_ty().unwrap()) + } + ast::GenericArg::Const(ct) => { + GenericArg::Const(self.lower_anon_const_to_const_arg(ct).try_as_ambig_ct().unwrap()) } - ast::GenericArg::Const(ct) => GenericArg::Const(self.lower_anon_const_to_const_arg(ct)), } } @@ -1158,7 +1166,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let lifetime_bound = this.elided_dyn_bound(t.span); (bounds, lifetime_bound) }); - let kind = hir::TyKind::TraitObject(bounds, lifetime_bound, TraitObjectSyntax::None); + let kind = hir::TyKind::TraitObject( + bounds, + TaggedRef::new(lifetime_bound, TraitObjectSyntax::None), + ); return hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.next_id() }; } @@ -1185,7 +1196,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> { let kind = match &t.kind { - TyKind::Infer => hir::TyKind::Infer, + TyKind::Infer => hir::TyKind::Infer(()), TyKind::Err(guar) => hir::TyKind::Err(*guar), TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), @@ -1309,7 +1320,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span)); (bounds, lifetime_bound) }); - hir::TyKind::TraitObject(bounds, lifetime_bound, *kind) + hir::TyKind::TraitObject(bounds, TaggedRef::new(lifetime_bound, *kind)) } TyKind::ImplTrait(def_node_id, bounds) => { let span = t.span; @@ -2041,7 +2052,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) .stash(c.value.span, StashKey::UnderscoreForArrayLengths); } - let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span)); + let ct_kind = hir::ConstArgKind::Infer(self.lower_span(c.value.span), ()); self.arena.alloc(hir::ConstArg { hir_id: self.lower_node_id(c.id), kind: ct_kind }) } _ => self.lower_anon_const_to_const_arg(c), @@ -2365,8 +2376,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir_id = self.next_id(); hir::TyKind::TraitObject( arena_vec![self; principal], - self.elided_dyn_bound(span), - TraitObjectSyntax::None, + TaggedRef::new(self.elided_dyn_bound(span), TraitObjectSyntax::None), ) } _ => hir::TyKind::Path(hir::QPath::Resolved(None, path)), diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 043144a54649..75abff7461b3 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -525,7 +525,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])), }; - let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))]; + let args = smallvec![GenericArg::Type( + self.arena.alloc(self.ty_tup(*inputs_span, inputs)).try_as_ambig_ty().unwrap() + )]; // If we have a bound like `async Fn() -> T`, make sure that we mark the // `Output = T` associated type bound with the right feature gates. diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index f0baa20648cd..3c4e4c29197b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -8,7 +8,7 @@ use rustc_hir::QPath::Resolved; use rustc_hir::WherePredicateKind::BoundPredicate; use rustc_hir::def::Res::Def; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate}; use rustc_infer::infer::{NllRegionVariableOrigin, RelateParamBound}; use rustc_middle::bug; @@ -887,7 +887,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if alias_ty.span.desugaring_kind().is_some() { // Skip `async` desugaring `impl Future`. } - if let TyKind::TraitObject(_, lt, _) = alias_ty.kind { + if let TyKind::TraitObject(_, lt) = alias_ty.kind { if lt.ident.name == kw::Empty { spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string())); } else { @@ -987,7 +987,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { for found_did in found_dids { let mut traits = vec![]; let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); - hir_v.visit_ty(self_ty); + hir_v.visit_ty_unambig(self_ty); debug!("trait spans found: {:?}", traits); for span in &traits { let mut multi_span: MultiSpan = vec![*span].into(); diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 9349b46ec5b0..ccd13badad74 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -432,7 +432,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // must highlight the variable. // NOTE(eddyb) this is handled in/by the sole caller // (`give_name_if_anonymous_region_appears_in_arguments`). - hir::TyKind::Infer => None, + hir::TyKind::Infer(()) => None, _ => Some(argument_hir_ty), } @@ -615,7 +615,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { } (GenericArgKind::Type(ty), hir::GenericArg::Type(hir_ty)) => { - search_stack.push((ty, hir_ty)); + search_stack.push((ty, hir_ty.as_unambig_ty())); } (GenericArgKind::Const(_ct), hir::GenericArg::Const(_hir_ct)) => { diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index f6ad0c79de54..38019faa7a98 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -444,7 +444,6 @@ impl WriteBackendMethods for GccCodegenBackend { } fn autodiff( _cgcx: &CodegenContext, - _tcx: TyCtxt<'_>, _module: &ModuleCodegen, _diff_fncs: Vec, _config: &ModuleConfig, diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 5a34b52e6ef3..d2de62b17f04 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::borrow::{Borrow, Cow}; use std::ops::Deref; use std::{iter, ptr}; @@ -31,20 +31,22 @@ use tracing::{debug, instrument}; use crate::abi::FnAbiLlvmExt; use crate::attributes; use crate::common::Funclet; -use crate::context::CodegenCx; +use crate::context::{CodegenCx, SimpleCx}; use crate::llvm::{self, AtomicOrdering, AtomicRmwBinOp, BasicBlock, False, True}; use crate::type_::Type; use crate::type_of::LayoutLlvmExt; use crate::value::Value; -// All Builders must have an llfn associated with them #[must_use] -pub(crate) struct Builder<'a, 'll, 'tcx> { +pub(crate) struct GenericBuilder<'a, 'll, CX: Borrow>> { pub llbuilder: &'ll mut llvm::Builder<'ll>, - pub cx: &'a CodegenCx<'ll, 'tcx>, + pub cx: &'a CX, } -impl Drop for Builder<'_, '_, '_> { +pub(crate) type SBuilder<'a, 'll> = GenericBuilder<'a, 'll, SimpleCx<'ll>>; +pub(crate) type Builder<'a, 'll, 'tcx> = GenericBuilder<'a, 'll, CodegenCx<'ll, 'tcx>>; + +impl<'a, 'll, CX: Borrow>> Drop for GenericBuilder<'a, 'll, CX> { fn drop(&mut self) { unsafe { llvm::LLVMDisposeBuilder(&mut *(self.llbuilder as *mut _)); @@ -52,6 +54,112 @@ impl Drop for Builder<'_, '_, '_> { } } +impl<'a, 'll> SBuilder<'a, 'll> { + fn call( + &mut self, + llty: &'ll Type, + llfn: &'ll Value, + args: &[&'ll Value], + funclet: Option<&Funclet<'ll>>, + ) -> &'ll Value { + debug!("call {:?} with args ({:?})", llfn, args); + + let args = self.check_call("call", llty, llfn, args); + let funclet_bundle = funclet.map(|funclet| funclet.bundle()); + let mut bundles: SmallVec<[_; 2]> = SmallVec::new(); + if let Some(funclet_bundle) = funclet_bundle { + bundles.push(funclet_bundle); + } + + let call = unsafe { + llvm::LLVMBuildCallWithOperandBundles( + self.llbuilder, + llty, + llfn, + args.as_ptr() as *const &llvm::Value, + args.len() as c_uint, + bundles.as_ptr(), + bundles.len() as c_uint, + c"".as_ptr(), + ) + }; + call + } + + fn with_scx(scx: &'a SimpleCx<'ll>) -> Self { + // Create a fresh builder from the simple context. + let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(scx.llcx) }; + SBuilder { llbuilder, cx: scx } + } +} +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { + pub(crate) fn bitcast(&mut self, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { + unsafe { llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty, UNNAMED) } + } + + fn ret_void(&mut self) { + unsafe { + llvm::LLVMBuildRetVoid(self.llbuilder); + } + } + + fn ret(&mut self, v: &'ll Value) { + unsafe { + llvm::LLVMBuildRet(self.llbuilder, v); + } + } +} +impl<'a, 'll> SBuilder<'a, 'll> { + fn build(cx: &'a SimpleCx<'ll>, llbb: &'ll BasicBlock) -> SBuilder<'a, 'll> { + let bx = SBuilder::with_scx(cx); + unsafe { + llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb); + } + bx + } + + fn check_call<'b>( + &mut self, + typ: &str, + fn_ty: &'ll Type, + llfn: &'ll Value, + args: &'b [&'ll Value], + ) -> Cow<'b, [&'ll Value]> { + assert!( + self.cx.type_kind(fn_ty) == TypeKind::Function, + "builder::{typ} not passed a function, but {fn_ty:?}" + ); + + let param_tys = self.cx.func_params_types(fn_ty); + + let all_args_match = iter::zip(¶m_tys, args.iter().map(|&v| self.cx.val_ty(v))) + .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty); + + if all_args_match { + return Cow::Borrowed(args); + } + + let casted_args: Vec<_> = iter::zip(param_tys, args) + .enumerate() + .map(|(i, (expected_ty, &actual_val))| { + let actual_ty = self.cx.val_ty(actual_val); + if expected_ty != actual_ty { + debug!( + "type mismatch in function call of {:?}. \ + Expected {:?} for param {}, got {:?}; injecting bitcast", + llfn, expected_ty, i, actual_ty + ); + self.bitcast(actual_val, expected_ty) + } else { + actual_val + } + }) + .collect(); + + Cow::Owned(casted_args) + } +} + /// Empty string, to be used where LLVM expects an instruction name, indicating /// that the instruction is to be left unnamed (i.e. numbered, in textual IR). // FIXME(eddyb) pass `&CStr` directly to FFI once it's a thin pointer. @@ -1222,6 +1330,14 @@ impl<'ll> StaticBuilderMethods for Builder<'_, 'll, '_> { } impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { + fn build(cx: &'a CodegenCx<'ll, 'tcx>, llbb: &'ll BasicBlock) -> Builder<'a, 'll, 'tcx> { + let bx = Builder::with_cx(cx); + unsafe { + llvm::LLVMPositionBuilderAtEnd(bx.llbuilder, llbb); + } + bx + } + fn with_cx(cx: &'a CodegenCx<'ll, 'tcx>) -> Self { // Create a fresh builder from the crate context. let llbuilder = unsafe { llvm::LLVMCreateBuilderInContext(cx.llcx) }; @@ -1231,13 +1347,16 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub(crate) fn llfn(&self) -> &'ll Value { unsafe { llvm::LLVMGetBasicBlockParent(self.llbb()) } } +} +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { fn position_at_start(&mut self, llbb: &'ll BasicBlock) { unsafe { llvm::LLVMRustPositionBuilderAtStart(self.llbuilder, llbb); } } - +} +impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn align_metadata(&mut self, load: &'ll Value, align: Align) { unsafe { let md = [llvm::LLVMValueAsMetadata(self.cx.const_u64(align.bytes()))]; @@ -1259,7 +1378,8 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { self.set_metadata(inst, llvm::MD_unpredictable, md); } } - +} +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { pub(crate) fn minnum(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value { unsafe { llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs) } } @@ -1360,7 +1480,9 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { let ret = unsafe { llvm::LLVMBuildCatchRet(self.llbuilder, funclet.cleanuppad(), unwind) }; ret.expect("LLVM does not have support for catchret") } +} +impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn check_call<'b>( &mut self, typ: &str, @@ -1401,11 +1523,13 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { Cow::Owned(casted_args) } - +} +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { pub(crate) fn va_arg(&mut self, list: &'ll Value, ty: &'ll Type) -> &'ll Value { unsafe { llvm::LLVMBuildVAArg(self.llbuilder, list, ty, UNNAMED) } } - +} +impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value { let (ty, f) = self.cx.get_intrinsic(intrinsic); self.call(ty, None, None, f, args, None, None) @@ -1423,7 +1547,8 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { self.call_intrinsic(intrinsic, &[self.cx.const_u64(size), ptr]); } - +} +impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { pub(crate) fn phi( &mut self, ty: &'ll Type, @@ -1443,7 +1568,8 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); } } - +} +impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { fn fptoint_sat(&mut self, signed: bool, val: &'ll Value, dest_ty: &'ll Type) -> &'ll Value { let src_ty = self.cx.val_ty(val); let (float_ty, int_ty, vector_length) = if self.cx.type_kind(src_ty) == TypeKind::Vector { diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 38f7eaa090f9..6b17b5f6989b 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -3,20 +3,19 @@ use std::ptr; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, AutoDiffItem, DiffActivity, DiffMode}; use rustc_codegen_ssa::ModuleCodegen; use rustc_codegen_ssa::back::write::ModuleConfig; -use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, BuilderMethods}; use rustc_errors::FatalError; -use rustc_middle::ty::TyCtxt; use rustc_session::config::Lto; use tracing::{debug, trace}; use crate::back::write::{llvm_err, llvm_optimize}; -use crate::builder::Builder; -use crate::declare::declare_raw_fn; +use crate::builder::SBuilder; +use crate::context::SimpleCx; +use crate::declare::declare_simple_fn; use crate::errors::LlvmError; use crate::llvm::AttributePlace::Function; use crate::llvm::{Metadata, True}; use crate::value::Value; -use crate::{CodegenContext, LlvmCodegenBackend, ModuleLlvm, attributes, context, llvm}; +use crate::{CodegenContext, LlvmCodegenBackend, ModuleLlvm, attributes, llvm}; fn get_params(fnc: &Value) -> Vec<&Value> { unsafe { @@ -38,8 +37,8 @@ fn get_params(fnc: &Value) -> Vec<&Value> { /// [^1]: // FIXME(ZuseZ4): `outer_fn` should include upstream safety checks to // cover some assumptions of enzyme/autodiff, which could lead to UB otherwise. -fn generate_enzyme_call<'ll, 'tcx>( - cx: &context::CodegenCx<'ll, 'tcx>, +fn generate_enzyme_call<'ll>( + cx: &SimpleCx<'ll>, fn_to_diff: &'ll Value, outer_fn: &'ll Value, attrs: AutoDiffAttrs, @@ -112,7 +111,7 @@ fn generate_enzyme_call<'ll, 'tcx>( //FIXME(ZuseZ4): the CC/Addr/Vis values are best effort guesses, we should look at tests and // think a bit more about what should go here. let cc = llvm::LLVMGetFunctionCallConv(outer_fn); - let ad_fn = declare_raw_fn( + let ad_fn = declare_simple_fn( cx, &ad_name, llvm::CallConv::try_from(cc).expect("invalid callconv"), @@ -132,7 +131,7 @@ fn generate_enzyme_call<'ll, 'tcx>( llvm::LLVMRustEraseInstFromParent(br); let last_inst = llvm::LLVMRustGetLastInstruction(entry).unwrap(); - let mut builder = Builder::build(cx, entry); + let mut builder = SBuilder::build(cx, entry); let num_args = llvm::LLVMCountParams(&fn_to_diff); let mut args = Vec::with_capacity(num_args as usize + 1); @@ -236,7 +235,7 @@ fn generate_enzyme_call<'ll, 'tcx>( } } - let call = builder.call(enzyme_ty, None, None, ad_fn, &args, None, None); + let call = builder.call(enzyme_ty, ad_fn, &args, None); // This part is a bit iffy. LLVM requires that a call to an inlineable function has some // metadata attachted to it, but we just created this code oota. Given that the @@ -274,10 +273,9 @@ fn generate_enzyme_call<'ll, 'tcx>( } } -pub(crate) fn differentiate<'ll, 'tcx>( +pub(crate) fn differentiate<'ll>( module: &'ll ModuleCodegen, cgcx: &CodegenContext, - tcx: TyCtxt<'tcx>, diff_items: Vec, config: &ModuleConfig, ) -> Result<(), FatalError> { @@ -286,8 +284,7 @@ pub(crate) fn differentiate<'ll, 'tcx>( } let diag_handler = cgcx.create_dcx(); - let (_, cgus) = tcx.collect_and_partition_mono_items(()); - let cx = context::CodegenCx::new(tcx, &cgus.first().unwrap(), &module.module_llvm); + let cx = SimpleCx { llmod: module.module_llvm.llmod(), llcx: module.module_llvm.llcx }; // Before dumping the module, we want all the TypeTrees to become part of the module. for item in diff_items.iter() { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 91283a5944e7..79381f35a3cd 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -1,11 +1,13 @@ use std::borrow::Borrow; use std::cell::{Cell, RefCell}; use std::ffi::{CStr, c_char, c_uint}; +use std::ops::Deref; use std::str; use rustc_abi::{HasDataLayout, TargetDataLayout, VariantIdx}; use rustc_codegen_ssa::back::versioned_llvm_target; use rustc_codegen_ssa::base::{wants_msvc_seh, wants_wasm_eh}; +use rustc_codegen_ssa::common::TypeKind; use rustc_codegen_ssa::errors as ssa_errors; use rustc_codegen_ssa::traits::*; use rustc_data_structures::base_n::{ALPHANUMERIC_ONLY, ToBaseN}; @@ -30,23 +32,46 @@ use smallvec::SmallVec; use crate::back::write::to_llvm_code_model; use crate::callee::get_fn; -use crate::common::AsCCharPtr; +use crate::common::{self, AsCCharPtr}; use crate::debuginfo::metadata::apply_vcall_visibility_metadata; use crate::llvm::{Metadata, MetadataType}; use crate::type_::Type; use crate::value::Value; use crate::{attributes, coverageinfo, debuginfo, llvm, llvm_util}; +/// `TyCtxt` (and related cache datastructures) can't be move between threads. +/// However, there are various cx related functions which we want to be available to the builder and +/// other compiler pieces. Here we define a small subset which has enough information and can be +/// moved around more freely. +pub(crate) struct SimpleCx<'ll> { + pub llmod: &'ll llvm::Module, + pub llcx: &'ll llvm::Context, +} + +impl<'ll> Borrow> for CodegenCx<'ll, '_> { + fn borrow(&self) -> &SimpleCx<'ll> { + &self.scx + } +} + +impl<'ll, 'tcx> Deref for CodegenCx<'ll, 'tcx> { + type Target = SimpleCx<'ll>; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.scx + } +} + /// There is one `CodegenCx` per codegen unit. Each one has its own LLVM /// `llvm::Context` so that several codegen units may be processed in parallel. /// All other LLVM data structures in the `CodegenCx` are tied to that `llvm::Context`. pub(crate) struct CodegenCx<'ll, 'tcx> { pub tcx: TyCtxt<'tcx>, + pub scx: SimpleCx<'ll>, pub use_dll_storage_attrs: bool, pub tls_model: llvm::ThreadLocalMode, - pub llmod: &'ll llvm::Module, - pub llcx: &'ll llvm::Context, pub codegen_unit: &'tcx CodegenUnit<'tcx>, /// Cache instances of monomorphic and polymorphic items @@ -553,10 +578,9 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { CodegenCx { tcx, + scx: SimpleCx { llcx, llmod }, use_dll_storage_attrs, tls_model, - llmod, - llcx, codegen_unit, instances: Default::default(), vtables: Default::default(), @@ -600,6 +624,11 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { llvm::set_section(g, c"llvm.metadata"); } } +} +impl<'ll> SimpleCx<'ll> { + pub(crate) fn val_ty(&self, v: &'ll Value) -> &'ll Type { + common::val_ty(v) + } pub(crate) fn get_metadata_value(&self, metadata: &'ll Metadata) -> &'ll Value { unsafe { llvm::LLVMMetadataAsValue(self.llcx, metadata) } @@ -625,6 +654,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { llvm::LLVMMDStringInContext2(self.llcx, name.as_ptr() as *const c_char, name.len()) }) } + + pub(crate) fn type_kind(&self, ty: &'ll Type) -> TypeKind { + unsafe { llvm::LLVMRustGetTypeKind(ty).to_generic() } + } } impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { @@ -1178,6 +1211,20 @@ impl CodegenCx<'_, '_> { } } +// This is a duplication of the set_metadata function above. However, so far it's the only one +// shared between both contexts, so it doesn't seem worth it to make the Cx generic like we did it +// for the Builder. +impl SimpleCx<'_> { + #[allow(unused)] + /// A wrapper for [`llvm::LLVMSetMetadata`], but it takes `Metadata` as a parameter instead of `Value`. + pub(crate) fn set_metadata<'a>(&self, val: &'a Value, kind_id: MetadataType, md: &'a Metadata) { + unsafe { + let node = llvm::LLVMMetadataAsValue(&self.llcx, md); + llvm::LLVMSetMetadata(val, kind_id as c_uint, node); + } + } +} + impl HasDataLayout for CodegenCx<'_, '_> { #[inline] fn data_layout(&self) -> &TargetDataLayout { diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs index 5428d776f41e..460a46646157 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs @@ -51,7 +51,7 @@ pub(crate) fn prepare_covfun_record<'tcx>( is_used: bool, ) -> Option> { let fn_cov_info = tcx.instance_mir(instance.def).function_coverage_info.as_deref()?; - let ids_info = tcx.coverage_ids_info(instance.def); + let ids_info = tcx.coverage_ids_info(instance.def)?; let expressions = prepare_expressions(fn_cov_info, ids_info, is_used); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 7311cd9d230c..021108cd51ca 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -8,7 +8,6 @@ use rustc_codegen_ssa::traits::{ use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; -use rustc_middle::ty::layout::HasTyCtxt; use tracing::{debug, instrument}; use crate::builder::Builder; @@ -147,6 +146,10 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { debug!("function has a coverage statement but no coverage info"); return; }; + let Some(ids_info) = bx.tcx.coverage_ids_info(instance.def) else { + debug!("function has a coverage statement but no IDs info"); + return; + }; // Mark the instance as used in this CGU, for coverage purposes. // This includes functions that were not partitioned into this CGU, @@ -162,8 +165,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { // be smaller than the number originally inserted by the instrumentor, // if some high-numbered counters were removed by MIR optimizations. // If so, LLVM's profiler runtime will use fewer physical counters. - let num_counters = - bx.tcx().coverage_ids_info(instance.def).num_counters_after_mir_opts(); + let num_counters = ids_info.num_counters_after_mir_opts(); assert!( num_counters as usize <= function_coverage_info.num_counters, "num_counters disagreement: query says {num_counters} but function info only has {}", diff --git a/compiler/rustc_codegen_llvm/src/declare.rs b/compiler/rustc_codegen_llvm/src/declare.rs index c72b5b5611f2..bdc83267cca9 100644 --- a/compiler/rustc_codegen_llvm/src/declare.rs +++ b/compiler/rustc_codegen_llvm/src/declare.rs @@ -21,26 +21,26 @@ use tracing::debug; use crate::abi::{FnAbi, FnAbiLlvmExt}; use crate::common::AsCCharPtr; -use crate::context::CodegenCx; +use crate::context::{CodegenCx, SimpleCx}; use crate::llvm::AttributePlace::Function; use crate::llvm::Visibility; use crate::type_::Type; use crate::value::Value; use crate::{attributes, llvm}; -/// Declare a function. +/// Declare a function with a SimpleCx. /// /// If there’s a value with the same name already declared, the function will /// update the declaration and return existing Value instead. -pub(crate) fn declare_raw_fn<'ll>( - cx: &CodegenCx<'ll, '_>, +pub(crate) fn declare_simple_fn<'ll>( + cx: &SimpleCx<'ll>, name: &str, callconv: llvm::CallConv, unnamed: llvm::UnnamedAddr, visibility: llvm::Visibility, ty: &'ll Type, ) -> &'ll Value { - debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); + debug!("declare_simple_fn(name={:?}, ty={:?})", name, ty); let llfn = unsafe { llvm::LLVMRustGetOrInsertFunction(cx.llmod, name.as_c_char_ptr(), name.len(), ty) }; @@ -49,6 +49,24 @@ pub(crate) fn declare_raw_fn<'ll>( llvm::SetUnnamedAddress(llfn, unnamed); llvm::set_visibility(llfn, visibility); + llfn +} + +/// Declare a function. +/// +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing Value instead. +pub(crate) fn declare_raw_fn<'ll, 'tcx>( + cx: &CodegenCx<'ll, 'tcx>, + name: &str, + callconv: llvm::CallConv, + unnamed: llvm::UnnamedAddr, + visibility: llvm::Visibility, + ty: &'ll Type, +) -> &'ll Value { + debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); + let llfn = declare_simple_fn(cx, name, callconv, unnamed, visibility, ty); + let mut attrs = SmallVec::<[_; 4]>::new(); if cx.tcx.sess.opts.cg.no_redzone.unwrap_or(cx.tcx.sess.target.disable_redzone) { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index cabcfc9b42b4..91b44b084b09 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -1081,11 +1081,11 @@ fn codegen_emcc_try<'ll>( // Helper function to give a Block to a closure to codegen a shim function. // This is currently primarily used for the `try` intrinsic functions above. -fn gen_fn<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, +fn gen_fn<'a, 'll, 'tcx>( + cx: &'a CodegenCx<'ll, 'tcx>, name: &str, rust_fn_sig: ty::PolyFnSig<'tcx>, - codegen: &mut dyn FnMut(Builder<'_, 'll, 'tcx>), + codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>), ) -> (&'ll Type, &'ll Value) { let fn_abi = cx.fn_abi_of_fn_ptr(rust_fn_sig, ty::List::empty()); let llty = fn_abi.llvm_type(cx); @@ -1104,9 +1104,9 @@ fn gen_fn<'ll, 'tcx>( // catch exceptions. // // This function is only generated once and is then cached. -fn get_rust_try_fn<'ll, 'tcx>( - cx: &CodegenCx<'ll, 'tcx>, - codegen: &mut dyn FnMut(Builder<'_, 'll, 'tcx>), +fn get_rust_try_fn<'a, 'll, 'tcx>( + cx: &'a CodegenCx<'ll, 'tcx>, + codegen: &mut dyn FnMut(Builder<'a, 'll, 'tcx>), ) -> (&'ll Type, &'ll Value) { if let Some(llfn) = cx.rust_try_fn.get() { return llfn; diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 06afe8bb3ad5..4a84fd29e447 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -237,7 +237,6 @@ impl WriteBackendMethods for LlvmCodegenBackend { /// Generate autodiff rules fn autodiff( cgcx: &CodegenContext, - tcx: TyCtxt<'_>, module: &ModuleCodegen, diff_fncs: Vec, config: &ModuleConfig, @@ -246,7 +245,7 @@ impl WriteBackendMethods for LlvmCodegenBackend { let dcx = cgcx.create_dcx(); return Err(dcx.handle().emit_almost_fatal(AutoDiffWithoutLTO)); } - builder::autodiff::differentiate(module, cgcx, tcx, diff_fncs, config) + builder::autodiff::differentiate(module, cgcx, diff_fncs, config) } } diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs index 6aec078e0de3..c56ad886120f 100644 --- a/compiler/rustc_codegen_llvm/src/type_.rs +++ b/compiler/rustc_codegen_llvm/src/type_.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_target::callconv::{CastTarget, FnAbi, Reg}; use crate::abi::{FnAbiLlvmExt, LlvmType}; -use crate::context::CodegenCx; +use crate::context::{CodegenCx, SimpleCx}; pub(crate) use crate::llvm::Type; use crate::llvm::{Bool, False, Metadata, True}; use crate::type_of::LayoutLlvmExt; @@ -35,7 +35,8 @@ impl fmt::Debug for Type { } } -impl<'ll> CodegenCx<'ll, '_> { +impl<'ll> CodegenCx<'ll, '_> {} +impl<'ll> SimpleCx<'ll> { pub(crate) fn type_named_struct(&self, name: &str) -> &'ll Type { let name = SmallCStr::new(name); unsafe { llvm::LLVMStructCreateNamed(self.llcx, name.as_ptr()) } @@ -44,11 +45,9 @@ impl<'ll> CodegenCx<'ll, '_> { pub(crate) fn set_struct_body(&self, ty: &'ll Type, els: &[&'ll Type], packed: bool) { unsafe { llvm::LLVMStructSetBody(ty, els.as_ptr(), els.len() as c_uint, packed as Bool) } } - pub(crate) fn type_void(&self) -> &'ll Type { unsafe { llvm::LLVMVoidTypeInContext(self.llcx) } } - pub(crate) fn type_token(&self) -> &'ll Type { unsafe { llvm::LLVMTokenTypeInContext(self.llcx) } } @@ -75,7 +74,8 @@ impl<'ll> CodegenCx<'ll, '_> { args } } - +} +impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { pub(crate) fn type_bool(&self) -> &'ll Type { self.type_i8() } @@ -120,7 +120,8 @@ impl<'ll> CodegenCx<'ll, '_> { assert_eq!(size % unit_size, 0); self.type_array(self.type_from_integer(unit), size / unit_size) } - +} +impl<'ll> SimpleCx<'ll> { pub(crate) fn type_variadic_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type { unsafe { llvm::LLVMFunctionType(ret, args.as_ptr(), args.len() as c_uint, True) } } diff --git a/compiler/rustc_codegen_ssa/src/back/lto.rs b/compiler/rustc_codegen_ssa/src/back/lto.rs index efccf7687a1e..9fd984b6419e 100644 --- a/compiler/rustc_codegen_ssa/src/back/lto.rs +++ b/compiler/rustc_codegen_ssa/src/back/lto.rs @@ -4,7 +4,6 @@ use std::sync::Arc; use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_data_structures::memmap::Mmap; use rustc_errors::FatalError; -use rustc_middle::ty::TyCtxt; use super::write::CodegenContext; use crate::ModuleCodegen; @@ -89,13 +88,12 @@ impl LtoModuleCodegen { pub unsafe fn autodiff( self, cgcx: &CodegenContext, - tcx: TyCtxt<'_>, diff_fncs: Vec, config: &ModuleConfig, ) -> Result, FatalError> { match &self { LtoModuleCodegen::Fat(module) => { - B::autodiff(cgcx, tcx, &module, diff_fncs, config)?; + B::autodiff(cgcx, &module, diff_fncs, config)?; } _ => panic!("autodiff called with non-fat LTO module"), } diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 8df270abc817..dc406809874d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -1,10 +1,14 @@ +use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind}; use rustc_attr_parsing::InstructionSetAttr; +use rustc_hir::def_id::DefId; use rustc_middle::mir::mono::{Linkage, MonoItem, MonoItemData, Visibility}; use rustc_middle::mir::{Body, InlineAsmOperand}; -use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf}; -use rustc_middle::ty::{Instance, TyCtxt}; -use rustc_middle::{bug, ty}; +use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, LayoutOf}; +use rustc_middle::ty::{Instance, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug, ty}; use rustc_span::sym; +use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; +use rustc_target::spec::WasmCAbi; use crate::common; use crate::traits::{AsmCodegenMethods, BuilderMethods, GlobalAsmOperandRef, MiscCodegenMethods}; @@ -32,7 +36,8 @@ pub(crate) fn codegen_naked_asm<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let item_data = cx.codegen_unit().items().get(&MonoItem::Fn(instance)).unwrap(); let name = cx.mangled_name(instance); - let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data); + let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty()); + let (begin, end) = prefix_and_suffix(cx.tcx(), instance, &name, item_data, fn_abi); let mut template_vec = Vec::new(); template_vec.push(rustc_ast::ast::InlineAsmTemplatePiece::String(begin.into())); @@ -103,6 +108,7 @@ enum AsmBinaryFormat { Elf, Macho, Coff, + Wasm, } impl AsmBinaryFormat { @@ -111,6 +117,8 @@ impl AsmBinaryFormat { Self::Coff } else if target.is_like_osx { Self::Macho + } else if target.is_like_wasm { + Self::Wasm } else { Self::Elf } @@ -122,6 +130,7 @@ fn prefix_and_suffix<'tcx>( instance: Instance<'tcx>, asm_name: &str, item_data: &MonoItemData, + fn_abi: &FnAbi<'tcx, Ty<'tcx>>, ) -> (String, String) { use std::fmt::Write; @@ -169,7 +178,7 @@ fn prefix_and_suffix<'tcx>( } Linkage::LinkOnceAny | Linkage::LinkOnceODR | Linkage::WeakAny | Linkage::WeakODR => { match asm_binary_format { - AsmBinaryFormat::Elf | AsmBinaryFormat::Coff => { + AsmBinaryFormat::Elf | AsmBinaryFormat::Coff | AsmBinaryFormat::Wasm => { writeln!(w, ".weak {asm_name}")?; } AsmBinaryFormat::Macho => { @@ -264,7 +273,161 @@ fn prefix_and_suffix<'tcx>( writeln!(end, "{}", arch_suffix).unwrap(); } } + AsmBinaryFormat::Wasm => { + let section = link_section.unwrap_or(format!(".text.{asm_name}")); + + writeln!(begin, ".section {section},\"\",@").unwrap(); + // wasm functions cannot be aligned, so skip + write_linkage(&mut begin).unwrap(); + if let Visibility::Hidden = item_data.visibility { + writeln!(begin, ".hidden {asm_name}").unwrap(); + } + writeln!(begin, ".type {asm_name}, @function").unwrap(); + if !arch_prefix.is_empty() { + writeln!(begin, "{}", arch_prefix).unwrap(); + } + writeln!(begin, "{asm_name}:").unwrap(); + writeln!( + begin, + ".functype {asm_name} {}", + wasm_functype(tcx, fn_abi, instance.def_id()) + ) + .unwrap(); + + writeln!(end).unwrap(); + // .size is ignored for function symbols, so we can skip it + writeln!(end, "end_function").unwrap(); + } } (begin, end) } + +/// The webassembly type signature for the given function. +/// +/// Used by the `.functype` directive on wasm targets. +fn wasm_functype<'tcx>(tcx: TyCtxt<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, def_id: DefId) -> String { + let mut signature = String::with_capacity(64); + + let ptr_type = match tcx.data_layout.pointer_size.bits() { + 32 => "i32", + 64 => "i64", + other => bug!("wasm pointer size cannot be {other} bits"), + }; + + // FIXME: remove this once the wasm32-unknown-unknown ABI is fixed + // please also add `wasm32-unknown-unknown` back in `tests/assembly/wasm32-naked-fn.rs` + // basically the commit introducing this comment should be reverted + if let PassMode::Pair { .. } = fn_abi.ret.mode { + let _ = WasmCAbi::Legacy; + span_bug!( + tcx.def_span(def_id), + "cannot return a pair (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666" + ); + } + + let hidden_return = matches!(fn_abi.ret.mode, PassMode::Indirect { .. }); + + signature.push('('); + + if hidden_return { + signature.push_str(ptr_type); + if !fn_abi.args.is_empty() { + signature.push_str(", "); + } + } + + let mut it = fn_abi.args.iter().peekable(); + while let Some(arg_abi) = it.next() { + wasm_type(tcx, &mut signature, arg_abi, ptr_type, def_id); + if it.peek().is_some() { + signature.push_str(", "); + } + } + + signature.push_str(") -> ("); + + if !hidden_return { + wasm_type(tcx, &mut signature, &fn_abi.ret, ptr_type, def_id); + } + + signature.push(')'); + + signature +} + +fn wasm_type<'tcx>( + tcx: TyCtxt<'tcx>, + signature: &mut String, + arg_abi: &ArgAbi<'_, Ty<'tcx>>, + ptr_type: &'static str, + def_id: DefId, +) { + match arg_abi.mode { + PassMode::Ignore => { /* do nothing */ } + PassMode::Direct(_) => { + let direct_type = match arg_abi.layout.backend_repr { + BackendRepr::Scalar(scalar) => wasm_primitive(scalar.primitive(), ptr_type), + BackendRepr::Vector { .. } => "v128", + BackendRepr::Memory { .. } => { + // FIXME: remove this branch once the wasm32-unknown-unknown ABI is fixed + let _ = WasmCAbi::Legacy; + span_bug!( + tcx.def_span(def_id), + "cannot use memory args (the wasm32-unknown-unknown ABI is broken, see https://github.com/rust-lang/rust/issues/115666" + ); + } + other => unreachable!("unexpected BackendRepr: {:?}", other), + }; + + signature.push_str(direct_type); + } + PassMode::Pair(_, _) => match arg_abi.layout.backend_repr { + BackendRepr::ScalarPair(a, b) => { + signature.push_str(wasm_primitive(a.primitive(), ptr_type)); + signature.push_str(", "); + signature.push_str(wasm_primitive(b.primitive(), ptr_type)); + } + other => unreachable!("{other:?}"), + }, + PassMode::Cast { pad_i32, ref cast } => { + // For wasm, Cast is used for single-field primitive wrappers like `struct Wrapper(i64);` + assert!(!pad_i32, "not currently used by wasm calling convention"); + assert!(cast.prefix[0].is_none(), "no prefix"); + assert_eq!(cast.rest.total, arg_abi.layout.size, "single item"); + + let wrapped_wasm_type = match cast.rest.unit.kind { + RegKind::Integer => match cast.rest.unit.size.bytes() { + ..=4 => "i32", + ..=8 => "i64", + _ => ptr_type, + }, + RegKind::Float => match cast.rest.unit.size.bytes() { + ..=4 => "f32", + ..=8 => "f64", + _ => ptr_type, + }, + RegKind::Vector => "v128", + }; + + signature.push_str(wrapped_wasm_type); + } + PassMode::Indirect { .. } => signature.push_str(ptr_type), + } +} + +fn wasm_primitive(primitive: Primitive, ptr_type: &'static str) -> &'static str { + match primitive { + Primitive::Int(integer, _) => match integer { + Integer::I8 | Integer::I16 | Integer::I32 => "i32", + Integer::I64 => "i64", + Integer::I128 => "i64, i64", + }, + Primitive::Float(float) => match float { + Float::F16 | Float::F32 => "f32", + Float::F64 => "f64", + Float::F128 => "i64, i64", + }, + Primitive::Pointer(_) => ptr_type, + } +} diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 51e2255efe14..97fe614aa10c 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -1,7 +1,6 @@ use rustc_ast::expand::autodiff_attrs::AutoDiffItem; use rustc_errors::{DiagCtxtHandle, FatalError}; use rustc_middle::dep_graph::WorkProduct; -use rustc_middle::ty::TyCtxt; use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule}; use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig}; @@ -65,7 +64,6 @@ pub trait WriteBackendMethods: 'static + Sized + Clone { fn serialize_module(module: ModuleCodegen) -> (String, Self::ModuleBuffer); fn autodiff( cgcx: &CodegenContext, - tcx: TyCtxt<'_>, module: &ModuleCodegen, diff_fncs: Vec, config: &ModuleConfig, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 5339feb5d273..5075ed86a6aa 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -15,6 +15,7 @@ pub use rustc_ast::{ }; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sorted_map::SortedMap; +use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::LocalDefId; @@ -30,7 +31,7 @@ use crate::LangItem; use crate::def::{CtorKind, DefKind, Res}; use crate::def_id::{DefId, LocalDefIdMap}; pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId}; -use crate::intravisit::FnKind; +use crate::intravisit::{FnKind, VisitorExt}; #[derive(Debug, Copy, Clone, HashStable_Generic)] pub struct Lifetime { @@ -263,14 +264,58 @@ impl<'hir> PathSegment<'hir> { /// So, `ConstArg` (specifically, [`ConstArgKind`]) distinguishes between const args /// that are [just paths](ConstArgKind::Path) (currently just bare const params) /// versus const args that are literals or have arbitrary computations (e.g., `{ 1 + 3 }`). +/// +/// The `Unambig` generic parameter represents whether the position this const is from is +/// unambiguously a const or ambiguous as to whether it is a type or a const. When in an +/// ambiguous context the parameter is instantiated with an uninhabited type making the +/// [`ConstArgKind::Infer`] variant unusable and [`GenericArg::Infer`] is used instead. #[derive(Clone, Copy, Debug, HashStable_Generic)] -pub struct ConstArg<'hir> { +#[repr(C)] +pub struct ConstArg<'hir, Unambig = ()> { #[stable_hasher(ignore)] pub hir_id: HirId, - pub kind: ConstArgKind<'hir>, + pub kind: ConstArgKind<'hir, Unambig>, +} + +impl<'hir> ConstArg<'hir, AmbigArg> { + /// Converts a `ConstArg` in an ambiguous position to one in an unambiguous position. + /// + /// Functions accepting an unambiguous consts may expect the [`ConstArgKind::Infer`] variant + /// to be used. Care should be taken to separately handle infer consts when calling this + /// function as it cannot be handled by downstream code making use of the returned const. + /// + /// In practice this may mean overriding the [`Visitor::visit_infer`][visit_infer] method on hir visitors, or + /// specifically matching on [`GenericArg::Infer`] when handling generic arguments. + /// + /// [visit_infer]: [rustc_hir::intravisit::Visitor::visit_infer] + pub fn as_unambig_ct(&self) -> &ConstArg<'hir> { + // SAFETY: `ConstArg` is `repr(C)` and `ConstArgKind` is marked `repr(u8)` so that the + // layout is the same across different ZST type arguments. + let ptr = self as *const ConstArg<'hir, AmbigArg> as *const ConstArg<'hir, ()>; + unsafe { &*ptr } + } } impl<'hir> ConstArg<'hir> { + /// Converts a `ConstArg` in an unambigous position to one in an ambiguous position. This is + /// fallible as the [`ConstArgKind::Infer`] variant is not present in ambiguous positions. + /// + /// Functions accepting ambiguous consts will not handle the [`ConstArgKind::Infer`] variant, if + /// infer consts are relevant to you then care should be taken to handle them separately. + pub fn try_as_ambig_ct(&self) -> Option<&ConstArg<'hir, AmbigArg>> { + if let ConstArgKind::Infer(_, ()) = self.kind { + return None; + } + + // SAFETY: `ConstArg` is `repr(C)` and `ConstArgKind` is marked `repr(u8)` so that the layout is + // the same across different ZST type arguments. We also asserted that the `self` is + // not a `ConstArgKind::Infer` so there is no risk of transmuting a `()` to `AmbigArg`. + let ptr = self as *const ConstArg<'hir> as *const ConstArg<'hir, AmbigArg>; + Some(unsafe { &*ptr }) + } +} + +impl<'hir, Unambig> ConstArg<'hir, Unambig> { pub fn anon_const_hir_id(&self) -> Option { match self.kind { ConstArgKind::Anon(ac) => Some(ac.hir_id), @@ -282,14 +327,15 @@ impl<'hir> ConstArg<'hir> { match self.kind { ConstArgKind::Path(path) => path.span(), ConstArgKind::Anon(anon) => anon.span, - ConstArgKind::Infer(span) => span, + ConstArgKind::Infer(span, _) => span, } } } /// See [`ConstArg`]. #[derive(Clone, Copy, Debug, HashStable_Generic)] -pub enum ConstArgKind<'hir> { +#[repr(u8, C)] +pub enum ConstArgKind<'hir, Unambig = ()> { /// **Note:** Currently this is only used for bare const params /// (`N` where `fn foo(...)`), /// not paths to any const (`N` where `const N: usize = ...`). @@ -297,11 +343,9 @@ pub enum ConstArgKind<'hir> { /// However, in the future, we'll be using it for all of those. Path(QPath<'hir>), Anon(&'hir AnonConst), - /// **Note:** Not all inferred consts are represented as - /// `ConstArgKind::Infer`. In cases where it is ambiguous whether - /// a generic arg is a type or a const, inference variables are - /// represented as `GenericArg::Infer` instead. - Infer(Span), + /// This variant is not always used to represent inference consts, sometimes + /// [`GenericArg::Infer`] is used instead. + Infer(Span, Unambig), } #[derive(Clone, Copy, Debug, HashStable_Generic)] @@ -313,19 +357,24 @@ pub struct InferArg { impl InferArg { pub fn to_ty(&self) -> Ty<'static> { - Ty { kind: TyKind::Infer, span: self.span, hir_id: self.hir_id } + Ty { kind: TyKind::Infer(()), span: self.span, hir_id: self.hir_id } } } #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum GenericArg<'hir> { Lifetime(&'hir Lifetime), - Type(&'hir Ty<'hir>), - Const(&'hir ConstArg<'hir>), - /// **Note:** Inference variables are only represented as - /// `GenericArg::Infer` in cases where it is ambiguous whether - /// a generic arg is a type or a const. Otherwise, inference variables - /// are represented as `TyKind::Infer` or `ConstArgKind::Infer`. + Type(&'hir Ty<'hir, AmbigArg>), + Const(&'hir ConstArg<'hir, AmbigArg>), + /// Inference variables in [`GenericArg`] are always represnted by + /// `GenericArg::Infer` instead of the `Infer` variants on [`TyKind`] and + /// [`ConstArgKind`] as it is not clear until hir ty lowering whether a + /// `_` argument is a type or const argument. + /// + /// However, some builtin types' generic arguments are represented by [`TyKind`] + /// without a [`GenericArg`], instead directly storing a [`Ty`] or [`ConstArg`]. In + /// such cases they *are* represented by the `Infer` variants on [`TyKind`] and + /// [`ConstArgKind`] as it is not ambiguous whether the argument is a type or const. Infer(InferArg), } @@ -353,7 +402,7 @@ impl GenericArg<'_> { GenericArg::Lifetime(_) => "lifetime", GenericArg::Type(_) => "type", GenericArg::Const(_) => "constant", - GenericArg::Infer(_) => "inferred", + GenericArg::Infer(_) => "placeholder", } } @@ -764,11 +813,8 @@ impl<'hir> Generics<'hir> { && let [.., segment] = trait_ref.path.segments && let Some(ret_ty) = segment.args().paren_sugar_output() && let ret_ty = ret_ty.peel_refs() - && let TyKind::TraitObject( - _, - _, - TraitObjectSyntax::Dyn | TraitObjectSyntax::DynStar, - ) = ret_ty.kind + && let TyKind::TraitObject(_, tagged_ptr) = ret_ty.kind + && let TraitObjectSyntax::Dyn | TraitObjectSyntax::DynStar = tagged_ptr.tag() && ret_ty.span.can_be_used_for_suggestions() { Some(ret_ty.span) @@ -2917,15 +2963,84 @@ impl<'hir> AssocItemConstraintKind<'hir> { } } +/// An uninhabited enum used to make `Infer` variants on [`Ty`] and [`ConstArg`] be +/// unreachable. Zero-Variant enums are guaranteed to have the same layout as the never +/// type. #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub struct Ty<'hir> { +pub enum AmbigArg {} + +#[derive(Debug, Clone, Copy, HashStable_Generic)] +#[repr(C)] +/// Represents a type in the `HIR`. +/// +/// The `Unambig` generic parameter represents whether the position this type is from is +/// unambiguously a type or ambiguous as to whether it is a type or a const. When in an +/// ambiguous context the parameter is instantiated with an uninhabited type making the +/// [`TyKind::Infer`] variant unusable and [`GenericArg::Infer`] is used instead. +pub struct Ty<'hir, Unambig = ()> { #[stable_hasher(ignore)] pub hir_id: HirId, - pub kind: TyKind<'hir>, pub span: Span, + pub kind: TyKind<'hir, Unambig>, +} + +impl<'hir> Ty<'hir, AmbigArg> { + /// Converts a `Ty` in an ambiguous position to one in an unambiguous position. + /// + /// Functions accepting an unambiguous types may expect the [`TyKind::Infer`] variant + /// to be used. Care should be taken to separately handle infer types when calling this + /// function as it cannot be handled by downstream code making use of the returned ty. + /// + /// In practice this may mean overriding the [`Visitor::visit_infer`][visit_infer] method on hir visitors, or + /// specifically matching on [`GenericArg::Infer`] when handling generic arguments. + /// + /// [visit_infer]: [rustc_hir::intravisit::Visitor::visit_infer] + pub fn as_unambig_ty(&self) -> &Ty<'hir> { + // SAFETY: `Ty` is `repr(C)` and `TyKind` is marked `repr(u8)` so that the layout is + // the same across different ZST type arguments. + let ptr = self as *const Ty<'hir, AmbigArg> as *const Ty<'hir, ()>; + unsafe { &*ptr } + } } impl<'hir> Ty<'hir> { + /// Converts a `Ty` in an unambigous position to one in an ambiguous position. This is + /// fallible as the [`TyKind::Infer`] variant is not present in ambiguous positions. + /// + /// Functions accepting ambiguous types will not handle the [`TyKind::Infer`] variant, if + /// infer types are relevant to you then care should be taken to handle them separately. + pub fn try_as_ambig_ty(&self) -> Option<&Ty<'hir, AmbigArg>> { + if let TyKind::Infer(()) = self.kind { + return None; + } + + // SAFETY: `Ty` is `repr(C)` and `TyKind` is marked `repr(u8)` so that the layout is + // the same across different ZST type arguments. We also asserted that the `self` is + // not a `TyKind::Infer` so there is no risk of transmuting a `()` to `AmbigArg`. + let ptr = self as *const Ty<'hir> as *const Ty<'hir, AmbigArg>; + Some(unsafe { &*ptr }) + } +} + +impl<'hir> Ty<'hir, AmbigArg> { + pub fn peel_refs(&self) -> &Ty<'hir> { + let mut final_ty = self.as_unambig_ty(); + while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind { + final_ty = ty; + } + final_ty + } +} + +impl<'hir> Ty<'hir> { + pub fn peel_refs(&self) -> &Self { + let mut final_ty = self; + while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind { + final_ty = ty; + } + final_ty + } + /// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate. pub fn as_generic_param(&self) -> Option<(DefId, Ident)> { let TyKind::Path(QPath::Resolved(None, path)) = self.kind else { @@ -2942,19 +3057,11 @@ impl<'hir> Ty<'hir> { } } - pub fn peel_refs(&self) -> &Self { - let mut final_ty = self; - while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind { - final_ty = ty; - } - final_ty - } - pub fn find_self_aliases(&self) -> Vec { use crate::intravisit::Visitor; struct MyVisitor(Vec); impl<'v> Visitor<'v> for MyVisitor { - fn visit_ty(&mut self, t: &'v Ty<'v>) { + fn visit_ty(&mut self, t: &'v Ty<'v, AmbigArg>) { if matches!( &t.kind, TyKind::Path(QPath::Resolved(_, Path { @@ -2970,7 +3077,7 @@ impl<'hir> Ty<'hir> { } let mut my_visitor = MyVisitor(vec![]); - my_visitor.visit_ty(self); + my_visitor.visit_ty_unambig(self); my_visitor.0 } @@ -2979,14 +3086,14 @@ impl<'hir> Ty<'hir> { pub fn is_suggestable_infer_ty(&self) -> bool { fn are_suggestable_generic_args(generic_args: &[GenericArg<'_>]) -> bool { generic_args.iter().any(|arg| match arg { - GenericArg::Type(ty) => ty.is_suggestable_infer_ty(), + GenericArg::Type(ty) => ty.as_unambig_ty().is_suggestable_infer_ty(), GenericArg::Infer(_) => true, _ => false, }) } debug!(?self); match &self.kind { - TyKind::Infer => true, + TyKind::Infer(()) => true, TyKind::Slice(ty) => ty.is_suggestable_infer_ty(), TyKind::Array(ty, length) => { ty.is_suggestable_infer_ty() || matches!(length.kind, ConstArgKind::Infer(..)) @@ -3200,7 +3307,9 @@ pub enum InferDelegationKind { /// The various kinds of types recognized by the compiler. #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub enum TyKind<'hir> { +// SAFETY: `repr(u8)` is required so that `TyKind<()>` and `TyKind` are layout compatible +#[repr(u8, C)] +pub enum TyKind<'hir, Unambig = ()> { /// Actual type should be inherited from `DefId` signature InferDelegation(DefId, InferDelegationKind), /// A variable length slice (i.e., `[T]`). @@ -3230,21 +3339,22 @@ pub enum TyKind<'hir> { TraitAscription(GenericBounds<'hir>), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. - TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax), + /// + /// We use pointer tagging to represent a `&'hir Lifetime` and `TraitObjectSyntax` pair + /// as otherwise this type being `repr(C)` would result in `TyKind` increasing in size. + TraitObject(&'hir [PolyTraitRef<'hir>], TaggedRef<'hir, Lifetime, TraitObjectSyntax>), /// Unused for now. Typeof(&'hir AnonConst), - /// `TyKind::Infer` means the type should be inferred instead of it having been - /// specified. This can appear anywhere in a type. - /// - /// **Note:** Not all inferred types are represented as - /// `TyKind::Infer`. In cases where it is ambiguous whether - /// a generic arg is a type or a const, inference variables are - /// represented as `GenericArg::Infer` instead. - Infer, /// Placeholder for a type that has failed to be defined. Err(rustc_span::ErrorGuaranteed), /// Pattern types (`pattern_type!(u32 is 1..)`) Pat(&'hir Ty<'hir>, &'hir Pat<'hir>), + /// `TyKind::Infer` means the type should be inferred instead of it having been + /// specified. This can appear anywhere in a type. + /// + /// This variant is not always used to represent inference types, sometimes + /// [`GenericArg::Infer`] is used instead. + Infer(Unambig), } #[derive(Debug, Clone, Copy, HashStable_Generic)] @@ -4537,3 +4647,6 @@ fn debug_fn(f: impl Fn(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Deb } DebugFn(f) } + +#[cfg(test)] +mod tests; diff --git a/compiler/rustc_hir/src/hir/tests.rs b/compiler/rustc_hir/src/hir/tests.rs new file mode 100644 index 000000000000..300d44355303 --- /dev/null +++ b/compiler/rustc_hir/src/hir/tests.rs @@ -0,0 +1,83 @@ +use rustc_span::def_id::DefIndex; + +use super::*; + +macro_rules! define_tests { + ($($name:ident $kind:ident $variant:ident {$($init:tt)*})*) => {$( + #[test] + fn $name() { + let unambig = $kind::$variant::<'_, ()> { $($init)* }; + let unambig_to_ambig = unsafe { std::mem::transmute::<_, $kind<'_, AmbigArg>>(unambig) }; + + assert!(matches!(&unambig_to_ambig, $kind::$variant { $($init)* })); + + let ambig_to_unambig = unsafe { std::mem::transmute::<_, $kind<'_, ()>>(unambig_to_ambig) }; + + assert!(matches!(&ambig_to_unambig, $kind::$variant { $($init)* })); + } + )*}; +} + +define_tests! { + cast_never TyKind Never {} + cast_tup TyKind Tup { 0: &[Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }] } + cast_ptr TyKind Ptr { 0: MutTy { ty: &Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }, mutbl: Mutability::Not }} + cast_array TyKind Array { + 0: &Ty { span: DUMMY_SP, hir_id: HirId::INVALID, kind: TyKind::Never }, + 1: &ConstArg { hir_id: HirId::INVALID, kind: ConstArgKind::Anon(&AnonConst { + hir_id: HirId::INVALID, + def_id: LocalDefId { local_def_index: DefIndex::ZERO }, + body: BodyId { hir_id: HirId::INVALID }, + span: DUMMY_SP, + })} + } + + cast_anon ConstArgKind Anon { + 0: &AnonConst { + hir_id: HirId::INVALID, + def_id: LocalDefId { local_def_index: DefIndex::ZERO }, + body: BodyId { hir_id: HirId::INVALID }, + span: DUMMY_SP, + } + } +} + +#[test] +fn trait_object_roundtrips() { + trait_object_roundtrips_impl(TraitObjectSyntax::Dyn); + trait_object_roundtrips_impl(TraitObjectSyntax::DynStar); + trait_object_roundtrips_impl(TraitObjectSyntax::None); +} + +fn trait_object_roundtrips_impl(syntax: TraitObjectSyntax) { + let unambig = TyKind::TraitObject::<'_, ()>( + &[], + TaggedRef::new( + &const { + Lifetime { + hir_id: HirId::INVALID, + ident: Ident::new(sym::name, DUMMY_SP), + res: LifetimeName::Static, + } + }, + syntax, + ), + ); + let unambig_to_ambig = unsafe { std::mem::transmute::<_, TyKind<'_, AmbigArg>>(unambig) }; + + match unambig_to_ambig { + TyKind::TraitObject(_, tagged_ref) => { + assert!(tagged_ref.tag() == syntax) + } + _ => panic!("`TyKind::TraitObject` did not roundtrip"), + }; + + let ambig_to_unambig = unsafe { std::mem::transmute::<_, TyKind<'_, ()>>(unambig_to_ambig) }; + + match ambig_to_unambig { + TyKind::TraitObject(_, tagged_ref) => { + assert!(tagged_ref.tag() == syntax) + } + _ => panic!("`TyKind::TraitObject` did not roundtrip"), + }; +} diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index ef863aca0905..b0d80d0f8092 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -351,18 +351,48 @@ pub trait Visitor<'v>: Sized { fn visit_inline_const(&mut self, c: &'v ConstBlock) -> Self::Result { walk_inline_const(self, c) } - fn visit_const_arg(&mut self, c: &'v ConstArg<'v>) -> Self::Result { - walk_const_arg(self, c) + + fn visit_generic_arg(&mut self, generic_arg: &'v GenericArg<'v>) -> Self::Result { + walk_generic_arg(self, generic_arg) } + + /// All types are treated as ambiguous types for the purposes of hir visiting in + /// order to ensure that visitors can handle infer vars without it being too error-prone. + /// + /// See the doc comments on [`Ty`] for an explanation of what it means for a type to be + /// ambiguous. + /// + /// The [`Visitor::visit_infer`] method should be overriden in order to handle infer vars. + fn visit_ty(&mut self, t: &'v Ty<'v, AmbigArg>) -> Self::Result { + walk_ty(self, t) + } + + /// All consts are treated as ambiguous consts for the purposes of hir visiting in + /// order to ensure that visitors can handle infer vars without it being too error-prone. + /// + /// See the doc comments on [`ConstArg`] for an explanation of what it means for a const to be + /// ambiguous. + /// + /// The [`Visitor::visit_infer`] method should be overriden in order to handle infer vars. + fn visit_const_arg(&mut self, c: &'v ConstArg<'v, AmbigArg>) -> Self::Result { + walk_ambig_const_arg(self, c) + } + + #[allow(unused_variables)] + fn visit_infer(&mut self, inf_id: HirId, inf_span: Span, kind: InferKind<'v>) -> Self::Result { + self.visit_id(inf_id) + } + + fn visit_lifetime(&mut self, lifetime: &'v Lifetime) -> Self::Result { + walk_lifetime(self, lifetime) + } + fn visit_expr(&mut self, ex: &'v Expr<'v>) -> Self::Result { walk_expr(self, ex) } fn visit_expr_field(&mut self, field: &'v ExprField<'v>) -> Self::Result { walk_expr_field(self, field) } - fn visit_ty(&mut self, t: &'v Ty<'v>) -> Self::Result { - walk_ty(self, t) - } fn visit_pattern_type_pattern(&mut self, _p: &'v Pat<'v>) { // Do nothing. Only a few visitors need to know the details of the pattern type, // and they opt into it. All other visitors will just choke on our fake patterns @@ -444,15 +474,6 @@ pub trait Visitor<'v>: Sized { fn visit_label(&mut self, label: &'v Label) -> Self::Result { walk_label(self, label) } - fn visit_infer(&mut self, inf: &'v InferArg) -> Self::Result { - walk_inf(self, inf) - } - fn visit_generic_arg(&mut self, generic_arg: &'v GenericArg<'v>) -> Self::Result { - walk_generic_arg(self, generic_arg) - } - fn visit_lifetime(&mut self, lifetime: &'v Lifetime) -> Self::Result { - walk_lifetime(self, lifetime) - } // The span is that of the surrounding type/pattern/expr/whatever. fn visit_qpath(&mut self, qpath: &'v QPath<'v>, id: HirId, _span: Span) -> Self::Result { walk_qpath(self, qpath, id) @@ -486,6 +507,26 @@ pub trait Visitor<'v>: Sized { } } +pub trait VisitorExt<'v>: Visitor<'v> { + /// Extension trait method to visit types in unambiguous positions, this is not + /// directly on the [`Visitor`] trait as this method should never be overridden. + /// + /// Named `visit_ty_unambig` instead of `visit_unambig_ty` to aid in discovery + /// by IDes when `v.visit_ty` is written. + fn visit_ty_unambig(&mut self, t: &'v Ty<'v>) -> Self::Result { + walk_unambig_ty(self, t) + } + /// Extension trait method to visit consts in unambiguous positions, this is not + /// directly on the [`Visitor`] trait as this method should never be overridden. + /// + /// Named `visit_const_arg_unambig` instead of `visit_unambig_const_arg` to aid in + /// discovery by IDes when `v.visit_const_arg` is written. + fn visit_const_arg_unambig(&mut self, c: &'v ConstArg<'v>) -> Self::Result { + walk_const_arg(self, c) + } +} +impl<'v, V: Visitor<'v>> VisitorExt<'v> for V {} + pub fn walk_param<'v, V: Visitor<'v>>(visitor: &mut V, param: &'v Param<'v>) -> V::Result { try_visit!(visitor.visit_id(param.hir_id)); visitor.visit_pat(param.pat) @@ -503,12 +544,12 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: } ItemKind::Static(ref typ, _, body) => { try_visit!(visitor.visit_id(item.hir_id())); - try_visit!(visitor.visit_ty(typ)); + try_visit!(visitor.visit_ty_unambig(typ)); try_visit!(visitor.visit_nested_body(body)); } ItemKind::Const(ref typ, ref generics, body) => { try_visit!(visitor.visit_id(item.hir_id())); - try_visit!(visitor.visit_ty(typ)); + try_visit!(visitor.visit_ty_unambig(typ)); try_visit!(visitor.visit_generics(generics)); try_visit!(visitor.visit_nested_body(body)); } @@ -539,7 +580,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: } ItemKind::TyAlias(ref ty, ref generics) => { try_visit!(visitor.visit_id(item.hir_id())); - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); try_visit!(visitor.visit_generics(generics)); } ItemKind::Enum(ref enum_definition, ref generics) => { @@ -561,7 +602,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V:: try_visit!(visitor.visit_id(item.hir_id())); try_visit!(visitor.visit_generics(generics)); visit_opt!(visitor, visit_trait_ref, of_trait); - try_visit!(visitor.visit_ty(self_ty)); + try_visit!(visitor.visit_ty_unambig(self_ty)); walk_list!(visitor, visit_impl_item_ref, *items); } ItemKind::Struct(ref struct_definition, ref generics) @@ -618,7 +659,7 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>( walk_list!(visitor, visit_ident, param_names.iter().copied()); } ForeignItemKind::Static(ref typ, _, _) => { - try_visit!(visitor.visit_ty(typ)); + try_visit!(visitor.visit_ty_unambig(typ)); } ForeignItemKind::Type => (), } @@ -632,7 +673,7 @@ pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v LetStmt<'v>) - try_visit!(visitor.visit_id(local.hir_id)); try_visit!(visitor.visit_pat(local.pat)); visit_opt!(visitor, visit_block, local.els); - visit_opt!(visitor, visit_ty, local.ty); + visit_opt!(visitor, visit_ty_unambig, local.ty); V::Result::output() } @@ -735,18 +776,6 @@ pub fn walk_inline_const<'v, V: Visitor<'v>>( visitor.visit_nested_body(constant.body) } -pub fn walk_const_arg<'v, V: Visitor<'v>>( - visitor: &mut V, - const_arg: &'v ConstArg<'v>, -) -> V::Result { - try_visit!(visitor.visit_id(const_arg.hir_id)); - match &const_arg.kind { - ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, const_arg.hir_id, qpath.span()), - ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon), - ConstArgKind::Infer(..) => V::Result::output(), - } -} - pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) -> V::Result { try_visit!(visitor.visit_id(expression.hir_id)); match expression.kind { @@ -758,7 +787,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) } ExprKind::Repeat(ref element, ref count) => { try_visit!(visitor.visit_expr(element)); - try_visit!(visitor.visit_const_arg(count)); + try_visit!(visitor.visit_const_arg_unambig(count)); } ExprKind::Struct(ref qpath, fields, ref optional_base) => { try_visit!(visitor.visit_qpath(qpath, expression.hir_id, expression.span)); @@ -789,7 +818,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) } ExprKind::Cast(ref subexpression, ref typ) | ExprKind::Type(ref subexpression, ref typ) => { try_visit!(visitor.visit_expr(subexpression)); - try_visit!(visitor.visit_ty(typ)); + try_visit!(visitor.visit_ty_unambig(typ)); } ExprKind::DropTemps(ref subexpression) => { try_visit!(visitor.visit_expr(subexpression)); @@ -798,7 +827,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) // match the visit order in walk_local try_visit!(visitor.visit_expr(init)); try_visit!(visitor.visit_pat(pat)); - visit_opt!(visitor, visit_ty, ty); + visit_opt!(visitor, visit_ty_unambig, ty); } ExprKind::If(ref cond, ref then, ref else_opt) => { try_visit!(visitor.visit_expr(cond)); @@ -866,7 +895,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) try_visit!(visitor.visit_inline_asm(asm, expression.hir_id)); } ExprKind::OffsetOf(ref container, ref fields) => { - try_visit!(visitor.visit_ty(container)); + try_visit!(visitor.visit_ty_unambig(container)); walk_list!(visitor, visit_ident, fields.iter().copied()); } ExprKind::Yield(ref subexpression, _) => { @@ -874,7 +903,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) } ExprKind::UnsafeBinderCast(_kind, expr, ty) => { try_visit!(visitor.visit_expr(expr)); - visit_opt!(visitor, visit_ty, ty); + visit_opt!(visitor, visit_ty_unambig, ty); } ExprKind::Lit(_) | ExprKind::Err(_) => {} } @@ -886,20 +915,49 @@ pub fn walk_expr_field<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v ExprField try_visit!(visitor.visit_ident(field.ident)); visitor.visit_expr(field.expr) } +/// We track whether an infer var is from a [`Ty`], [`ConstArg`], or [`GenericArg`] so that +/// HIR visitors overriding [`Visitor::visit_infer`] can determine what kind of infer is being visited +pub enum InferKind<'hir> { + Ty(&'hir Ty<'hir>), + Const(&'hir ConstArg<'hir>), + Ambig(&'hir InferArg), +} -pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Result { +pub fn walk_generic_arg<'v, V: Visitor<'v>>( + visitor: &mut V, + generic_arg: &'v GenericArg<'v>, +) -> V::Result { + match generic_arg { + GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt), + GenericArg::Type(ty) => visitor.visit_ty(ty), + GenericArg::Const(ct) => visitor.visit_const_arg(ct), + GenericArg::Infer(inf) => visitor.visit_infer(inf.hir_id, inf.span, InferKind::Ambig(inf)), + } +} + +pub fn walk_unambig_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Result { + match typ.try_as_ambig_ty() { + Some(ambig_ty) => visitor.visit_ty(ambig_ty), + None => { + try_visit!(visitor.visit_id(typ.hir_id)); + visitor.visit_infer(typ.hir_id, typ.span, InferKind::Ty(typ)) + } + } +} + +pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v, AmbigArg>) -> V::Result { try_visit!(visitor.visit_id(typ.hir_id)); match typ.kind { - TyKind::Slice(ref ty) => try_visit!(visitor.visit_ty(ty)), - TyKind::Ptr(ref mutable_type) => try_visit!(visitor.visit_ty(mutable_type.ty)), + TyKind::Slice(ref ty) => try_visit!(visitor.visit_ty_unambig(ty)), + TyKind::Ptr(ref mutable_type) => try_visit!(visitor.visit_ty_unambig(mutable_type.ty)), TyKind::Ref(ref lifetime, ref mutable_type) => { try_visit!(visitor.visit_lifetime(lifetime)); - try_visit!(visitor.visit_ty(mutable_type.ty)); + try_visit!(visitor.visit_ty_unambig(mutable_type.ty)); } TyKind::Never => {} TyKind::Tup(tuple_element_types) => { - walk_list!(visitor, visit_ty, tuple_element_types); + walk_list!(visitor, visit_ty_unambig, tuple_element_types); } TyKind::BareFn(ref function_declaration) => { walk_list!(visitor, visit_generic_param, function_declaration.generic_params); @@ -907,7 +965,7 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul } TyKind::UnsafeBinder(ref unsafe_binder) => { walk_list!(visitor, visit_generic_param, unsafe_binder.generic_params); - try_visit!(visitor.visit_ty(unsafe_binder.inner_ty)); + try_visit!(visitor.visit_ty_unambig(unsafe_binder.inner_ty)); } TyKind::Path(ref qpath) => { try_visit!(visitor.visit_qpath(qpath, typ.hir_id, typ.span)); @@ -919,25 +977,49 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul walk_list!(visitor, visit_param_bound, bounds); } TyKind::Array(ref ty, ref length) => { - try_visit!(visitor.visit_ty(ty)); - try_visit!(visitor.visit_const_arg(length)); + try_visit!(visitor.visit_ty_unambig(ty)); + try_visit!(visitor.visit_const_arg_unambig(length)); } - TyKind::TraitObject(bounds, ref lifetime, _syntax) => { + TyKind::TraitObject(bounds, ref lifetime) => { for bound in bounds { try_visit!(visitor.visit_poly_trait_ref(bound)); } try_visit!(visitor.visit_lifetime(lifetime)); } TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)), - TyKind::Infer | TyKind::InferDelegation(..) | TyKind::Err(_) => {} + TyKind::InferDelegation(..) | TyKind::Err(_) => {} TyKind::Pat(ty, pat) => { - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); try_visit!(visitor.visit_pattern_type_pattern(pat)); } } V::Result::output() } +pub fn walk_const_arg<'v, V: Visitor<'v>>( + visitor: &mut V, + const_arg: &'v ConstArg<'v>, +) -> V::Result { + match const_arg.try_as_ambig_ct() { + Some(ambig_ct) => visitor.visit_const_arg(ambig_ct), + None => { + try_visit!(visitor.visit_id(const_arg.hir_id)); + visitor.visit_infer(const_arg.hir_id, const_arg.span(), InferKind::Const(const_arg)) + } + } +} + +pub fn walk_ambig_const_arg<'v, V: Visitor<'v>>( + visitor: &mut V, + const_arg: &'v ConstArg<'v, AmbigArg>, +) -> V::Result { + try_visit!(visitor.visit_id(const_arg.hir_id)); + match &const_arg.kind { + ConstArgKind::Path(qpath) => visitor.visit_qpath(qpath, const_arg.hir_id, qpath.span()), + ConstArgKind::Anon(anon) => visitor.visit_anon_const(*anon), + } +} + pub fn walk_generic_param<'v, V: Visitor<'v>>( visitor: &mut V, param: &'v GenericParam<'v>, @@ -949,9 +1031,11 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>( } match param.kind { GenericParamKind::Lifetime { .. } => {} - GenericParamKind::Type { ref default, .. } => visit_opt!(visitor, visit_ty, default), + GenericParamKind::Type { ref default, .. } => { + visit_opt!(visitor, visit_ty_unambig, default) + } GenericParamKind::Const { ref ty, ref default, synthetic: _ } => { - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); if let Some(ref default) = default { try_visit!(visitor.visit_const_param_default(param.hir_id, default)); } @@ -964,7 +1048,7 @@ pub fn walk_const_param_default<'v, V: Visitor<'v>>( visitor: &mut V, ct: &'v ConstArg<'v>, ) -> V::Result { - visitor.visit_const_arg(ct) + visitor.visit_const_arg_unambig(ct) } pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics<'v>) -> V::Result { @@ -986,7 +1070,7 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( bound_generic_params, origin: _, }) => { - try_visit!(visitor.visit_ty(bounded_ty)); + try_visit!(visitor.visit_ty_unambig(bounded_ty)); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_generic_param, bound_generic_params); } @@ -999,8 +1083,8 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( walk_list!(visitor, visit_param_bound, bounds); } WherePredicateKind::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty }) => { - try_visit!(visitor.visit_ty(lhs_ty)); - try_visit!(visitor.visit_ty(rhs_ty)); + try_visit!(visitor.visit_ty_unambig(lhs_ty)); + try_visit!(visitor.visit_ty_unambig(rhs_ty)); } } V::Result::output() @@ -1010,13 +1094,13 @@ pub fn walk_fn_decl<'v, V: Visitor<'v>>( visitor: &mut V, function_declaration: &'v FnDecl<'v>, ) -> V::Result { - walk_list!(visitor, visit_ty, function_declaration.inputs); + walk_list!(visitor, visit_ty_unambig, function_declaration.inputs); visitor.visit_fn_ret_ty(&function_declaration.output) } pub fn walk_fn_ret_ty<'v, V: Visitor<'v>>(visitor: &mut V, ret_ty: &'v FnRetTy<'v>) -> V::Result { if let FnRetTy::Return(output_ty) = *ret_ty { - try_visit!(visitor.visit_ty(output_ty)); + try_visit!(visitor.visit_ty_unambig(output_ty)); } V::Result::output() } @@ -1069,7 +1153,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>( try_visit!(visitor.visit_id(hir_id)); match *kind { TraitItemKind::Const(ref ty, default) => { - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); visit_opt!(visitor, visit_nested_body, default); } TraitItemKind::Fn(ref sig, TraitFn::Required(param_names)) => { @@ -1087,7 +1171,7 @@ pub fn walk_trait_item<'v, V: Visitor<'v>>( } TraitItemKind::Type(bounds, ref default) => { walk_list!(visitor, visit_param_bound, bounds); - visit_opt!(visitor, visit_ty, default); + visit_opt!(visitor, visit_ty_unambig, default); } } V::Result::output() @@ -1125,7 +1209,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>( try_visit!(visitor.visit_id(impl_item.hir_id())); match *kind { ImplItemKind::Const(ref ty, body) => { - try_visit!(visitor.visit_ty(ty)); + try_visit!(visitor.visit_ty_unambig(ty)); visitor.visit_nested_body(body) } ImplItemKind::Fn(ref sig, body_id) => visitor.visit_fn( @@ -1135,7 +1219,7 @@ pub fn walk_impl_item<'v, V: Visitor<'v>>( impl_item.span, impl_item.owner_id.def_id, ), - ImplItemKind::Type(ref ty) => visitor.visit_ty(ty), + ImplItemKind::Type(ref ty) => visitor.visit_ty_unambig(ty), } } @@ -1223,7 +1307,7 @@ pub fn walk_field_def<'v, V: Visitor<'v>>( try_visit!(visitor.visit_id(*hir_id)); try_visit!(visitor.visit_ident(*ident)); visit_opt!(visitor, visit_anon_const, default); - visitor.visit_ty(*ty) + visitor.visit_ty_unambig(*ty) } pub fn walk_enum_def<'v, V: Visitor<'v>>( @@ -1252,18 +1336,6 @@ pub fn walk_inf<'v, V: Visitor<'v>>(visitor: &mut V, inf: &'v InferArg) -> V::Re visitor.visit_id(inf.hir_id) } -pub fn walk_generic_arg<'v, V: Visitor<'v>>( - visitor: &mut V, - generic_arg: &'v GenericArg<'v>, -) -> V::Result { - match generic_arg { - GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt), - GenericArg::Type(ty) => visitor.visit_ty(ty), - GenericArg::Const(ct) => visitor.visit_const_arg(ct), - GenericArg::Infer(inf) => visitor.visit_infer(inf), - } -} - pub fn walk_lifetime<'v, V: Visitor<'v>>(visitor: &mut V, lifetime: &'v Lifetime) -> V::Result { try_visit!(visitor.visit_id(lifetime.hir_id)); visitor.visit_ident(lifetime.ident) @@ -1276,11 +1348,11 @@ pub fn walk_qpath<'v, V: Visitor<'v>>( ) -> V::Result { match *qpath { QPath::Resolved(ref maybe_qself, ref path) => { - visit_opt!(visitor, visit_ty, maybe_qself); + visit_opt!(visitor, visit_ty_unambig, maybe_qself); visitor.visit_path(path, id) } QPath::TypeRelative(ref qself, ref segment) => { - try_visit!(visitor.visit_ty(qself)); + try_visit!(visitor.visit_ty_unambig(qself)); visitor.visit_path_segment(segment) } QPath::LangItem(..) => V::Result::output(), @@ -1320,8 +1392,8 @@ pub fn walk_assoc_item_constraint<'v, V: Visitor<'v>>( try_visit!(visitor.visit_generic_args(constraint.gen_args)); match constraint.kind { AssocItemConstraintKind::Equality { ref term } => match term { - Term::Ty(ref ty) => try_visit!(visitor.visit_ty(ty)), - Term::Const(ref c) => try_visit!(visitor.visit_const_arg(c)), + Term::Ty(ref ty) => try_visit!(visitor.visit_ty_unambig(ty)), + Term::Const(ref c) => try_visit!(visitor.visit_const_arg_unambig(c)), }, AssocItemConstraintKind::Bound { bounds } => { walk_list!(visitor, visit_param_bound, bounds) diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs index 381062b14297..8ec2054bf53a 100644 --- a/compiler/rustc_hir/src/lib.rs +++ b/compiler/rustc_hir/src/lib.rs @@ -6,6 +6,7 @@ #![allow(internal_features)] #![feature(associated_type_defaults)] #![feature(closure_track_caller)] +#![feature(exhaustive_patterns)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_attrs)] 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 4a957d5da242..dbc5c634c455 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -6,9 +6,9 @@ use hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{GenericParamKind, ImplItemKind, intravisit}; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{self as hir, AmbigArg, GenericParamKind, ImplItemKind, intravisit}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::util; @@ -1610,7 +1610,7 @@ fn compare_synthetic_generics<'tcx>( struct Visitor(hir::def_id::LocalDefId); impl<'v> intravisit::Visitor<'v> for Visitor { type Result = ControlFlow; - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) -> Self::Result { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) -> Self::Result { if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind && let Res::Def(DefKind::TyParam, def_id) = path.res && def_id == self.0.to_def_id() @@ -1622,9 +1622,9 @@ fn compare_synthetic_generics<'tcx>( } } - let span = input_tys.iter().find_map(|ty| { - intravisit::Visitor::visit_ty(&mut Visitor(impl_def_id), ty).break_value() - })?; + let span = input_tys + .iter() + .find_map(|ty| Visitor(impl_def_id).visit_ty_unambig(ty).break_value())?; let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds; let bounds = bounds.first()?.span().to(bounds.last()?.span()); diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index dd6adb17c5e4..cd19993f9378 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -6,10 +6,10 @@ use rustc_abi::ExternAbi; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err}; -use rustc_hir::ItemKind; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::lang_items::LangItem; +use rustc_hir::{AmbigArg, ItemKind}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_macros::LintDiagnostic; @@ -2196,7 +2196,7 @@ impl<'tcx> Visitor<'tcx> for CollectUsageSpans<'_> { // Skip the generics. We only care about fields, not where clause/param bounds. } - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { if let hir::TyKind::Path(hir::QPath::Resolved(None, qpath)) = t.kind { if let Res::Def(DefKind::TyParam, def_id) = qpath.res && def_id == self.param_def_id diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 86c6532c97dd..c517d25fcbfc 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -28,7 +28,7 @@ use rustc_errors::{ }; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{self, Visitor, walk_generics}; +use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt, walk_generics}; use rustc_hir::{self as hir, GenericParamKind, HirId, Node}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; @@ -139,29 +139,12 @@ pub(crate) struct HirPlaceholderCollector { } impl<'v> Visitor<'v> for HirPlaceholderCollector { - fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { - if let hir::TyKind::Infer = t.kind { - self.spans.push(t.span); - } - intravisit::walk_ty(self, t) - } - fn visit_generic_arg(&mut self, generic_arg: &'v hir::GenericArg<'v>) { - match generic_arg { - hir::GenericArg::Infer(inf) => { - self.spans.push(inf.span); - self.may_contain_const_infer = true; - intravisit::walk_inf(self, inf); - } - hir::GenericArg::Type(t) => self.visit_ty(t), - _ => {} - } - } - fn visit_const_arg(&mut self, const_arg: &'v hir::ConstArg<'v>) { - if let hir::ConstArgKind::Infer(span) = const_arg.kind { + fn visit_infer(&mut self, _inf_id: HirId, inf_span: Span, kind: InferKind<'v>) -> Self::Result { + self.spans.push(inf_span); + + if let InferKind::Const(_) | InferKind::Ambig(_) = kind { self.may_contain_const_infer = true; - self.spans.push(span); } - intravisit::walk_const_arg(self, const_arg) } } @@ -583,7 +566,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { .iter() .enumerate() .map(|(i, a)| { - if let hir::TyKind::Infer = a.kind { + if let hir::TyKind::Infer(()) = a.kind { if let Some(suggested_ty) = self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i)) { @@ -593,21 +576,21 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { } // Only visit the type looking for `_` if we didn't fix the type above - visitor.visit_ty(a); + visitor.visit_ty_unambig(a); self.lowerer().lower_arg_ty(a, None) }) .collect(); let output_ty = match decl.output { hir::FnRetTy::Return(output) => { - if let hir::TyKind::Infer = output.kind + if let hir::TyKind::Infer(()) = output.kind && let Some(suggested_ty) = self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None) { infer_replacements.push((output.span, suggested_ty.to_string())); Ty::new_error_with_message(tcx, output.span, suggested_ty.to_string()) } else { - visitor.visit_ty(output); + visitor.visit_ty_unambig(output); self.lower_ty(output) } } @@ -1453,7 +1436,7 @@ fn recover_infer_ret_ty<'tcx>( }); let mut visitor = HirPlaceholderCollector::default(); - visitor.visit_ty(infer_ret_ty); + visitor.visit_ty_unambig(infer_ret_ty); let mut diag = bad_placeholder(icx.lowerer(), visitor.spans, "return type"); let ret_ty = fn_sig.output(); diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 075615de5227..2ac8acbd3a4b 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -3,9 +3,10 @@ use std::ops::ControlFlow; use hir::intravisit::{self, Visitor}; use hir::{GenericParamKind, HirId, Node}; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint; use rustc_span::{Span, Symbol, kw}; @@ -461,7 +462,7 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option Visitor<'tcx> for LateBoundRegionsDetector<'tcx> { type Result = ControlFlow; - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) -> ControlFlow { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) -> ControlFlow { match ty.kind { hir::TyKind::BareFn(..) => { self.outer_index.shift_in(1); @@ -539,7 +540,7 @@ impl<'v> Visitor<'v> for AnonConstInParamTyDetector { if let GenericParamKind::Const { ty, default: _, synthetic: _ } = p.kind { let prev = self.in_param_ty; self.in_param_ty = true; - let res = self.visit_ty(ty); + let res = self.visit_ty_unambig(ty); self.in_param_ty = prev; res } else { diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index e8706d1adfbb..582a2c7a0fc4 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -14,11 +14,11 @@ use rustc_ast::visit::walk_list; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, InferKind, Visitor, VisitorExt}; use rustc_hir::{ - GenericArg, GenericParam, GenericParamKind, HirId, ItemLocalMap, LifetimeName, Node, + self as hir, AmbigArg, GenericArg, GenericParam, GenericParamKind, HirId, ItemLocalMap, + LifetimeName, Node, }; use rustc_macros::extension; use rustc_middle::hir::nested_filter; @@ -489,15 +489,17 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { struct FindInferInClosureWithBinder; impl<'v> Visitor<'v> for FindInferInClosureWithBinder { type Result = ControlFlow; - fn visit_ty(&mut self, t: &'v hir::Ty<'v>) -> Self::Result { - if matches!(t.kind, hir::TyKind::Infer) { - ControlFlow::Break(t.span) - } else { - intravisit::walk_ty(self, t) - } + + fn visit_infer( + &mut self, + _inf_id: HirId, + inf_span: Span, + _kind: InferKind<'v>, + ) -> Self::Result { + ControlFlow::Break(inf_span) } } - FindInferInClosureWithBinder.visit_ty(ty).break_value() + FindInferInClosureWithBinder.visit_ty_unambig(ty).break_value() } let infer_in_rt_sp = match fn_decl.output { @@ -749,7 +751,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } #[instrument(level = "debug", skip(self))] - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { match ty.kind { hir::TyKind::BareFn(c) => { let (mut bound_vars, binders): (FxIndexMap, Vec<_>) = c @@ -810,7 +812,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { intravisit::walk_ty(this, ty); }); } - hir::TyKind::TraitObject(bounds, lifetime, _) => { + hir::TyKind::TraitObject(bounds, lifetime) => { + let lifetime = lifetime.pointer(); + debug!(?bounds, ?lifetime, "TraitObject"); let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |this| { @@ -827,7 +831,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { // use the object lifetime defaulting // rules. So e.g., `Box` becomes // `Box`. - self.resolve_object_lifetime_default(lifetime) + self.resolve_object_lifetime_default(&*lifetime) } LifetimeName::Infer => { // If the user writes `'_`, we use the *ordinary* elision @@ -838,7 +842,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } LifetimeName::Param(..) | LifetimeName::Static => { // If the user wrote an explicit name, use that. - self.visit_lifetime(lifetime); + self.visit_lifetime(&*lifetime); } LifetimeName::Error => {} } @@ -849,7 +853,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { lifetime: self.map.defs.get(&lifetime_ref.hir_id.local_id).cloned(), s: self.scope, }; - self.with(scope, |this| this.visit_ty(mt.ty)); + self.with(scope, |this| this.visit_ty_unambig(mt.ty)); } hir::TyKind::TraitAscription(bounds) => { let scope = Scope::TraitRefBoundary { s: self.scope }; @@ -891,7 +895,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { this.visit_param_bound(bound); } if let Some(ty) = ty { - this.visit_ty(ty); + this.visit_ty_unambig(ty); } }) } @@ -910,7 +914,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }), Type(ty) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| { this.visit_generics(impl_item.generics); - this.visit_ty(ty); + this.visit_ty_unambig(ty); }), Const(_, _) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| { intravisit::walk_impl_item(this, impl_item) @@ -1019,7 +1023,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { }; self.with(scope, |this| { walk_list!(this, visit_generic_param, bound_generic_params); - this.visit_ty(bounded_ty); + this.visit_ty_unambig(bounded_ty); walk_list!(this, visit_param_bound, bounds); }) } @@ -1034,8 +1038,8 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { &hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { - self.visit_ty(lhs_ty); - self.visit_ty(rhs_ty); + self.visit_ty_unambig(lhs_ty); + self.visit_ty_unambig(rhs_ty); } } } @@ -1068,13 +1072,13 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { GenericParamKind::Lifetime { .. } => {} GenericParamKind::Type { default, .. } => { if let Some(ty) = default { - self.visit_ty(ty); + self.visit_ty_unambig(ty); } } GenericParamKind::Const { ty, default, .. } => { - self.visit_ty(ty); + self.visit_ty_unambig(ty); if let Some(default) = default { - self.visit_const_arg(default); + self.visit_const_arg_unambig(default); } } } @@ -1983,15 +1987,15 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { }, |this| { for input in inputs { - this.visit_ty(input); + this.visit_ty_unambig(input); } if !in_closure && let Some(output) = output { - this.visit_ty(output); + this.visit_ty_unambig(output); } }, ); if in_closure && let Some(output) = output { - self.visit_ty(output); + self.visit_ty_unambig(output); } } @@ -2309,7 +2313,7 @@ fn is_late_bound_map( let mut constrained_by_input = ConstrainedCollector { regions: Default::default(), tcx }; for arg_ty in sig.decl.inputs { - constrained_by_input.visit_ty(arg_ty); + constrained_by_input.visit_ty_unambig(arg_ty); } let mut appears_in_output = @@ -2417,7 +2421,7 @@ fn is_late_bound_map( } impl<'v> Visitor<'v> for ConstrainedCollector<'_> { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { match ty.kind { hir::TyKind::Path( hir::QPath::Resolved(Some(_), _) | hir::QPath::TypeRelative(..), diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 51e15767b8c7..348d4d708b57 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -1,10 +1,9 @@ use core::ops::ControlFlow; use rustc_errors::{Applicability, StashKey, Suggestions}; -use rustc_hir as hir; -use rustc_hir::HirId; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{self as hir, AmbigArg, HirId}; use rustc_middle::query::plumbing::CyclePlaceholder; use rustc_middle::ty::fold::fold_regions; use rustc_middle::ty::print::with_forced_trimmed_paths; @@ -451,7 +450,7 @@ fn infer_placeholder_type<'tcx>( let mut visitor = HirPlaceholderCollector::default(); let node = tcx.hir_node_by_def_id(def_id); if let Some(ty) = node.ty() { - visitor.visit_ty(ty); + visitor.visit_ty_unambig(ty); } // If we have just one span, let's try to steal a const `_` feature error. let try_steal_span = if !tcx.features().generic_arg_infer() && visitor.spans.len() == 1 @@ -525,7 +524,7 @@ pub(crate) fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> struct HasTait; impl<'tcx> Visitor<'tcx> for HasTait { type Result = ControlFlow<()>; - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { if let hir::TyKind::OpaqueDef(..) = t.kind { ControlFlow::Break(()) } else { @@ -533,5 +532,5 @@ pub(crate) fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> } } } - HasTait.visit_ty(tcx.hir().expect_item(def_id).expect_ty_alias().0).is_break() + HasTait.visit_ty_unambig(tcx.hir().expect_item(def_id).expect_ty_alias().0).is_break() } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 5d751a250805..36b214b6ae73 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -9,6 +9,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_middle::bug; +use rustc_middle::ty::fast_reject::{TreatParams, simplify_type}; use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; use rustc_middle::ty::{ self, AdtDef, GenericParamDefKind, Ty, TyCtxt, TypeVisitableExt, @@ -998,12 +999,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { )), .. }) = node - && let Some(adt_def) = qself_ty.ty_adt_def() - && let [inherent_impl] = tcx.inherent_impls(adt_def.did()) - && let name = format!("{ident2}_{ident3}") - && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = tcx - .associated_items(inherent_impl) - .filter_by_name_unhygienic(Symbol::intern(&name)) + && let Some(inherent_impls) = qself_ty + .ty_adt_def() + .map(|adt_def| tcx.inherent_impls(adt_def.did())) + .or_else(|| { + simplify_type(tcx, qself_ty, TreatParams::InstantiateWithInfer) + .map(|simple_ty| tcx.incoherent_impls(simple_ty)) + }) + && let name = Symbol::intern(&format!("{ident2}_{ident3}")) + && let Some(ty::AssocItem { kind: ty::AssocKind::Fn, .. }) = inherent_impls + .iter() + .flat_map(|inherent_impl| { + tcx.associated_items(inherent_impl).filter_by_name_unhygienic(name) + }) .next() { Err(struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type") diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs index df00948dd211..fe3dcb35639f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs @@ -1,10 +1,9 @@ use rustc_ast::ast::ParamKindOrd; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, struct_span_code_err}; -use rustc_hir as hir; -use rustc_hir::GenericArg; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; +use rustc_hir::{self as hir, GenericArg}; use rustc_middle::ty::{ self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, }; @@ -41,17 +40,6 @@ fn generic_arg_mismatch_err( param.kind.descr(), ); - if let GenericParamDefKind::Const { .. } = param.kind { - if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) { - err.help("const arguments cannot yet be inferred with `_`"); - tcx.disabled_nightly_features( - &mut err, - param.def_id.as_local().map(|local| tcx.local_def_id_to_hir_id(local)), - [(String::new(), sym::generic_arg_infer)], - ); - } - } - let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diag<'_>| { let suggestions = vec![ (arg.span().shrink_to_lo(), String::from("{ ")), @@ -270,6 +258,21 @@ pub fn lower_generic_args<'tcx: 'a, 'a>( GenericParamDefKind::Const { .. }, _, ) => { + if let GenericParamDefKind::Const { .. } = param.kind + && let GenericArg::Infer(inf) = arg + && !tcx.features().generic_arg_infer() + { + rustc_session::parse::feature_err( + tcx.sess, + sym::generic_arg_infer, + inf.span, + "const arguments cannot yet be inferred with `_`", + ) + .emit(); + } + + // We lower to an infer even when the feature gate is not enabled + // as it is useful for diagnostics to be able to see a `ConstKind::Infer` args.push(ctx.provided_kind(&args, param, arg)); args_iter.next(); params.next(); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index a1f2b8c75948..539c5f6a20af 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -23,9 +23,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) -> Option { let tcx = self.tcx(); - let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) = + let poly_trait_ref = if let hir::TyKind::TraitObject([poly_trait_ref, ..], tagged_ptr) = self_ty.kind - else { + && let TraitObjectSyntax::None = tagged_ptr.tag() + { + poly_trait_ref + } else { return None; }; @@ -294,7 +297,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let (dyn_str, paren_dyn_str) = if borrowed { ("dyn ", "(dyn ") } else { ("&dyn ", "&(dyn ") }; - let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind { + let sugg = if let hir::TyKind::TraitObject([_, _, ..], _) = self_ty.kind { // There are more than one trait bound, we need surrounding parentheses. vec![ (self_ty.span.shrink_to_lo(), paren_dyn_str.to_string()), 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 cb90fff782fb..61d5869c19f1 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -517,14 +517,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lowerer.lower_lifetime(lt, RegionInferReason::Param(param)).into() } (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => { - handle_ty_args(has_default, ty) + // We handle the other parts of `Ty` in the match arm below + handle_ty_args(has_default, ty.as_unambig_ty()) } (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Infer(inf)) => { handle_ty_args(has_default, &inf.to_ty()) } - (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.lowerer.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into() - } + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self + .lowerer + // Ambig portions of `ConstArg` are handled in the match arm below + .lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id)) + .into(), (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { self.lowerer.ct_infer(Some(param), inf.span).into() } @@ -2115,7 +2118,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { format!("Const::lower_const_arg: invalid qpath {qpath:?}"), ), hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon), - hir::ConstArgKind::Infer(span) => self.ct_infer(None, span), + hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span), } } @@ -2311,7 +2314,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.late_bound_vars(hir_ty.hir_id), ), ), - hir::TyKind::TraitObject(bounds, lifetime, repr) => { + hir::TyKind::TraitObject(bounds, tagged_ptr) => { + let lifetime = tagged_ptr.pointer(); + let repr = tagged_ptr.tag(); + if let Some(guar) = self.prohibit_or_lint_bare_trait_object_ty(hir_ty) { // Don't continue with type analysis if the `dyn` keyword is missing // It generates confusing errors, especially if the user meant to use another @@ -2420,7 +2426,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Ty::new_array_with_const_len(tcx, self.lower_ty(ty), length) } hir::TyKind::Typeof(e) => tcx.type_of(e.def_id).instantiate_identity(), - hir::TyKind::Infer => { + hir::TyKind::Infer(()) => { // Infer also appears as the type of arguments or return // values in an ExprKind::Closure, or as // the type of local variables. Both of these cases are @@ -2553,7 +2559,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub fn lower_arg_ty(&self, ty: &hir::Ty<'tcx>, expected_ty: Option>) -> Ty<'tcx> { match ty.kind { - hir::TyKind::Infer if let Some(expected_ty) = expected_ty => { + hir::TyKind::Infer(()) if let Some(expected_ty) = expected_ty => { self.record_ty(ty.hir_id, expected_ty, ty.span); expected_ty } diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index c0fb94d2cb29..f5abcd234401 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -1,6 +1,5 @@ -use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{ForeignItem, ForeignItemKind}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; +use rustc_hir::{self as hir, AmbigArg, ForeignItem, ForeignItemKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::{ObligationCause, WellFormedLoc}; use rustc_middle::bug; @@ -68,11 +67,13 @@ fn diagnostic_hir_wf_check<'tcx>( } impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { let infcx = self.tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let ocx = ObligationCtxt::new_with_diagnostics(&infcx); - let tcx_ty = self.icx.lower_ty(ty); + // We don't handle infer vars but we wouldn't handle them anyway as we're creating a + // fresh `InferCtxt` in this function. + let tcx_ty = self.icx.lower_ty(ty.as_unambig_ty()); // This visitor can walk into binders, resulting in the `tcx_ty` to // potentially reference escaping bound variables. We simply erase // those here. @@ -149,7 +150,11 @@ fn diagnostic_hir_wf_check<'tcx>( .iter() .flat_map(|seg| seg.args().args) .filter_map(|arg| { - if let hir::GenericArg::Type(ty) = arg { Some(*ty) } else { None } + if let hir::GenericArg::Type(ty) = arg { + Some(ty.as_unambig_ty()) + } else { + None + } }) .chain([impl_.self_ty]) .collect(), @@ -196,7 +201,7 @@ fn diagnostic_hir_wf_check<'tcx>( } }; for ty in tys { - visitor.visit_ty(ty); + visitor.visit_ty_unambig(ty); } visitor.cause } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 3ff6acd79fcb..afc0f627f696 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -402,7 +402,8 @@ impl<'a> State<'a> { self.print_bounds("impl", bounds); } hir::TyKind::Path(ref qpath) => self.print_qpath(qpath, false), - hir::TyKind::TraitObject(bounds, lifetime, syntax) => { + hir::TyKind::TraitObject(bounds, lifetime) => { + let syntax = lifetime.tag(); match syntax { ast::TraitObjectSyntax::Dyn => self.word_nbsp("dyn"), ast::TraitObjectSyntax::DynStar => self.word_nbsp("dyn*"), @@ -421,7 +422,7 @@ impl<'a> State<'a> { if !lifetime.is_elided() { self.nbsp(); self.word_space("+"); - self.print_lifetime(lifetime); + self.print_lifetime(lifetime.pointer()); } } hir::TyKind::Array(ty, ref length) => { @@ -441,7 +442,7 @@ impl<'a> State<'a> { self.word("/*ERROR*/"); self.pclose(); } - hir::TyKind::Infer | hir::TyKind::InferDelegation(..) => { + hir::TyKind::Infer(()) | hir::TyKind::InferDelegation(..) => { self.word("_"); } hir::TyKind::Pat(ty, pat) => { @@ -1799,8 +1800,8 @@ impl<'a> State<'a> { match generic_arg { GenericArg::Lifetime(lt) if !elide_lifetimes => s.print_lifetime(lt), GenericArg::Lifetime(_) => {} - GenericArg::Type(ty) => s.print_type(ty), - GenericArg::Const(ct) => s.print_const_arg(ct), + GenericArg::Type(ty) => s.print_type(ty.as_unambig_ty()), + GenericArg::Const(ct) => s.print_const_arg(ct.as_unambig_ct()), GenericArg::Infer(_inf) => s.word("_"), } }); @@ -2150,7 +2151,7 @@ impl<'a> State<'a> { s.ann.nested(s, Nested::BodyParamPat(body_id, i)); i += 1; - if let hir::TyKind::Infer = ty.kind { + if let hir::TyKind::Infer(()) = ty.kind { // Print nothing. } else { s.word(":"); diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index c128485d93ed..1f3f03b99299 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -10,7 +10,7 @@ use rustc_hir as hir; use rustc_hir::HirId; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{InferKind, Visitor}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; @@ -641,16 +641,21 @@ impl<'tcx> AnnotateUnitFallbackVisitor<'_, 'tcx> { impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> { type Result = ControlFlow; - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_infer( + &mut self, + inf_id: HirId, + inf_span: Span, + _kind: InferKind<'tcx>, + ) -> Self::Result { // Try to replace `_` with `()`. - if let hir::TyKind::Infer = hir_ty.kind - && let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(hir_ty.hir_id) + if let Some(ty) = self.fcx.typeck_results.borrow().node_type_opt(inf_id) && let Some(vid) = self.fcx.root_vid(ty) && self.reachable_vids.contains(&vid) { - return ControlFlow::Break(errors::SuggestAnnotation::Unit(hir_ty.span)); + return ControlFlow::Break(errors::SuggestAnnotation::Unit(inf_span)); } - hir::intravisit::walk_ty(self, hir_ty) + + ControlFlow::Continue(()) } fn visit_qpath( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index e26c09e3601c..8e647ad3c6a4 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -6,9 +6,9 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; use rustc_hir::lang_items::LangItem; -use rustc_hir::{self as hir, ExprKind, GenericArg, HirId, Node, QPath, intravisit}; +use rustc_hir::{self as hir, AmbigArg, ExprKind, GenericArg, HirId, Node, QPath, intravisit}; use rustc_hir_analysis::hir_ty_lowering::errors::GenericsArgsErrExtend; use rustc_hir_analysis::hir_ty_lowering::generics::{ check_generic_arg_count_for_call, lower_generic_args, @@ -470,7 +470,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } impl<'tcx> intravisit::Visitor<'tcx> for CollectClauses<'_, 'tcx> { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { if let Some(clauses) = self.fcx.trait_ascriptions.borrow().get(&ty.hir_id.local_id) { self.clauses.extend(clauses.iter().cloned()); @@ -480,7 +480,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let mut clauses = CollectClauses { clauses: vec![], fcx: self }; - clauses.visit_ty(hir_ty); + clauses.visit_ty_unambig(hir_ty); self.tcx.mk_clauses(&clauses.clauses) } @@ -1272,14 +1272,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lower_lifetime(lt, RegionInferReason::Param(param)) .into(), (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.fcx.lower_ty(ty).raw.into() - } - (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.fcx.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into() + // We handle the ambig portions of `Ty` in match arm below + self.fcx.lower_ty(ty.as_unambig_ty()).raw.into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { - self.fcx.ty_infer(Some(param), inf.span).into() + self.fcx.lower_ty(&inf.to_ty()).raw.into() } + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self + .fcx + // Ambiguous parts of `ConstArg` are handled in the match arms below + .lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id)) + .into(), (&GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { self.fcx.ct_infer(Some(param), inf.span).into() } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 9cd9ca040ce7..c9e55695e5d9 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -251,7 +251,8 @@ fn typeck_with_inspect<'tcx>( fn infer_type_if_missing<'tcx>(fcx: &FnCtxt<'_, 'tcx>, node: Node<'tcx>) -> Option> { let tcx = fcx.tcx; let def_id = fcx.body_id; - let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = node.ty() { + let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer(()), span, .. }) = node.ty() + { if let Some(item) = tcx.opt_associated_item(def_id.into()) && let ty::AssocKind::Const = item.kind && let ty::AssocItemContainer::Impl = item.container diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index f549ced9dc33..880ee83c80a0 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -425,14 +425,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { .lower_lifetime(lt, RegionInferReason::Param(param)) .into(), (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.cfcx.lower_ty(ty).raw.into() - } - (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.cfcx.lower_const_arg(ct, FeedConstTy::Param(param.def_id)).into() + // We handle the ambig portions of `Ty` in the match arms below + self.cfcx.lower_ty(ty.as_unambig_ty()).raw.into() } (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { - self.cfcx.ty_infer(Some(param), inf.span).into() + self.cfcx.lower_ty(&inf.to_ty()).raw.into() } + (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => self + .cfcx + // We handle the ambig portions of `ConstArg` in the match arms below + .lower_const_arg(ct.as_unambig_ct(), FeedConstTy::Param(param.def_id)) + .into(), (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { self.cfcx.ct_infer(Some(param), inf.span).into() } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 683cacdff7d7..1bf5b19d68d0 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -6,9 +6,8 @@ use std::mem; use rustc_data_structures::unord::ExtendUnord; use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; -use rustc_hir::HirId; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, InferKind, Visitor}; +use rustc_hir::{self as hir, AmbigArg, HirId}; use rustc_middle::span_bug; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; @@ -354,7 +353,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { self.write_ty_to_typeck_results(l.hir_id, var_ty); } - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx, AmbigArg>) { intravisit::walk_ty(self, hir_ty); // If there are type checking errors, Type privacy pass will stop, // so we may not get the type from hid_id, see #104513 @@ -364,12 +363,20 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { } } - fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { - intravisit::walk_inf(self, inf); - // Ignore cases where the inference is a const. - if let Some(ty) = self.fcx.node_ty_opt(inf.hir_id) { - let ty = self.resolve(ty, &inf.span); - self.write_ty_to_typeck_results(inf.hir_id, ty); + fn visit_infer( + &mut self, + inf_id: HirId, + inf_span: Span, + _kind: InferKind<'cx>, + ) -> Self::Result { + self.visit_id(inf_id); + + // We don't currently write inference results of const infer vars to + // the typeck results as there is not yet any part of the compiler that + // needs this information. + if let Some(ty) = self.fcx.node_ty_opt(inf_id) { + let ty = self.resolve(ty, &inf_span); + self.write_ty_to_typeck_results(inf_id, ty); } } } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index b31a4c74787f..166ff60f7e1e 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -5,8 +5,8 @@ use rustc_ast as ast; use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{ - BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, PatKind, - Path, PathSegment, QPath, Ty, TyKind, + AmbigArg, BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, + PatKind, Path, PathSegment, QPath, Ty, TyKind, }; use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -159,7 +159,7 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { } } - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx Ty<'tcx, AmbigArg>) { match &ty.kind { TyKind::Path(QPath::Resolved(_, path)) => { if lint_ty_kind_usage(cx, &path.res) { diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index a4d50c731042..c6d7b839e197 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -8,9 +8,8 @@ use std::cell::Cell; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::join; -use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LocalModDefId}; -use rustc_hir::{HirId, intravisit as hir_visit}; +use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::Session; @@ -214,15 +213,11 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas }) } - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) { lint_callback!(self, check_ty, t); hir_visit::walk_ty(self, t); } - fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { - hir_visit::walk_inf(self, inf); - } - fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _: Span, n: HirId) { if !self.context.only_module { self.process_mod(m, n); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 2f610802ff5d..84a6272198fa 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -9,6 +9,7 @@ use rustc_errors::{ }; use rustc_hir::def::Namespace; use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::VisitorExt; use rustc_hir::{self as hir, MissingLifetimeKind}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::inhabitedness::InhabitedPredicate; @@ -293,7 +294,7 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> { // avoid doing throwaway work in case the lint ends up getting suppressed. let mut collector = ShorthandAssocTyCollector { qselves: Vec::new() }; if let Some(ty) = self.ty { - hir::intravisit::Visitor::visit_ty(&mut collector, ty); + collector.visit_ty_unambig(ty); } let affect_object_lifetime_defaults = self diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index a1cc3a85109a..1bf19047ade4 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -1,6 +1,6 @@ use rustc_errors::MultiSpan; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind}; use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint, impl_lint_pass}; @@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { // 1. We collect all the `hir::Path` from the `Self` type and `Trait` ref // of the `impl` definition let mut collector = PathCollector { paths: Vec::new() }; - collector.visit_ty(&impl_.self_ty); + collector.visit_ty_unambig(&impl_.self_ty); if let Some(of_trait) = &impl_.of_trait { collector.visit_trait_ref(of_trait); } diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 53a411e2b871..d347a8c1bc7f 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -1,4 +1,4 @@ -use rustc_hir as hir; +use rustc_hir::{self as hir, AmbigArg}; use rustc_infer::infer::TyCtxtInferExt; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::fold::BottomUpFolder; @@ -67,7 +67,7 @@ declare_lint! { declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]); impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { - fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { let hir::TyKind::OpaqueDef(opaque) = &ty.kind else { return; }; diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index 3f264859d483..244cd358e9ce 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -1,6 +1,5 @@ -use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::{GenericArg, PathSegment, QPath, TyKind}; +use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::sym; @@ -22,7 +21,7 @@ declare_tool_lint! { declare_lint_pass!(PassByValue => [PASS_BY_VALUE]); impl<'tcx> LateLintPass<'tcx> for PassByValue { - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) { + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { match &ty.kind { TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => { if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) { diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 3a323298bee8..8cc8f911d3a6 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -25,7 +25,7 @@ macro_rules! late_lint_methods { fn check_pat(a: &'tcx rustc_hir::Pat<'tcx>); fn check_expr(a: &'tcx rustc_hir::Expr<'tcx>); fn check_expr_post(a: &'tcx rustc_hir::Expr<'tcx>); - fn check_ty(a: &'tcx rustc_hir::Ty<'tcx>); + fn check_ty(a: &'tcx rustc_hir::Ty<'tcx, rustc_hir::AmbigArg>); fn check_generic_param(a: &'tcx rustc_hir::GenericParam<'tcx>); fn check_generics(a: &'tcx rustc_hir::Generics<'tcx>); fn check_poly_trait_ref(a: &'tcx rustc_hir::PolyTraitRef<'tcx>); diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index a9797c3b32a0..e0937e43c9a3 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -1,4 +1,4 @@ -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::{self as hir, AmbigArg, LangItem}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; @@ -110,8 +110,10 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { } } - fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) { - let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return }; + fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { + let hir::TyKind::TraitObject(bounds, _lifetime_and_syntax_pointer) = &ty.kind else { + return; + }; for bound in &bounds[..] { let def_id = bound.trait_ref.trait_def_id(); if def_id.is_some_and(|def_id| cx.tcx.is_lang_item(def_id, LangItem::Drop)) { diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 0757e6840c6d..0060f33888eb 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -4,7 +4,8 @@ use std::ops::ControlFlow; use rustc_abi::{BackendRepr, ExternAbi, TagEncoding, VariantIdx, Variants, WrappingRange}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::DiagMessage; -use rustc_hir::{Expr, ExprKind, LangItem}; +use rustc_hir::intravisit::VisitorExt; +use rustc_hir::{AmbigArg, Expr, ExprKind, LangItem}; use rustc_middle::bug; use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton}; use rustc_middle::ty::{ @@ -1526,7 +1527,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } impl<'a, 'b, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'a, 'b, 'tcx> { - fn visit_ty(&mut self, ty: &'_ hir::Ty<'_>) { + fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) { debug!(?ty); if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind && !self.visitor.is_internal_abi(*abi) @@ -1554,7 +1555,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { let mut visitor = FnPtrFinder { visitor: self, spans: Vec::new(), tys: Vec::new() }; ty.visit_with(&mut visitor); - hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty); + visitor.visit_ty_unambig(hir_ty); iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect() } diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 65f51ae9d39c..46534697e1d6 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Formatter}; use rustc_index::IndexVec; use rustc_index::bit_set::DenseBitSet; -use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; +use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_span::Span; rustc_index::newtype_index! { @@ -72,7 +72,7 @@ impl ConditionId { /// Enum that can hold a constant zero value, the ID of an physical coverage /// counter, or the ID of a coverage-counter expression. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub enum CovTerm { Zero, Counter(CounterId), @@ -89,7 +89,7 @@ impl Debug for CovTerm { } } -#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] pub enum CoverageKind { /// Marks a span that might otherwise not be represented in MIR, so that /// coverage instrumentation can associate it with its enclosing block/BCB. @@ -151,7 +151,7 @@ impl Debug for CoverageKind { } #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] -#[derive(TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable)] pub enum Op { Subtract, Add, @@ -168,7 +168,7 @@ impl Op { } #[derive(Clone, Debug, PartialEq, Eq)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct Expression { pub lhs: CovTerm, pub op: Op, @@ -176,7 +176,7 @@ pub struct Expression { } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub enum MappingKind { /// Associates a normal region of code with a counter/expression/zero. Code(CovTerm), @@ -208,7 +208,7 @@ impl MappingKind { } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct Mapping { pub kind: MappingKind, pub span: Span, @@ -218,7 +218,7 @@ pub struct Mapping { /// to be used in conjunction with the individual coverage statements injected /// into the function's basic blocks. #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct FunctionCoverageInfo { pub function_source_hash: u64, pub body_span: Span, @@ -238,7 +238,7 @@ pub struct FunctionCoverageInfo { /// ("Hi" indicates that this is "high-level" information collected at the /// THIR/MIR boundary, before the MIR-based coverage instrumentation pass.) #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct CoverageInfoHi { /// 1 more than the highest-numbered [`CoverageKind::BlockMarker`] that was /// injected into the MIR body. This makes it possible to allocate per-ID @@ -252,7 +252,7 @@ pub struct CoverageInfoHi { } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct BranchSpan { pub span: Span, pub true_marker: BlockMarkerId, @@ -260,7 +260,7 @@ pub struct BranchSpan { } #[derive(Copy, Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct ConditionInfo { pub condition_id: ConditionId, pub true_next_id: Option, @@ -268,7 +268,7 @@ pub struct ConditionInfo { } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct MCDCBranchSpan { pub span: Span, pub condition_info: ConditionInfo, @@ -277,14 +277,14 @@ pub struct MCDCBranchSpan { } #[derive(Copy, Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct DecisionInfo { pub bitmap_idx: u32, pub num_conditions: u16, } #[derive(Clone, Debug)] -#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable)] pub struct MCDCDecisionSpan { pub span: Span, pub end_markers: Vec, diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index bbb8bdce4a0c..0f3fca434eec 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -358,6 +358,8 @@ pub struct Body<'tcx> { /// /// Only present if coverage is enabled and this function is eligible. /// Boxed to limit space overhead in non-coverage builds. + #[type_foldable(identity)] + #[type_visitable(ignore)] pub coverage_info_hi: Option>, /// Per-function coverage information added by the `InstrumentCoverage` @@ -366,6 +368,8 @@ pub struct Body<'tcx> { /// /// If `-Cinstrument-coverage` is not active, or if an individual function /// is not eligible for coverage, then this should always be `None`. + #[type_foldable(identity)] + #[type_visitable(ignore)] pub function_coverage_info: Option>, } diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 0c17a2e0fe5a..29ae2e1bd6bc 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -417,7 +417,14 @@ pub enum StatementKind<'tcx> { /// /// Interpreters and codegen backends that don't support coverage instrumentation /// can usually treat this as a no-op. - Coverage(CoverageKind), + Coverage( + // Coverage statements are unlikely to ever contain type information in + // the foreseeable future, so excluding them from TypeFoldable/TypeVisitable + // avoids some unhelpful derive boilerplate. + #[type_foldable(identity)] + #[type_visitable(ignore)] + CoverageKind, + ), /// Denotes a call to an intrinsic that does not require an unwind path and always returns. /// This avoids adding a new block and a terminator for simple intrinsics. diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 05ded71dbeb5..17e1fe35bba0 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -618,7 +618,9 @@ rustc_queries! { /// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass /// (for compiler option `-Cinstrument-coverage`), after MIR optimizations /// have had a chance to potentially remove some of them. - query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> &'tcx mir::coverage::CoverageIdsInfo { + /// + /// Returns `None` for functions that were not instrumented. + query coverage_ids_info(key: ty::InstanceKind<'tcx>) -> Option<&'tcx mir::coverage::CoverageIdsInfo> { desc { |tcx| "retrieving coverage IDs info from MIR for `{}`", tcx.def_path_str(key.def_id()) } arena_cache } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 2d76f6ec7d69..aeb734ba3f65 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -33,7 +33,7 @@ use rustc_errors::{ use rustc_hir::def::{CtorKind, DefKind}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::definitions::Definitions; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; use rustc_hir::lang_items::LangItem; use rustc_hir::{self as hir, Attribute, HirId, Node, TraitCandidate}; use rustc_index::IndexVec; @@ -2028,7 +2028,7 @@ impl<'tcx> TyCtxt<'tcx> { }; let mut v = TraitObjectVisitor(vec![], self.hir()); - v.visit_ty(hir_output); + v.visit_ty_unambig(hir_output); v.0 } @@ -2050,7 +2050,7 @@ impl<'tcx> TyCtxt<'tcx> { && let Some(alias_ty) = self.hir_node_by_def_id(local_id).alias_ty() // it is type alias && let Some(alias_generics) = self.hir_node_by_def_id(local_id).generics() { - v.visit_ty(alias_ty); + v.visit_ty_unambig(alias_ty); if !v.0.is_empty() { return Some(( v.0, diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index e4187d2760c3..b4b66a8133a3 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -9,7 +9,7 @@ use rustc_errors::{ }; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicateKind}; +use rustc_hir::{self as hir, AmbigArg, LangItem, PredicateOrigin, WherePredicateKind}; use rustc_span::{BytePos, Span}; use rustc_type_ir::TyKind::*; @@ -570,18 +570,18 @@ pub fn suggest_constraining_type_params<'a>( pub struct TraitObjectVisitor<'tcx>(pub Vec<&'tcx hir::Ty<'tcx>>, pub crate::hir::map::Map<'tcx>); impl<'v> hir::intravisit::Visitor<'v> for TraitObjectVisitor<'v> { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { match ty.kind { - hir::TyKind::TraitObject( - _, - hir::Lifetime { + hir::TyKind::TraitObject(_, tagged_ptr) + if let hir::Lifetime { res: hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Static, .. - }, - _, - ) - | hir::TyKind::OpaqueDef(..) => self.0.push(ty), + } = tagged_ptr.pointer() => + { + self.0.push(ty.as_unambig_ty()) + } + hir::TyKind::OpaqueDef(..) => self.0.push(ty.as_unambig_ty()), _ => {} } hir::intravisit::walk_ty(self, ty); diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 9120a248d95f..867f8f639698 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -374,7 +374,13 @@ fn find_item_ty_spans( if let hir::GenericArg::Type(ty) = arg && params_in_repr.contains(i as u32) { - find_item_ty_spans(tcx, ty, needle, spans, seen_representable); + find_item_ty_spans( + tcx, + ty.as_unambig_ty(), + needle, + spans, + seen_representable, + ); } } } diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 20441530a479..945f02821d99 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -785,6 +785,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let local = place.as_local().unwrap_or_else(|| bug!("projection in tail call args")); + if !self.local_decls[local].ty.needs_drop(self.tcx, self.typing_env()) { + return None; + } + Some(DropData { source_info, local, kind: DropKind::Value }) } Operand::Constant(_) => None, @@ -795,6 +799,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.scopes.scopes.iter().rev().nth(1).unwrap().region_scope, DUMMY_SP, ); + let typing_env = self.typing_env(); let unwind_drops = &mut self.scopes.unwind_drops; // the innermost scope contains only the destructors for the tail call arguments @@ -805,6 +810,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let source_info = drop_data.source_info; let local = drop_data.local; + if !self.local_decls[local].ty.needs_drop(self.tcx, typing_env) { + continue; + } + match drop_data.kind { DropKind::Value => { // `unwind_to` should drop the value that we're about to diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 8d397f63cc7e..50ebde3292ea 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -11,7 +11,9 @@ use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, use crate::coverage::counters::balanced_flow::BalancedFlowGraph; use crate::coverage::counters::iter_nodes::IterNodes; -use crate::coverage::counters::node_flow::{CounterTerm, MergedNodeFlowGraph, NodeCounters}; +use crate::coverage::counters::node_flow::{ + CounterTerm, NodeCounters, make_node_counters, node_flow_data_for_balanced_graph, +}; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; mod balanced_flow; @@ -27,12 +29,12 @@ pub(super) fn make_bcb_counters( ) -> CoverageCounters { // Create the derived graphs that are necessary for subsequent steps. let balanced_graph = BalancedFlowGraph::for_graph(graph, |n| !graph[n].is_out_summable); - let merged_graph = MergedNodeFlowGraph::for_balanced_graph(&balanced_graph); + let node_flow_data = node_flow_data_for_balanced_graph(&balanced_graph); // Use those graphs to determine which nodes get physical counters, and how // to compute the execution counts of other nodes from those counters. - let nodes = make_node_counter_priority_list(graph, balanced_graph); - let node_counters = merged_graph.make_node_counters(&nodes); + let priority_list = make_node_flow_priority_list(graph, balanced_graph); + let node_counters = make_node_counters(&node_flow_data, &priority_list); // Convert the counters into a form suitable for embedding into MIR. transcribe_counters(&node_counters, bcb_needs_counter) @@ -40,7 +42,7 @@ pub(super) fn make_bcb_counters( /// Arranges the nodes in `balanced_graph` into a list, such that earlier nodes /// take priority in being given a counter expression instead of a physical counter. -fn make_node_counter_priority_list( +fn make_node_flow_priority_list( graph: &CoverageGraph, balanced_graph: BalancedFlowGraph<&CoverageGraph>, ) -> Vec { @@ -81,11 +83,11 @@ fn transcribe_counters( let mut new = CoverageCounters::with_num_bcbs(bcb_needs_counter.domain_size()); for bcb in bcb_needs_counter.iter() { - // Our counter-creation algorithm doesn't guarantee that a counter - // expression starts or ends with a positive term, so partition the + // Our counter-creation algorithm doesn't guarantee that a node's list + // of terms starts or ends with a positive term, so partition the // counters into "positive" and "negative" lists for easier handling. let (mut pos, mut neg): (Vec<_>, Vec<_>) = - old.counter_expr(bcb).iter().partition_map(|&CounterTerm { node, op }| match op { + old.counter_terms[bcb].iter().partition_map(|&CounterTerm { node, op }| match op { Op::Add => Either::Left(node), Op::Subtract => Either::Right(node), }); diff --git a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs index 610498c6c0ec..3647c8899374 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs @@ -8,18 +8,17 @@ use rustc_data_structures::graph; use rustc_index::bit_set::DenseBitSet; -use rustc_index::{Idx, IndexVec}; +use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_middle::mir::coverage::Op; -use smallvec::SmallVec; use crate::coverage::counters::iter_nodes::IterNodes; -use crate::coverage::counters::union_find::{FrozenUnionFind, UnionFind}; +use crate::coverage::counters::union_find::UnionFind; #[cfg(test)] mod tests; -/// View of some underlying graph, in which each node's successors have been -/// merged into a single "supernode". +/// Data representing a view of some underlying graph, in which each node's +/// successors have been merged into a single "supernode". /// /// The resulting supernodes have no obvious meaning on their own. /// However, merging successor nodes means that a node's out-edges can all @@ -30,10 +29,10 @@ mod tests; /// in the merged graph, it becomes possible to analyze the original node flows /// using techniques for analyzing edge flows. #[derive(Debug)] -pub(crate) struct MergedNodeFlowGraph { +pub(crate) struct NodeFlowData { /// Maps each node to the supernode that contains it, indicated by some /// arbitrary "root" node that is part of that supernode. - supernodes: FrozenUnionFind, + supernodes: IndexVec, /// For each node, stores the single supernode that all of its successors /// have been merged into. /// @@ -42,84 +41,71 @@ pub(crate) struct MergedNodeFlowGraph { succ_supernodes: IndexVec, } -impl MergedNodeFlowGraph { - /// Creates a "merged" view of an underlying graph. - /// - /// The given graph is assumed to have [“balanced flow”](balanced-flow), - /// though it does not necessarily have to be a `BalancedFlowGraph`. - /// - /// [balanced-flow]: `crate::coverage::counters::balanced_flow::BalancedFlowGraph`. - pub(crate) fn for_balanced_graph(graph: G) -> Self - where - G: graph::DirectedGraph + graph::Successors, - { - let mut supernodes = UnionFind::::new(graph.num_nodes()); +/// Creates a "merged" view of an underlying graph. +/// +/// The given graph is assumed to have [“balanced flow”](balanced-flow), +/// though it does not necessarily have to be a `BalancedFlowGraph`. +/// +/// [balanced-flow]: `crate::coverage::counters::balanced_flow::BalancedFlowGraph`. +pub(crate) fn node_flow_data_for_balanced_graph(graph: G) -> NodeFlowData +where + G: graph::Successors, +{ + let mut supernodes = UnionFind::::new(graph.num_nodes()); - // For each node, merge its successors into a single supernode, and - // arbitrarily choose one of those successors to represent all of them. - let successors = graph - .iter_nodes() - .map(|node| { - graph - .successors(node) - .reduce(|a, b| supernodes.unify(a, b)) - .expect("each node in a balanced graph must have at least one out-edge") - }) - .collect::>(); + // For each node, merge its successors into a single supernode, and + // arbitrarily choose one of those successors to represent all of them. + let successors = graph + .iter_nodes() + .map(|node| { + graph + .successors(node) + .reduce(|a, b| supernodes.unify(a, b)) + .expect("each node in a balanced graph must have at least one out-edge") + }) + .collect::>(); - // Now that unification is complete, freeze the supernode forest, - // and resolve each arbitrarily-chosen successor to its canonical root. - // (This avoids having to explicitly resolve them later.) - let supernodes = supernodes.freeze(); - let succ_supernodes = successors.into_iter().map(|succ| supernodes.find(succ)).collect(); + // Now that unification is complete, take a snapshot of the supernode forest, + // and resolve each arbitrarily-chosen successor to its canonical root. + // (This avoids having to explicitly resolve them later.) + let supernodes = supernodes.snapshot(); + let succ_supernodes = successors.into_iter().map(|succ| supernodes[succ]).collect(); - Self { supernodes, succ_supernodes } + NodeFlowData { supernodes, succ_supernodes } +} + +/// Uses the graph information in `node_flow_data`, together with a given +/// permutation of all nodes in the graph, to create physical counters and +/// counter expressions for each node in the underlying graph. +/// +/// The given list must contain exactly one copy of each node in the +/// underlying balanced-flow graph. The order of nodes is used as a hint to +/// influence counter allocation: +/// - Earlier nodes are more likely to receive counter expressions. +/// - Later nodes are more likely to receive physical counters. +pub(crate) fn make_node_counters( + node_flow_data: &NodeFlowData, + priority_list: &[Node], +) -> NodeCounters { + let mut builder = SpantreeBuilder::new(node_flow_data); + + for &node in priority_list { + builder.visit_node(node); } - fn num_nodes(&self) -> usize { - self.succ_supernodes.len() - } - - fn is_supernode(&self, node: Node) -> bool { - self.supernodes.find(node) == node - } - - /// Using the information in this merged graph, together with a given - /// permutation of all nodes in the graph, to create physical counters and - /// counter expressions for each node in the underlying graph. - /// - /// The given list must contain exactly one copy of each node in the - /// underlying balanced-flow graph. The order of nodes is used as a hint to - /// influence counter allocation: - /// - Earlier nodes are more likely to receive counter expressions. - /// - Later nodes are more likely to receive physical counters. - pub(crate) fn make_node_counters(&self, all_nodes_permutation: &[Node]) -> NodeCounters { - let mut builder = SpantreeBuilder::new(self); - - for &node in all_nodes_permutation { - builder.visit_node(node); - } - - NodeCounters { counter_exprs: builder.finish() } - } + NodeCounters { counter_terms: builder.finish() } } /// End result of allocating physical counters and counter expressions for the /// nodes of a graph. #[derive(Debug)] pub(crate) struct NodeCounters { - counter_exprs: IndexVec>, -} - -impl NodeCounters { /// For the given node, returns the finished list of terms that represent /// its physical counter or counter expression. Always non-empty. /// - /// If a node was given a physical counter, its "expression" will contain + /// If a node was given a physical counter, the term list will contain /// that counter as its sole element. - pub(crate) fn counter_expr(&self, this: Node) -> &[CounterTerm] { - self.counter_exprs[this].as_slice() - } + pub(crate) counter_terms: IndexVec>>, } #[derive(Debug)] @@ -146,12 +132,11 @@ pub(crate) struct CounterTerm { pub(crate) node: Node, } -/// Stores the list of counter terms that make up a node's counter expression. -type CounterExprVec = SmallVec<[CounterTerm; 2]>; - #[derive(Debug)] struct SpantreeBuilder<'a, Node: Idx> { - graph: &'a MergedNodeFlowGraph, + supernodes: &'a IndexSlice, + succ_supernodes: &'a IndexSlice, + is_unvisited: DenseBitSet, /// Links supernodes to each other, gradually forming a spanning tree of /// the merged-flow graph. @@ -163,26 +148,32 @@ struct SpantreeBuilder<'a, Node: Idx> { yank_buffer: Vec, /// An in-progress counter expression for each node. Each expression is /// initially empty, and will be filled in as relevant nodes are visited. - counter_exprs: IndexVec>, + counter_terms: IndexVec>>, } impl<'a, Node: Idx> SpantreeBuilder<'a, Node> { - fn new(graph: &'a MergedNodeFlowGraph) -> Self { - let num_nodes = graph.num_nodes(); + fn new(node_flow_data: &'a NodeFlowData) -> Self { + let NodeFlowData { supernodes, succ_supernodes } = node_flow_data; + let num_nodes = supernodes.len(); Self { - graph, + supernodes, + succ_supernodes, is_unvisited: DenseBitSet::new_filled(num_nodes), span_edges: IndexVec::from_fn_n(|_| None, num_nodes), yank_buffer: vec![], - counter_exprs: IndexVec::from_fn_n(|_| SmallVec::new(), num_nodes), + counter_terms: IndexVec::from_fn_n(|_| vec![], num_nodes), } } + fn is_supernode(&self, node: Node) -> bool { + self.supernodes[node] == node + } + /// Given a supernode, finds the supernode that is the "root" of its /// spantree component. Two nodes that have the same spantree root are /// connected in the spantree. fn spantree_root(&self, this: Node) -> Node { - debug_assert!(self.graph.is_supernode(this)); + debug_assert!(self.is_supernode(this)); match self.span_edges[this] { None => this, @@ -193,7 +184,7 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> { /// Rotates edges in the spantree so that `this` is the root of its /// spantree component. fn yank_to_spantree_root(&mut self, this: Node) { - debug_assert!(self.graph.is_supernode(this)); + debug_assert!(self.is_supernode(this)); // The rotation is done iteratively, by first traversing from `this` to // its root and storing the path in a buffer, and then traversing the @@ -235,12 +226,12 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> { // Get the supernode containing `this`, and make it the root of its // component of the spantree. - let this_supernode = self.graph.supernodes.find(this); + let this_supernode = self.supernodes[this]; self.yank_to_spantree_root(this_supernode); // Get the supernode containing all of this's successors. - let succ_supernode = self.graph.succ_supernodes[this]; - debug_assert!(self.graph.is_supernode(succ_supernode)); + let succ_supernode = self.succ_supernodes[this]; + debug_assert!(self.is_supernode(succ_supernode)); // If two supernodes are already connected in the spantree, they will // have the same spantree root. (Each supernode is connected to itself.) @@ -268,8 +259,8 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> { // `this_supernode`. // Instead of setting `this.measure = true` as in the original paper, - // we just add the node's ID to its own "expression". - self.counter_exprs[this].push(CounterTerm { node: this, op: Op::Add }); + // we just add the node's ID to its own list of terms. + self.counter_terms[this].push(CounterTerm { node: this, op: Op::Add }); // Walk the spantree from `this.successor` back to `this`. For each // spantree edge along the way, add this node's physical counter to @@ -279,7 +270,7 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> { let &SpantreeEdge { is_reversed, claiming_node, span_parent } = self.span_edges[curr].as_ref().unwrap(); let op = if is_reversed { Op::Subtract } else { Op::Add }; - self.counter_exprs[claiming_node].push(CounterTerm { node: this, op }); + self.counter_terms[claiming_node].push(CounterTerm { node: this, op }); curr = span_parent; } @@ -288,19 +279,20 @@ impl<'a, Node: Idx> SpantreeBuilder<'a, Node> { /// Asserts that all nodes have been visited, and returns the computed /// counter expressions (made up of physical counters) for each node. - fn finish(self) -> IndexVec> { - let Self { graph, is_unvisited, span_edges, yank_buffer: _, counter_exprs } = self; + fn finish(self) -> IndexVec>> { + let Self { ref span_edges, ref is_unvisited, ref counter_terms, .. } = self; assert!(is_unvisited.is_empty(), "some nodes were never visited: {is_unvisited:?}"); debug_assert!( span_edges .iter_enumerated() - .all(|(node, span_edge)| { span_edge.is_some() <= graph.is_supernode(node) }), + .all(|(node, span_edge)| { span_edge.is_some() <= self.is_supernode(node) }), "only supernodes can have a span edge", ); debug_assert!( - counter_exprs.iter().all(|expr| !expr.is_empty()), - "after visiting all nodes, every node should have a non-empty expression", + counter_terms.iter().all(|terms| !terms.is_empty()), + "after visiting all nodes, every node should have at least one term", ); - counter_exprs + + self.counter_terms } } diff --git a/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs b/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs index 9e7f754523d3..b509a14514b7 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters/node_flow/tests.rs @@ -4,10 +4,12 @@ use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_index::Idx; use rustc_middle::mir::coverage::Op; -use super::{CounterTerm, MergedNodeFlowGraph, NodeCounters}; +use crate::coverage::counters::node_flow::{ + CounterTerm, NodeCounters, NodeFlowData, make_node_counters, node_flow_data_for_balanced_graph, +}; -fn merged_node_flow_graph(graph: G) -> MergedNodeFlowGraph { - MergedNodeFlowGraph::for_balanced_graph(graph) +fn node_flow_data(graph: G) -> NodeFlowData { + node_flow_data_for_balanced_graph(graph) } fn make_graph(num_nodes: usize, edge_pairs: Vec<(Node, Node)>) -> VecGraph { @@ -30,8 +32,8 @@ fn example_driver() { (4, 0), ]); - let merged = merged_node_flow_graph(&graph); - let counters = merged.make_node_counters(&[3, 1, 2, 0, 4]); + let node_flow_data = node_flow_data(&graph); + let counters = make_node_counters(&node_flow_data, &[3, 1, 2, 0, 4]); assert_eq!(format_counter_expressions(&counters), &[ // (comment to force vertical formatting for clarity) @@ -53,12 +55,12 @@ fn format_counter_expressions(counters: &NodeCounters) -> Vec>(); - expr.sort_by_key(|item| item.node.index()); - format!("[{node:?}]: {}", expr.into_iter().map(format_item).join(" ")) + let mut terms = counters.counter_terms[node].iter().collect::>(); + terms.sort_by_key(|item| item.node.index()); + format!("[{node:?}]: {}", terms.into_iter().map(format_item).join(" ")) }) .collect() } diff --git a/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs b/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs index 2da4f5f5fce1..a826a953fa67 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs @@ -88,29 +88,9 @@ impl UnionFind { a } - /// Creates a snapshot of this disjoint-set forest that can no longer be - /// mutated, but can be queried without mutation. - pub(crate) fn freeze(&mut self) -> FrozenUnionFind { - // Just resolve each key to its actual root. - let roots = self.table.indices().map(|key| self.find(key)).collect(); - FrozenUnionFind { roots } - } -} - -/// Snapshot of a disjoint-set forest that can no longer be mutated, but can be -/// queried in O(1) time without mutation. -/// -/// This is really just a wrapper around a direct mapping from keys to roots, -/// but with a [`Self::find`] method that resembles [`UnionFind::find`]. -#[derive(Debug)] -pub(crate) struct FrozenUnionFind { - roots: IndexVec, -} - -impl FrozenUnionFind { - /// Returns the "root" key of the disjoint-set containing the given key. - /// If two keys have the same root, they belong to the same set. - pub(crate) fn find(&self, key: Key) -> Key { - self.roots[key] + /// Takes a "snapshot" of the current state of this disjoint-set forest, in + /// the form of a vector that directly maps each key to its current root. + pub(crate) fn snapshot(&mut self) -> IndexVec { + self.table.indices().map(|key| self.find(key)).collect() } } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 19568735df76..b8aa76a7dbe5 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -180,7 +180,12 @@ fn create_mappings( )); for (decision, branches) in mcdc_mappings { - let num_conditions = branches.len() as u16; + // FIXME(#134497): Previously it was possible for some of these branch + // conversions to fail, in which case the remaining branches in the + // decision would be degraded to plain `MappingKind::Branch`. + // The changes in #134497 made that failure impossible, because the + // fallible step was deferred to codegen. But the corresponding code + // in codegen wasn't updated to detect the need for a degrade step. let conditions = branches .into_iter() .map( @@ -206,24 +211,13 @@ fn create_mappings( ) .collect::>(); - if conditions.len() == num_conditions as usize { - // LLVM requires end index for counter mapping regions. - let kind = MappingKind::MCDCDecision(DecisionInfo { - bitmap_idx: (decision.bitmap_idx + decision.num_test_vectors) as u32, - num_conditions, - }); - let span = decision.span; - mappings.extend(std::iter::once(Mapping { kind, span }).chain(conditions.into_iter())); - } else { - mappings.extend(conditions.into_iter().map(|mapping| { - let MappingKind::MCDCBranch { true_term, false_term, mcdc_params: _ } = - mapping.kind - else { - unreachable!("all mappings here are MCDCBranch as shown above"); - }; - Mapping { kind: MappingKind::Branch { true_term, false_term }, span: mapping.span } - })) - } + // LLVM requires end index for counter mapping regions. + let kind = MappingKind::MCDCDecision(DecisionInfo { + bitmap_idx: (decision.bitmap_idx + decision.num_test_vectors) as u32, + num_conditions: u16::try_from(conditions.len()).unwrap(), + }); + let span = decision.span; + mappings.extend(std::iter::once(Mapping { kind, span }).chain(conditions.into_iter())); } mappings diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 3e7cf8541c23..5e7b46182dcf 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -87,15 +87,9 @@ fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { fn coverage_ids_info<'tcx>( tcx: TyCtxt<'tcx>, instance_def: ty::InstanceKind<'tcx>, -) -> CoverageIdsInfo { +) -> Option { let mir_body = tcx.instance_mir(instance_def); - - let Some(fn_cov_info) = mir_body.function_coverage_info.as_deref() else { - return CoverageIdsInfo { - counters_seen: DenseBitSet::new_empty(0), - zero_expressions: DenseBitSet::new_empty(0), - }; - }; + let fn_cov_info = mir_body.function_coverage_info.as_deref()?; let mut counters_seen = DenseBitSet::new_empty(fn_cov_info.num_counters); let mut expressions_seen = DenseBitSet::new_filled(fn_cov_info.expressions.len()); @@ -129,7 +123,7 @@ fn coverage_ids_info<'tcx>( let zero_expressions = identify_zero_expressions(fn_cov_info, &counters_seen, &expressions_seen); - CoverageIdsInfo { counters_seen, zero_expressions } + Some(CoverageIdsInfo { counters_seen, zero_expressions }) } fn all_coverage_in_mir_body<'a, 'tcx>( diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 10756be6afb8..25d8eb9b4539 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -189,8 +189,9 @@ pub struct Parser<'a> { } // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with -// nonterminals. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_pointer_width = "64", not(target_arch = "s390x")))] +// nonterminals. Make sure it doesn't unintentionally get bigger. We only check a few arches +// though, because `TokenTypeSet(u128)` alignment varies on others, changing the total size. +#[cfg(all(target_pointer_width = "64", any(target_arch = "aarch64", target_arch = "x86_64")))] rustc_data_structures::static_assert_size!(Parser<'_>, 288); /// Stores span information about a closure. diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index dbb87443eed5..576ca24bf994 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -2808,7 +2808,7 @@ fn doc_fake_variadic_is_allowed_self_ty(self_ty: &hir::Ty<'_>) -> bool { && let Some(&[hir::GenericArg::Type(ty)]) = path.segments.last().map(|last| last.args().args) { - doc_fake_variadic_is_allowed_self_ty(ty) + doc_fake_variadic_is_allowed_self_ty(ty.as_unambig_ty()) } else { false }) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 34deb854e0fe..e5b63b9b4a60 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -460,7 +460,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } // mark self_ty live - intravisit::walk_ty(self, impl_ref.self_ty); + intravisit::walk_unambig_ty(self, impl_ref.self_ty); if let Some(&impl_item_id) = self.tcx.impl_item_implementor_ids(impl_id).get(&trait_item_id) { diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 6617cf2f723a..8b10543f6fdb 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -5,8 +5,7 @@ use rustc_ast::visit::BoundKind; use rustc_ast::{self as ast, NodeId, visit as ast_visit}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir as hir; -use rustc_hir::{HirId, intravisit as hir_visit}; +use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit}; use rustc_middle::hir::map::Map; use rustc_middle::ty::TyCtxt; use rustc_middle::util::common::to_readable_str; @@ -363,7 +362,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_expr_field(self, f) } - fn visit_ty(&mut self, t: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, t: &'v hir::Ty<'v, AmbigArg>) { record_variants!((self, t, t.kind, Some(t.hir_id), hir, Ty, TyKind), [ InferDelegation, Slice, @@ -476,7 +475,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir::GenericArg::Lifetime(lt) => self.visit_lifetime(lt), hir::GenericArg::Type(ty) => self.visit_ty(ty), hir::GenericArg::Const(ct) => self.visit_const_arg(ct), - hir::GenericArg::Infer(inf) => self.visit_infer(inf), + hir::GenericArg::Infer(inf) => self.visit_id(inf.hir_id), } } diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 60734122e632..a52f080038d1 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -11,12 +11,11 @@ use rustc_attr_parsing::{ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet}; use rustc_feature::{ACCEPTED_LANG_FEATURES, EnabledLangFeature, EnabledLibFeature}; -use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::hir_id::CRATE_HIR_ID; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; +use rustc_hir::{self as hir, AmbigArg, FieldDef, Item, ItemKind, TraitRef, Ty, TyKind, Variant}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::lib_features::{FeatureStability, LibFeatures}; use rustc_middle::middle::privacy::EffectiveVisibilities; @@ -802,7 +801,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { )) = stab { let mut c = CheckTraitImplStable { tcx: self.tcx, fully_stable: true }; - c.visit_ty(self_ty); + c.visit_ty_unambig(self_ty); c.visit_trait_ref(t); // do not lint when the trait isn't resolved, since resolution error should @@ -1028,7 +1027,7 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { intravisit::walk_trait_ref(self, t) } - fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) { + fn visit_ty(&mut self, t: &'tcx Ty<'tcx, AmbigArg>) { if let TyKind::Never = t.kind { self.fully_stable = false; } @@ -1042,12 +1041,12 @@ impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) { for ty in fd.inputs { - self.visit_ty(ty) + self.visit_ty_unambig(ty) } if let hir::FnRetTy::Return(output_ty) = fd.output { match output_ty.kind { TyKind::Never => {} // `-> !` is stable - _ => self.visit_ty(output_ty), + _ => self.visit_ty_unambig(output_ty), } } } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index cb7b0815a49c..d19df08519d2 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -27,8 +27,8 @@ use rustc_data_structures::intern::Interned; use rustc_errors::MultiSpan; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId, LocalModDefId}; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{AssocItemKind, ForeignItemKind, ItemId, ItemKind, PatKind}; +use rustc_hir::intravisit::{self, InferKind, Visitor}; +use rustc_hir::{AmbigArg, AssocItemKind, ForeignItemKind, ItemId, ItemKind, PatKind}; use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level}; use rustc_middle::query::Providers; use rustc_middle::ty::print::PrintTraitRefExt as _; @@ -1179,7 +1179,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { self.maybe_typeck_results = old_maybe_typeck_results; } - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx, AmbigArg>) { self.span = hir_ty.span; if self .visit( @@ -1195,12 +1195,17 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { intravisit::walk_ty(self, hir_ty); } - fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { - self.span = inf.span; + fn visit_infer( + &mut self, + inf_id: rustc_hir::HirId, + inf_span: Span, + _kind: InferKind<'tcx>, + ) -> Self::Result { + self.span = inf_span; if let Some(ty) = self .maybe_typeck_results - .unwrap_or_else(|| span_bug!(inf.span, "`hir::InferArg` outside of a body")) - .node_type_opt(inf.hir_id) + .unwrap_or_else(|| span_bug!(inf_span, "Inference variable outside of a body")) + .node_type_opt(inf_id) { if self.visit(ty).is_break() { return; @@ -1208,7 +1213,8 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { } else { // FIXME: check types of const infers here. } - intravisit::walk_inf(self, inf); + + self.visit_id(inf_id) } // Check types of expressions diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 1f2df7f0168c..37564ab38fca 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1853,6 +1853,8 @@ supported_targets! { ("armv7a-none-eabi", armv7a_none_eabi), ("armv7a-none-eabihf", armv7a_none_eabihf), + ("armv7a-nuttx-eabi", armv7a_nuttx_eabi), + ("armv7a-nuttx-eabihf", armv7a_nuttx_eabihf), ("msp430-none-elf", msp430_none_elf), @@ -1896,6 +1898,7 @@ supported_targets! { ("aarch64-unknown-none", aarch64_unknown_none), ("aarch64-unknown-none-softfloat", aarch64_unknown_none_softfloat), + ("aarch64-unknown-nuttx", aarch64_unknown_nuttx), ("x86_64-fortanix-unknown-sgx", x86_64_fortanix_unknown_sgx), @@ -1971,6 +1974,8 @@ supported_targets! { ("x86_64-unknown-linux-none", x86_64_unknown_linux_none), ("thumbv6m-nuttx-eabi", thumbv6m_nuttx_eabi), + ("thumbv7a-nuttx-eabi", thumbv7a_nuttx_eabi), + ("thumbv7a-nuttx-eabihf", thumbv7a_nuttx_eabihf), ("thumbv7m-nuttx-eabi", thumbv7m_nuttx_eabi), ("thumbv7em-nuttx-eabi", thumbv7em_nuttx_eabi), ("thumbv7em-nuttx-eabihf", thumbv7em_nuttx_eabihf), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs index d6b77ffd091a..3b719ebaf07e 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs @@ -7,7 +7,8 @@ // For example, `-C target-cpu=cortex-a53`. use crate::spec::{ - Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, StackProbeType, Target, TargetOptions, + Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target, + TargetOptions, }; pub(crate) fn target() -> Target { @@ -19,6 +20,7 @@ pub(crate) fn target() -> Target { relocation_model: RelocModel::Static, disable_redzone: true, max_atomic_width: Some(128), + supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, stack_probes: StackProbeType::Inline, panic_strategy: PanicStrategy::Abort, ..Default::default() diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs new file mode 100644 index 000000000000..04fd3ec1c26d --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs @@ -0,0 +1,46 @@ +// Generic AArch64 target for NuttX OS +// +// Can be used in conjunction with the `target-feature` and +// `target-cpu` compiler flags to opt-in more hardware-specific +// features. +// +// For example, `-C target-cpu=cortex-a53`. + +use crate::spec::{ + Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, SanitizerSet, StackProbeType, Target, + TargetOptions, cvs, +}; + +pub(crate) fn target() -> Target { + let opts = TargetOptions { + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + // Enable the Cortex-A53 errata 843419 mitigation by default + pre_link_args: TargetOptions::link_args(LinkerFlavor::Gnu(Cc::No, Lld::No), &[ + "--fix-cortex-a53-843419", + ]), + features: "+v8a,+strict-align,+neon,+fp-armv8".into(), + supported_sanitizers: SanitizerSet::KCFI | SanitizerSet::KERNELADDRESS, + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(128), + stack_probes: StackProbeType::Inline, + panic_strategy: PanicStrategy::Abort, + families: cvs!["unix"], + os: "nuttx".into(), + ..Default::default() + }; + Target { + llvm_target: "aarch64-unknown-none".into(), + metadata: crate::spec::TargetMetadata { + description: Some("AArch64 NuttX".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 64, + data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), + arch: "aarch64".into(), + options: opts, + } +} diff --git a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs new file mode 100644 index 000000000000..138716e8f143 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs @@ -0,0 +1,41 @@ +// Targets Cortex-A7/A8/A9 processors (ARMv7-A) running NuttX +// +// This target assumes that the device does NOT have a FPU (Floating Point Unit) +// and will use software floating point operations. This matches the NuttX EABI +// configuration without hardware floating point support. + +use crate::spec::{ + Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions, cvs, +}; + +pub(crate) fn target() -> Target { + let opts = TargetOptions { + abi: "eabi".into(), + llvm_floatabi: Some(FloatAbi::Soft), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + features: "+v7,+thumb2,+soft-float,-neon,+strict-align".into(), + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(64), + panic_strategy: PanicStrategy::Abort, + emit_debug_gdb_scripts: false, + c_enum_min_bits: Some(8), + families: cvs!["unix"], + os: "nuttx".into(), + ..Default::default() + }; + Target { + llvm_target: "armv7a-none-eabi".into(), + metadata: crate::spec::TargetMetadata { + description: Some("ARMv7-A Cortex-A with NuttX".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + options: opts, + } +} diff --git a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs new file mode 100644 index 000000000000..40391c9f48e4 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs @@ -0,0 +1,41 @@ +// Targets Cortex-A7/A8/A9 processors (ARMv7-A) running NuttX with hardware floating point +// +// This target assumes that the device has a FPU (Floating Point Unit) +// and will use hardware floating point operations. This matches the NuttX EABI +// configuration with hardware floating point support. + +use crate::spec::{ + Cc, FloatAbi, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions, cvs, +}; + +pub(crate) fn target() -> Target { + let opts = TargetOptions { + abi: "eabihf".into(), + llvm_floatabi: Some(FloatAbi::Hard), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + features: "+v7,+thumb2,+vfp3,+neon,+strict-align".into(), + relocation_model: RelocModel::Static, + disable_redzone: true, + max_atomic_width: Some(64), + panic_strategy: PanicStrategy::Abort, + emit_debug_gdb_scripts: false, + c_enum_min_bits: Some(8), + families: cvs!["unix"], + os: "nuttx".into(), + ..Default::default() + }; + Target { + llvm_target: "armv7a-none-eabihf".into(), + metadata: crate::spec::TargetMetadata { + description: Some("ARMv7-A Cortex-A with NuttX (hard float)".into()), + tier: Some(3), + host_tools: Some(false), + std: Some(false), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + options: opts, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs new file mode 100644 index 000000000000..7fd22602e562 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs @@ -0,0 +1,33 @@ +// Targets Cortex-A7/A8/A9 processors (ARMv7-A) +// +// This target assumes that the device does NOT have a FPU (Floating Point Unit) +// and will use software floating point operations. This matches the NuttX EABI +// configuration without hardware floating point support. + +use crate::spec::{FloatAbi, Target, TargetOptions, base, cvs}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "thumbv7a-none-eabi".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "nuttx".into(), + abi: "eabi".into(), + llvm_floatabi: Some(FloatAbi::Soft), + // Cortex-A7/A8/A9 with software floating point + features: "+soft-float,-neon".into(), + max_atomic_width: Some(64), + ..base::thumb::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs new file mode 100644 index 000000000000..d3148c53a829 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs @@ -0,0 +1,37 @@ +// Targets Cortex-A7/A8/A9 processors (ARMv7-A) +// +// This target assumes that the device has a FPU (Floating Point Unit) and lowers all (single +// precision) floating point operations to hardware instructions. Cortex-A7/A8/A9 processors +// support VFPv3-D32 or VFPv4-D32 floating point units with optional double-precision support. +// +// This target uses the "hard" floating convention (ABI) where floating point values +// are passed to/from subroutines via FPU registers (S0, S1, D0, D1, etc.). + +use crate::spec::{FloatAbi, Target, TargetOptions, base, cvs}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "thumbv7a-none-eabihf".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), + arch: "arm".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "nuttx".into(), + abi: "eabihf".into(), + llvm_floatabi: Some(FloatAbi::Hard), + // Cortex-A7/A8/A9 support VFPv3-D32/VFPv4-D32 with optional double-precision + // and NEON SIMD instructions + features: "+vfp3,+neon".into(), + max_atomic_width: Some(64), + ..base::thumb::opts() + }, + } +} diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 1e9ef5e536c7..bcb6ac13b8fa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -435,6 +435,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { exp_found: Option>>, terr: TypeError<'tcx>, param_env: Option>, + path: &mut Option, ) { match *cause.code() { ObligationCauseCode::Pattern { @@ -457,6 +458,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { format!("this is an iterator with items of type `{}`", args.type_at(0)), ); } else { + let expected_ty = self.tcx.short_ty_string(expected_ty, path); err.span_label(span, format!("this expression has type `{expected_ty}`")); } } @@ -717,53 +719,47 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { value: &mut DiagStyledString, other_value: &mut DiagStyledString, name: String, - sub: ty::GenericArgsRef<'tcx>, + args: &[ty::GenericArg<'tcx>], pos: usize, other_ty: Ty<'tcx>, ) { // `value` and `other_value` hold two incomplete type representation for display. // `name` is the path of both types being compared. `sub` value.push_highlighted(name); - let len = sub.len(); - if len > 0 { - value.push_highlighted("<"); + + if args.is_empty() { + return; } + value.push_highlighted("<"); - // Output the lifetimes for the first type - let lifetimes = sub - .regions() - .map(|lifetime| { - let s = lifetime.to_string(); - if s.is_empty() { "'_".to_string() } else { s } - }) - .collect::>() - .join(", "); - if !lifetimes.is_empty() { - if sub.regions().count() < len { - value.push_normal(lifetimes + ", "); - } else { - value.push_normal(lifetimes); - } - } - - // Highlight all the type arguments that aren't at `pos` and compare the type argument at - // `pos` and `other_ty`. - for (i, type_arg) in sub.types().enumerate() { - if i == pos { - let values = self.cmp(type_arg, other_ty); - value.0.extend((values.0).0); - other_value.0.extend((values.1).0); - } else { - value.push_highlighted(type_arg.to_string()); - } - - if len > 0 && i != len - 1 { + for (i, arg) in args.iter().enumerate() { + if i > 0 { value.push_normal(", "); } + + match arg.unpack() { + ty::GenericArgKind::Lifetime(lt) => { + let s = lt.to_string(); + value.push_normal(if s.is_empty() { "'_" } else { &s }); + } + ty::GenericArgKind::Const(ct) => { + value.push_normal(ct.to_string()); + } + // Highlight all the type arguments that aren't at `pos` and compare + // the type argument at `pos` and `other_ty`. + ty::GenericArgKind::Type(type_arg) => { + if i == pos { + let values = self.cmp(type_arg, other_ty); + value.0.extend((values.0).0); + other_value.0.extend((values.1).0); + } else { + value.push_highlighted(type_arg.to_string()); + } + } + } } - if len > 0 { - value.push_highlighted(">"); - } + + value.push_highlighted(">"); } /// If `other_ty` is the same as a type argument present in `sub`, highlight `path` in `t1_out`, @@ -791,27 +787,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { t1_out: &mut DiagStyledString, t2_out: &mut DiagStyledString, path: String, - sub: &'tcx [ty::GenericArg<'tcx>], + args: &'tcx [ty::GenericArg<'tcx>], other_path: String, other_ty: Ty<'tcx>, - ) -> Option<()> { - // FIXME/HACK: Go back to `GenericArgsRef` to use its inherent methods, - // ideally that shouldn't be necessary. - let sub = self.tcx.mk_args(sub); - for (i, ta) in sub.types().enumerate() { - if ta == other_ty { - self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty); - return Some(()); - } - if let ty::Adt(def, _) = ta.kind() { - let path_ = self.tcx.def_path_str(def.did()); - if path_ == other_path { - self.highlight_outer(t1_out, t2_out, path, sub, i, other_ty); - return Some(()); + ) -> bool { + for (i, arg) in args.iter().enumerate() { + if let Some(ta) = arg.as_type() { + if ta == other_ty { + self.highlight_outer(t1_out, t2_out, path, args, i, other_ty); + return true; + } + if let ty::Adt(def, _) = ta.kind() { + let path_ = self.tcx.def_path_str(def.did()); + if path_ == other_path { + self.highlight_outer(t1_out, t2_out, path, args, i, other_ty); + return true; + } } } } - None + false } /// Adds a `,` to the type representation only if it is appropriate. @@ -819,10 +814,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, value: &mut DiagStyledString, other_value: &mut DiagStyledString, - len: usize, pos: usize, ) { - if len > 0 && pos != len - 1 { + if pos > 0 { value.push_normal(", "); other_value.push_normal(", "); } @@ -899,10 +893,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let len2 = sig2.inputs().len(); if len1 == len2 { for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() { + self.push_comma(&mut values.0, &mut values.1, i); let (x1, x2) = self.cmp(*l, *r); (values.0).0.extend(x1.0); (values.1).0.extend(x2.0); - self.push_comma(&mut values.0, &mut values.1, len1, i); } } else { for (i, l) in sig1.inputs().iter().enumerate() { @@ -1150,14 +1144,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let len1 = sub_no_defaults_1.len(); let len2 = sub_no_defaults_2.len(); let common_len = cmp::min(len1, len2); - let remainder1: Vec<_> = sub1.types().skip(common_len).collect(); - let remainder2: Vec<_> = sub2.types().skip(common_len).collect(); + let remainder1 = &sub1[common_len..]; + let remainder2 = &sub2[common_len..]; let common_default_params = iter::zip(remainder1.iter().rev(), remainder2.iter().rev()) .filter(|(a, b)| a == b) .count(); let len = sub1.len() - common_default_params; - let consts_offset = len - sub1.consts().count(); // Only draw `<...>` if there are lifetime/type arguments. if len > 0 { @@ -1169,70 +1162,68 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let s = lifetime.to_string(); if s.is_empty() { "'_".to_string() } else { s } } - // At one point we'd like to elide all lifetimes here, they are irrelevant for - // all diagnostics that use this output - // - // Foo<'x, '_, Bar> - // Foo<'y, '_, Qux> - // ^^ ^^ --- type arguments are not elided - // | | - // | elided as they were the same - // not elided, they were different, but irrelevant - // - // For bound lifetimes, keep the names of the lifetimes, - // even if they are the same so that it's clear what's happening - // if we have something like - // - // for<'r, 's> fn(Inv<'r>, Inv<'s>) - // for<'r> fn(Inv<'r>, Inv<'r>) - let lifetimes = sub1.regions().zip(sub2.regions()); - for (i, lifetimes) in lifetimes.enumerate() { - let l1 = lifetime_display(lifetimes.0); - let l2 = lifetime_display(lifetimes.1); - if lifetimes.0 != lifetimes.1 { - values.0.push_highlighted(l1); - values.1.push_highlighted(l2); - } else if lifetimes.0.is_bound() || self.tcx.sess.opts.verbose { - values.0.push_normal(l1); - values.1.push_normal(l2); - } else { - values.0.push_normal("'_"); - values.1.push_normal("'_"); - } - self.push_comma(&mut values.0, &mut values.1, len, i); - } - // We're comparing two types with the same path, so we compare the type - // arguments for both. If they are the same, do not highlight and elide from the - // output. - // Foo<_, Bar> - // Foo<_, Qux> - // ^ elided type as this type argument was the same in both sides - let type_arguments = sub1.types().zip(sub2.types()); - let regions_len = sub1.regions().count(); - let num_display_types = consts_offset - regions_len; - for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() { - let i = i + regions_len; - if ta1 == ta2 && !self.tcx.sess.opts.verbose { - values.0.push_normal("_"); - values.1.push_normal("_"); - } else { - recurse(ta1, ta2, &mut values); - } - self.push_comma(&mut values.0, &mut values.1, len, i); - } + for (i, (arg1, arg2)) in sub1.iter().zip(sub2).enumerate().take(len) { + self.push_comma(&mut values.0, &mut values.1, i); + match arg1.unpack() { + // At one point we'd like to elide all lifetimes here, they are + // irrelevant for all diagnostics that use this output. + // + // Foo<'x, '_, Bar> + // Foo<'y, '_, Qux> + // ^^ ^^ --- type arguments are not elided + // | | + // | elided as they were the same + // not elided, they were different, but irrelevant + // + // For bound lifetimes, keep the names of the lifetimes, + // even if they are the same so that it's clear what's happening + // if we have something like + // + // for<'r, 's> fn(Inv<'r>, Inv<'s>) + // for<'r> fn(Inv<'r>, Inv<'r>) + ty::GenericArgKind::Lifetime(l1) => { + let l1_str = lifetime_display(l1); + let l2 = arg2.expect_region(); + let l2_str = lifetime_display(l2); + if l1 != l2 { + values.0.push_highlighted(l1_str); + values.1.push_highlighted(l2_str); + } else if l1.is_bound() || self.tcx.sess.opts.verbose { + values.0.push_normal(l1_str); + values.1.push_normal(l2_str); + } else { + values.0.push_normal("'_"); + values.1.push_normal("'_"); + } + } + ty::GenericArgKind::Type(ta1) => { + let ta2 = arg2.expect_ty(); + if ta1 == ta2 && !self.tcx.sess.opts.verbose { + values.0.push_normal("_"); + values.1.push_normal("_"); + } else { + recurse(ta1, ta2, &mut values); + } + } + // We're comparing two types with the same path, so we compare the type + // arguments for both. If they are the same, do not highlight and elide + // from the output. + // Foo<_, Bar> + // Foo<_, Qux> + // ^ elided type as this type argument was the same in both sides - // Do the same for const arguments, if they are equal, do not highlight and - // elide them from the output. - let const_arguments = sub1.consts().zip(sub2.consts()); - for (i, (ca1, ca2)) in const_arguments.enumerate() { - let i = i + consts_offset; - maybe_highlight(ca1, ca2, &mut values, self.tcx); - self.push_comma(&mut values.0, &mut values.1, len, i); + // Do the same for const arguments, if they are equal, do not highlight and + // elide them from the output. + ty::GenericArgKind::Const(ca1) => { + let ca2 = arg2.expect_const(); + maybe_highlight(ca1, ca2, &mut values, self.tcx); + } + } } // Close the type argument bracket. - // Only draw `<...>` if there are lifetime/type arguments. + // Only draw `<...>` if there are arguments. if len > 0 { values.0.push_normal(">"); values.1.push_normal(">"); @@ -1244,17 +1235,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Foo // ------- this type argument is exactly the same as the other type // Bar - if self - .cmp_type_arg( - &mut values.0, - &mut values.1, - path1.clone(), - sub_no_defaults_1, - path2.clone(), - t2, - ) - .is_some() - { + if self.cmp_type_arg( + &mut values.0, + &mut values.1, + path1.clone(), + sub_no_defaults_1, + path2.clone(), + t2, + ) { return values; } // Check for case: @@ -1262,17 +1250,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Bar // Foo> // ------- this type argument is exactly the same as the other type - if self - .cmp_type_arg( - &mut values.1, - &mut values.0, - path2, - sub_no_defaults_2, - path1, - t1, - ) - .is_some() - { + if self.cmp_type_arg( + &mut values.1, + &mut values.0, + path2, + sub_no_defaults_2, + path1, + t1, + ) { return values; } @@ -1343,8 +1328,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let mut values = (DiagStyledString::normal("("), DiagStyledString::normal("(")); let len = args1.len(); for (i, (left, right)) in args1.iter().zip(args2).enumerate() { + self.push_comma(&mut values.0, &mut values.1, i); recurse(left, right, &mut values); - self.push_comma(&mut values.0, &mut values.1, len, i); } if len == 1 { // Keep the output for single element tuples as `(ty,)`. @@ -1611,7 +1596,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return; } - if let Some((expected, found, path)) = expected_found { + let mut path = None; + if let Some((expected, found, p)) = expected_found { + path = p; let (expected_label, found_label, exp_found) = match exp_found { Mismatch::Variable(ef) => ( ef.expected.prefix_string(self.tcx), @@ -1792,13 +1779,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &sort_string(values.expected), &sort_string(values.found), ); - if let Some(path) = path { - diag.note(format!( - "the full type name has been written to '{}'", - path.display(), - )); - diag.note("consider using `--verbose` to print the full type name to the console"); - } } } } @@ -1894,7 +1874,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // It reads better to have the error origin as the final // thing. - self.note_error_origin(diag, cause, exp_found, terr, param_env); + self.note_error_origin(diag, cause, exp_found, terr, param_env, &mut path); + if let Some(path) = path { + diag.note(format!("the full type name has been written to '{}'", path.display())); + diag.note("consider using `--verbose` to print the full type name to the console"); + } debug!(?diag); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs index 2a487a48a8e8..b9f3abc2534a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs @@ -1,8 +1,8 @@ use core::ops::ControlFlow; -use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::intravisit::{self, Visitor, VisitorExt}; +use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::middle::resolve_bound_vars as rbv; @@ -48,7 +48,7 @@ fn find_component_for_bound_region<'tcx>( region_def_id: DefId, ) -> Option<&'tcx hir::Ty<'tcx>> { FindNestedTypeVisitor { tcx, region_def_id, current_index: ty::INNERMOST } - .visit_ty(arg) + .visit_ty_unambig(arg) .break_value() } @@ -74,7 +74,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { self.tcx.hir() } - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { match arg.kind { hir::TyKind::BareFn(_) => { self.current_index.shift_in(1); @@ -101,7 +101,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { Some(rbv::ResolvedArg::EarlyBound(id)) => { debug!("EarlyBound id={:?}", id); if id.to_def_id() == self.region_def_id { - return ControlFlow::Break(arg); + return ControlFlow::Break(arg.as_unambig_ty()); } } @@ -117,7 +117,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { if debruijn_index == self.current_index && id.to_def_id() == self.region_def_id { - return ControlFlow::Break(arg); + return ControlFlow::Break(arg.as_unambig_ty()); } } @@ -147,7 +147,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { ) .is_break() { - ControlFlow::Break(arg) + ControlFlow::Break(arg.as_unambig_ty()) } else { ControlFlow::Continue(()) }; @@ -210,7 +210,7 @@ impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { ControlFlow::Continue(()) } - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) -> Self::Result { + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result { // ignore nested types // // If you have a type like `Foo<'a, &Ty>` we diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs index 54d8a9e25ca7..886581bc35fd 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mismatched_static_lifetime.rs @@ -4,7 +4,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::VisitorExt; use rustc_middle::bug; use rustc_middle::ty::TypeVisitor; use tracing::debug; @@ -87,7 +87,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { for matching_def_id in v.0 { let mut hir_v = super::static_impl_trait::HirTraitObjectVisitor(&mut traits, matching_def_id); - hir_v.visit_ty(impl_self_ty); + hir_v.visit_ty_unambig(impl_self_ty); } if traits.is_empty() { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index 503090b57971..dfbef39e9e18 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -3,9 +3,9 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, Subdiagnostic}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{Visitor, walk_ty}; +use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty}; use rustc_hir::{ - self as hir, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime, + self as hir, AmbigArg, GenericBound, GenericParam, GenericParamKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, MissingLifetimeKind, Node, TyKind, }; use rustc_middle::ty::{ @@ -153,7 +153,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let mut add_label = true; if let hir::FnRetTy::Return(ty) = fn_decl.output { let mut v = StaticLifetimeVisitor(vec![], tcx.hir()); - v.visit_ty(ty); + v.visit_ty_unambig(ty); if !v.0.is_empty() { span = v.0.clone().into(); spans = v.0; @@ -374,7 +374,7 @@ pub fn suggest_new_region_bound( } } } - TyKind::TraitObject(_, lt, _) => { + TyKind::TraitObject(_, lt) => { if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res { err.span_suggestion_verbose( fn_return.span.shrink_to_hi(), @@ -500,7 +500,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // In that case, only the first one will get suggestions. let mut traits = vec![]; let mut hir_v = HirTraitObjectVisitor(&mut traits, *did); - hir_v.visit_ty(self_ty); + hir_v.visit_ty_unambig(self_ty); !traits.is_empty() }) { @@ -560,7 +560,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { for found_did in found_dids { let mut traits = vec![]; let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); - hir_v.visit_ty(self_ty); + hir_v.visit_ty_unambig(self_ty); for &span in &traits { let subdiag = DynTraitConstraintSuggestion { span, ident }; subdiag.add_to_diag(err); @@ -591,12 +591,10 @@ impl<'tcx> TypeVisitor> for TraitObjectVisitor { pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec, pub DefId); impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { - fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { - if let TyKind::TraitObject( - poly_trait_refs, - Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. }, - _, - ) = t.kind + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) { + if let TyKind::TraitObject(poly_trait_refs, lifetime_ptr) = t.kind + && let Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. } = + lifetime_ptr.pointer() { for ptr in poly_trait_refs { if Some(self.1) == ptr.trait_ref.trait_def_id() { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs index 95dd1b28a399..bbcd28c08351 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs @@ -1,10 +1,10 @@ //! Error Reporting for `impl` items that do not match the obligations from their `trait`. use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{Visitor, walk_ty}; +use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::hir::nested_filter; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::ExpectedFound; @@ -137,11 +137,13 @@ impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { self.tcx.hir() } - fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx, AmbigArg>) { match arg.kind { hir::TyKind::Ref(_, ref mut_ty) => { // We don't want to suggest looking into borrowing `&T` or `&Self`. - hir::intravisit::walk_ty(self, mut_ty.ty); + if let Some(ambig_ty) = mut_ty.ty.try_as_ambig_ty() { + walk_ty(self, ambig_ty); + } return; } hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index 21124cf20e5d..af7e56961b72 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -655,7 +655,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && let ty::Ref(found_region, _, _) = found.kind() && expected_region.is_bound() && !found_region.is_bound() - && let hir::TyKind::Infer = arg_hir.kind + && let hir::TyKind::Infer(()) = arg_hir.kind { // If the expected region is late bound, the found region is not, and users are asking compiler // to infer the type, we can suggest adding `: &_`. diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index bcd3b0109b70..961719f263c5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -580,8 +580,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.tcx.hir_node_by_def_id(obligation.cause.body_id) && let hir::ItemKind::Impl(impl_) = item.kind && let None = impl_.of_trait - && let hir::TyKind::TraitObject(_, _, syntax) = impl_.self_ty.kind - && let TraitObjectSyntax::None = syntax + && let hir::TyKind::TraitObject(_, tagged_ptr) = impl_.self_ty.kind + && let TraitObjectSyntax::None = tagged_ptr.tag() && impl_.self_ty.span.edition().at_least_rust_2021() { // Silence the dyn-compatibility error in favor of the missing dyn on diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index f2bcc51e687b..8111983c5398 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{Applicability, Diag, E0038, E0276, MultiSpan, struct_span_code_err}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{self as hir, LangItem}; +use rustc_hir::{self as hir, AmbigArg, LangItem}; use rustc_infer::traits::{ DynCompatibilityViolation, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError, @@ -87,9 +87,9 @@ impl<'v> Visitor<'v> for FindExprBySpan<'v> { } } - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { if self.span == ty.span { - self.ty_result = Some(ty); + self.ty_result = Some(ty.as_unambig_ty()); } else { hir::intravisit::walk_ty(self, ty); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 4669d2866653..471105773e2b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -14,14 +14,13 @@ use rustc_errors::{ Applicability, Diag, EmissionGuarantee, MultiSpan, Style, SuggestionStyle, pluralize, struct_span_code_err, }; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; +use rustc_hir::intravisit::{Visitor, VisitorExt}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, expr_needs_parens, - is_range_literal, + self as hir, AmbigArg, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node, + expr_needs_parens, is_range_literal, }; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk}; use rustc_middle::hir::map; @@ -179,7 +178,7 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( let mut ty_spans = vec![]; for input in fn_sig.decl.inputs { ReplaceImplTraitVisitor { ty_spans: &mut ty_spans, param_did: param.def_id } - .visit_ty(input); + .visit_ty_unambig(input); } // The type param `T: Trait` we will suggest to introduce. let type_param = format!("{type_param_name}: {bound_str}"); @@ -3074,7 +3073,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if let Some(ty) = ty { match ty.kind { - hir::TyKind::TraitObject(traits, _, _) => { + hir::TyKind::TraitObject(traits, _) => { let (span, kw) = match traits { [first, ..] if first.span.lo() == ty.span.lo() => { // Missing `dyn` in front of trait object. @@ -5065,7 +5064,7 @@ pub struct SelfVisitor<'v> { } impl<'v> Visitor<'v> for SelfVisitor<'v> { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { if let hir::TyKind::Path(path) = ty.kind && let hir::QPath::TypeRelative(inner_ty, segment) = path && (Some(segment.ident.name) == self.name || self.name.is_none()) @@ -5073,7 +5072,7 @@ impl<'v> Visitor<'v> for SelfVisitor<'v> { && let hir::QPath::Resolved(None, inner_path) = inner_path && let Res::SelfTyAlias { .. } = inner_path.res { - self.paths.push(ty); + self.paths.push(ty.as_unambig_ty()); } hir::intravisit::walk_ty(self, ty); } @@ -5187,7 +5186,7 @@ struct ReplaceImplTraitVisitor<'a> { } impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { - fn visit_ty(&mut self, t: &'hir hir::Ty<'hir>) { + fn visit_ty(&mut self, t: &'hir hir::Ty<'hir, AmbigArg>) { if let hir::TyKind::Path(hir::QPath::Resolved( None, hir::Path { res: Res::Def(_, segment_did), .. }, @@ -5480,7 +5479,7 @@ impl<'v> Visitor<'v> for FindTypeParam { // Skip where-clauses, to avoid suggesting indirection for type parameters found there. } - fn visit_ty(&mut self, ty: &hir::Ty<'_>) { + fn visit_ty(&mut self, ty: &hir::Ty<'_, AmbigArg>) { // We collect the spans of all uses of the "bare" type param, like in `field: T` or // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized` diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 0bf91ad35c15..2dfa72972ba1 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -6,11 +6,10 @@ use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, MultiSpan, SubdiagMessageOp, Subdiagnostic, }; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{Visitor, walk_ty}; -use rustc_hir::{FnRetTy, GenericParamKind, Node}; +use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty}; +use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath}; use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, TyCtxt}; @@ -579,7 +578,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { } impl<'v> Visitor<'v> for ImplicitLifetimeFinder { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) { let make_suggestion = |ident: Ident| { if ident.name == kw::Empty && ident.span.is_empty() { format!("{}, ", self.suggestion_param_name) @@ -642,16 +641,16 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { if let Some(fn_decl) = node.fn_decl() && let hir::FnRetTy::Return(ty) = fn_decl.output { - visitor.visit_ty(ty); + visitor.visit_ty_unambig(ty); } if visitor.suggestions.is_empty() { // Do not suggest constraining the `&self` param, but rather the return type. // If that is wrong (because it is not sufficient), a follow up error will tell the // user to fix it. This way we lower the chances of *over* constraining, but still // get the cake of "correctly" contrained in two steps. - visitor.visit_ty(self.ty_sup); + visitor.visit_ty_unambig(self.ty_sup); } - visitor.visit_ty(self.ty_sub); + visitor.visit_ty_unambig(self.ty_sub); if visitor.suggestions.is_empty() { return false; } diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 4004e354dc18..26ba1511b540 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -6,8 +6,7 @@ use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; use crate::traits::query::NoSolution; -use crate::traits::query::normalize::QueryNormalizeExt; -use crate::traits::{Normalized, ObligationCause, ObligationCtxt}; +use crate::traits::{ObligationCause, ObligationCtxt}; /// This returns true if the type `ty` is "trivial" for /// dropck-outlives -- that is, if it doesn't require any types to @@ -172,13 +171,18 @@ pub fn compute_dropck_outlives_inner<'tcx>( // do not themselves define a destructor", more or less. We have // to push them onto the stack to be expanded. for ty in constraints.dtorck_types.drain(..) { - let Normalized { value: ty, obligations } = - ocx.infcx.at(&cause, param_env).query_normalize(ty)?; - ocx.register_obligations(obligations); + let normalized_ty = ocx.normalize(&cause, param_env, ty); - debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); + let errors = ocx.select_where_possible(); + if !errors.is_empty() { + debug!("failed to normalize dtorck type: {ty} ~> {errors:#?}"); + return Err(NoSolution); + } - match ty.kind() { + let normalized_ty = ocx.infcx.resolve_vars_if_possible(normalized_ty); + debug!("dropck_outlives: ty from dtorck_types = {:?}", normalized_ty); + + match normalized_ty.kind() { // All parameters live for the duration of the // function. ty::Param(..) => {} @@ -186,12 +190,12 @@ pub fn compute_dropck_outlives_inner<'tcx>( // A projection that we couldn't resolve - it // might have a destructor. ty::Alias(..) => { - result.kinds.push(ty.into()); + result.kinds.push(normalized_ty.into()); } _ => { - if ty_set.insert(ty) { - ty_stack.push((ty, depth + 1)); + if ty_set.insert(normalized_ty) { + ty_stack.push((normalized_ty, depth + 1)); } } } diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index 71088a598bb9..5d041c2623aa 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -6,13 +6,12 @@ use rustc_middle::query::Providers; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{Clause, FnSig, ParamEnvAnd, PolyFnSig, Ty, TyCtxt, TypeFoldable}; use rustc_trait_selection::infer::InferCtxtBuilderExt; -use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::query::type_op::ascribe_user_type::{ AscribeUserType, type_op_ascribe_user_type_with_span, }; use rustc_trait_selection::traits::query::type_op::normalize::Normalize; use rustc_trait_selection::traits::query::type_op::prove_predicate::ProvePredicate; -use rustc_trait_selection::traits::{Normalized, Obligation, ObligationCause, ObligationCtxt}; +use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; pub(crate) fn provide(p: &mut Providers) { *p = Providers { @@ -43,10 +42,7 @@ where T: fmt::Debug + TypeFoldable>, { let (param_env, Normalize { value }) = key.into_parts(); - let Normalized { value, obligations } = - ocx.infcx.at(&ObligationCause::dummy(), param_env).query_normalize(value)?; - ocx.register_obligations(obligations); - Ok(value) + Ok(ocx.normalize(&ObligationCause::dummy(), param_env, value)) } fn type_op_normalize_ty<'tcx>( diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 8f9f66db1bdd..f71b924b177a 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::FxIndexSet; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{self as hir, AmbigArg}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; use rustc_middle::{bug, span_bug}; @@ -187,7 +187,7 @@ fn associated_types_for_impl_traits_in_associated_fn( } impl<'tcx> Visitor<'tcx> for RPITVisitor { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { + fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { if let hir::TyKind::OpaqueDef(opaq) = ty.kind && self.rpits.insert(opaq.def_id) { diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 0b6a55297e1a..1c33f8f60d82 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1735,6 +1735,52 @@ impl VecDeque { } } + /// Removes and returns the first element from the deque if the predicate + /// returns `true`, or [`None`] if the predicate returns false or the deque + /// is empty (the predicate will not be called in that case). + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_deque_pop_if)] + /// use std::collections::VecDeque; + /// + /// let mut deque: VecDeque = vec![0, 1, 2, 3, 4].into(); + /// let pred = |x: &mut i32| *x % 2 == 0; + /// + /// assert_eq!(deque.pop_front_if(pred), Some(0)); + /// assert_eq!(deque, [1, 2, 3, 4]); + /// assert_eq!(deque.pop_front_if(pred), None); + /// ``` + #[unstable(feature = "vec_deque_pop_if", issue = "135889")] + pub fn pop_front_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { + let first = self.front_mut()?; + if predicate(first) { self.pop_front() } else { None } + } + + /// Removes and returns the last element from the deque if the predicate + /// returns `true`, or [`None`] if the predicate returns false or the deque + /// is empty (the predicate will not be called in that case). + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_deque_pop_if)] + /// use std::collections::VecDeque; + /// + /// let mut deque: VecDeque = vec![0, 1, 2, 3, 4].into(); + /// let pred = |x: &mut i32| *x % 2 == 0; + /// + /// assert_eq!(deque.pop_back_if(pred), Some(4)); + /// assert_eq!(deque, [0, 1, 2, 3]); + /// assert_eq!(deque.pop_back_if(pred), None); + /// ``` + #[unstable(feature = "vec_deque_pop_if", issue = "135889")] + pub fn pop_back_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { + let first = self.back_mut()?; + if predicate(first) { self.pop_back() } else { None } + } + /// Prepends an element to the deque. /// /// # Examples diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index cd2afd7a4731..54673ceb1da8 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2511,9 +2511,9 @@ impl Vec { } } - /// Removes and returns the last element in a vector if the predicate + /// Removes and returns the last element from a vector if the predicate /// returns `true`, or [`None`] if the predicate returns false or the vector - /// is empty. + /// is empty (the predicate will not be called in that case). /// /// # Examples /// @@ -2528,12 +2528,9 @@ impl Vec { /// assert_eq!(vec.pop_if(pred), None); /// ``` #[unstable(feature = "vec_pop_if", issue = "122741")] - pub fn pop_if(&mut self, f: F) -> Option - where - F: FnOnce(&mut T) -> bool, - { + pub fn pop_if(&mut self, predicate: impl FnOnce(&mut T) -> bool) -> Option { let last = self.last_mut()?; - if f(last) { self.pop() } else { None } + if predicate(last) { self.pop() } else { None } } /// Moves all the elements of `other` into `self`, leaving `other` empty. @@ -2574,9 +2571,11 @@ impl Vec { self.len += count; } - /// Removes the specified range from the vector in bulk, returning all - /// removed elements as an iterator. If the iterator is dropped before - /// being fully consumed, it drops the remaining removed elements. + /// Removes the subslice indicated by the given range from the vector, + /// returning a double-ended iterator over the removed subslice. + /// + /// If the iterator is dropped before being fully consumed, + /// it drops the remaining removed elements. /// /// The returned iterator keeps a mutable borrow on the vector to optimize /// its implementation. @@ -3016,10 +3015,9 @@ impl Vec { /// Iterates over the slice `other`, clones each element, and then appends /// it to this `Vec`. The `other` slice is traversed in-order. /// - /// Note that this function is same as [`extend`] except that it is - /// specialized to work with slices instead. If and when Rust gets - /// specialization this function will likely be deprecated (but still - /// available). + /// Note that this function is the same as [`extend`], + /// except that it also works with slice elements that are Clone but not Copy. + /// If Rust gets specialization this function may be deprecated. /// /// # Examples /// diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs index 393bdfe48b74..d8364d750fa8 100644 --- a/library/alloc/tests/lib.rs +++ b/library/alloc/tests/lib.rs @@ -38,6 +38,7 @@ #![feature(str_as_str)] #![feature(strict_provenance_lints)] #![feature(vec_pop_if)] +#![feature(vec_deque_pop_if)] #![feature(unique_rc_arc)] #![feature(macro_metavar_expr_concat)] #![allow(internal_features)] diff --git a/library/alloc/tests/vec_deque.rs b/library/alloc/tests/vec_deque.rs index 4b8d3c735f72..1b03c29e5bda 100644 --- a/library/alloc/tests/vec_deque.rs +++ b/library/alloc/tests/vec_deque.rs @@ -80,6 +80,45 @@ fn test_parameterized(a: T, b: T, c: T, d: T) { assert_eq!(deq[3].clone(), d.clone()); } +#[test] +fn test_pop_if() { + let mut deq: VecDeque<_> = vec![0, 1, 2, 3, 4].into(); + let pred = |x: &mut i32| *x % 2 == 0; + + assert_eq!(deq.pop_front_if(pred), Some(0)); + assert_eq!(deq, [1, 2, 3, 4]); + + assert_eq!(deq.pop_front_if(pred), None); + assert_eq!(deq, [1, 2, 3, 4]); + + assert_eq!(deq.pop_back_if(pred), Some(4)); + assert_eq!(deq, [1, 2, 3]); + + assert_eq!(deq.pop_back_if(pred), None); + assert_eq!(deq, [1, 2, 3]); +} + +#[test] +fn test_pop_if_empty() { + let mut deq = VecDeque::::new(); + assert_eq!(deq.pop_front_if(|_| true), None); + assert_eq!(deq.pop_back_if(|_| true), None); + assert!(deq.is_empty()); +} + +#[test] +fn test_pop_if_mutates() { + let mut v: VecDeque<_> = vec![-1, 1].into(); + let pred = |x: &mut i32| { + *x *= 2; + false + }; + assert_eq!(v.pop_front_if(pred), None); + assert_eq!(v, [-2, 1]); + assert_eq!(v.pop_back_if(pred), None); + assert_eq!(v, [-2, 2]); +} + #[test] fn test_push_front_grow() { let mut deq = VecDeque::new(); diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index ba61679564a6..28329bb09084 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -156,7 +156,6 @@ pub const fn from_mut(s: &mut T) -> &mut [T; 1] { /// The error type returned when a conversion from a slice to an array fails. #[stable(feature = "try_from", since = "1.34.0")] -#[rustc_allowed_through_unstable_modules] #[derive(Debug, Copy, Clone)] pub struct TryFromSliceError(()); diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index dbce64420ac4..8089d6164090 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -90,6 +90,26 @@ impl_zeroable_primitive!( /// /// assert_eq!(size_of::>>(), size_of::()); /// ``` +/// +/// # Layout +/// +/// `NonZero` is guaranteed to have the same layout and bit validity as `T` +/// with the exception that the all-zero bit pattern is invalid. +/// `Option>` is guaranteed to be compatible with `T`, including in +/// FFI. +/// +/// Thanks to the [null pointer optimization], `NonZero` and +/// `Option>` are guaranteed to have the same size and alignment: +/// +/// ``` +/// # use std::mem::{size_of, align_of}; +/// use std::num::NonZero; +/// +/// assert_eq!(size_of::>(), size_of::>>()); +/// assert_eq!(align_of::>(), align_of::>>()); +/// ``` +/// +/// [null pointer optimization]: crate::option#representation #[stable(feature = "generic_nonzero", since = "1.79.0")] #[repr(transparent)] #[rustc_nonnull_optimization_guaranteed] diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 71044190f0c8..2a0bf89fcf7a 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -156,8 +156,8 @@ //! //! In order to implement the second option, we must in some way enforce its key invariant, //! *i.e.* prevent the value from being *moved* or otherwise invalidated (you may notice this -//! sounds an awful lot like the definition of *pinning* a value). There a few ways one might be -//! able to enforce this invariant in Rust: +//! sounds an awful lot like the definition of *pinning* a value). There are a few ways one might +//! be able to enforce this invariant in Rust: //! //! 1. Offer a wholly `unsafe` API to interact with the object, thus requiring every caller to //! uphold the invariant themselves diff --git a/library/proc_macro/src/bridge/closure.rs b/library/proc_macro/src/bridge/closure.rs index d371ae3cea09..524fdf53d6b7 100644 --- a/library/proc_macro/src/bridge/closure.rs +++ b/library/proc_macro/src/bridge/closure.rs @@ -3,7 +3,7 @@ use std::marker::PhantomData; #[repr(C)] -pub struct Closure<'a, A, R> { +pub(super) struct Closure<'a, A, R> { call: unsafe extern "C" fn(*mut Env, A) -> R, env: *mut Env, // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing @@ -26,7 +26,7 @@ impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> { } impl<'a, A, R> Closure<'a, A, R> { - pub fn call(&mut self, arg: A) -> R { + pub(super) fn call(&mut self, arg: A) -> R { unsafe { (self.call)(self.env, arg) } } } diff --git a/library/proc_macro/src/bridge/fxhash.rs b/library/proc_macro/src/bridge/fxhash.rs index 3345e099a372..5f6b3d1b929e 100644 --- a/library/proc_macro/src/bridge/fxhash.rs +++ b/library/proc_macro/src/bridge/fxhash.rs @@ -9,7 +9,7 @@ use std::hash::{BuildHasherDefault, Hasher}; use std::ops::BitXor; /// Type alias for a hashmap using the `fx` hash algorithm. -pub type FxHashMap = HashMap>; +pub(super) type FxHashMap = HashMap>; /// A speedy hash algorithm for use within rustc. The hashmap in alloc by /// default uses SipHash which isn't quite as speedy as we want. In the compiler @@ -23,7 +23,7 @@ pub type FxHashMap = HashMap>; /// similar or slightly worse than FNV, but the speed of the hash function /// itself is much higher because it works on up to 8 bytes at a time. #[derive(Default)] -pub struct FxHasher { +pub(super) struct FxHasher { hash: usize, } diff --git a/library/proc_macro/src/bridge/rpc.rs b/library/proc_macro/src/bridge/rpc.rs index 202a8e04543b..85fd7d138585 100644 --- a/library/proc_macro/src/bridge/rpc.rs +++ b/library/proc_macro/src/bridge/rpc.rs @@ -67,7 +67,7 @@ macro_rules! rpc_encode_decode { mod tag { #[repr(u8)] enum Tag { $($variant),* } - $(pub const $variant: u8 = Tag::$variant as u8;)* + $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* } match self { @@ -89,7 +89,7 @@ macro_rules! rpc_encode_decode { mod tag { #[repr(u8)] enum Tag { $($variant),* } - $(pub const $variant: u8 = Tag::$variant as u8;)* + $(pub(crate) const $variant: u8 = Tag::$variant as u8;)* } match u8::decode(r, s) { diff --git a/library/proc_macro/src/bridge/selfless_reify.rs b/library/proc_macro/src/bridge/selfless_reify.rs index 907ad256e4b4..312a79152e23 100644 --- a/library/proc_macro/src/bridge/selfless_reify.rs +++ b/library/proc_macro/src/bridge/selfless_reify.rs @@ -44,7 +44,7 @@ macro_rules! define_reify_functions { fn $name:ident $(<$($param:ident),*>)? for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty; )+) => { - $(pub const fn $name< + $(pub(super) const fn $name< $($($param,)*)? F: Fn($($arg_ty),*) -> $ret_ty + Copy >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index b19c9cee75a0..6611ce30a1b0 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -32,6 +32,7 @@ #![allow(internal_features)] #![deny(ffi_unwind_calls)] #![warn(rustdoc::unescaped_backticks)] +#![warn(unreachable_pub)] #[unstable(feature = "proc_macro_internals", issue = "27812")] #[doc(hidden)] diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 9b752ed14437..1f8aac48a9a8 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2529,6 +2529,7 @@ pub fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { /// limited to just these cases: /// /// * The `original` path is not a file or doesn't exist. +/// * The 'link' path already exists. /// /// # Examples /// diff --git a/library/test/src/cli.rs b/library/test/src/cli.rs index 4ccd825bf8dd..52c809176234 100644 --- a/library/test/src/cli.rs +++ b/library/test/src/cli.rs @@ -44,7 +44,7 @@ impl TestOpts { } /// Result of parsing the options. -pub type OptRes = Result; +pub(crate) type OptRes = Result; /// Result of parsing the option part. type OptPartRes = Result; diff --git a/library/test/src/console.rs b/library/test/src/console.rs index 4d4cdcf4d7b6..024ef48fc510 100644 --- a/library/test/src/console.rs +++ b/library/test/src/console.rs @@ -20,7 +20,7 @@ use super::types::{NamePadding, TestDesc, TestDescAndFn}; use super::{filter_tests, run_tests, term}; /// Generic wrapper over stdout. -pub enum OutputLocation { +pub(crate) enum OutputLocation { Pretty(Box), Raw(T), } @@ -41,7 +41,7 @@ impl Write for OutputLocation { } } -pub struct ConsoleTestDiscoveryState { +pub(crate) struct ConsoleTestDiscoveryState { pub log_out: Option, pub tests: usize, pub benchmarks: usize, @@ -49,7 +49,7 @@ pub struct ConsoleTestDiscoveryState { } impl ConsoleTestDiscoveryState { - pub fn new(opts: &TestOpts) -> io::Result { + pub(crate) fn new(opts: &TestOpts) -> io::Result { let log_out = match opts.logfile { Some(ref path) => Some(File::create(path)?), None => None, @@ -58,7 +58,7 @@ impl ConsoleTestDiscoveryState { Ok(ConsoleTestDiscoveryState { log_out, tests: 0, benchmarks: 0, ignored: 0 }) } - pub fn write_log(&mut self, msg: F) -> io::Result<()> + pub(crate) fn write_log(&mut self, msg: F) -> io::Result<()> where S: AsRef, F: FnOnce() -> S, @@ -74,7 +74,7 @@ impl ConsoleTestDiscoveryState { } } -pub struct ConsoleTestState { +pub(crate) struct ConsoleTestState { pub log_out: Option, pub total: usize, pub passed: usize, @@ -92,7 +92,7 @@ pub struct ConsoleTestState { } impl ConsoleTestState { - pub fn new(opts: &TestOpts) -> io::Result { + pub(crate) fn new(opts: &TestOpts) -> io::Result { let log_out = match opts.logfile { Some(ref path) => Some(File::create(path)?), None => None, @@ -116,7 +116,7 @@ impl ConsoleTestState { }) } - pub fn write_log(&mut self, msg: F) -> io::Result<()> + pub(crate) fn write_log(&mut self, msg: F) -> io::Result<()> where S: AsRef, F: FnOnce() -> S, @@ -131,7 +131,7 @@ impl ConsoleTestState { } } - pub fn write_log_result( + pub(crate) fn write_log_result( &mut self, test: &TestDesc, result: &TestResult, @@ -170,7 +170,7 @@ impl ConsoleTestState { } // List the tests to console, and optionally to logfile. Filters are honored. -pub fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { +pub(crate) fn list_tests_console(opts: &TestOpts, tests: Vec) -> io::Result<()> { let output = match term::stdout() { None => OutputLocation::Raw(io::stdout().lock()), Some(t) => OutputLocation::Pretty(t), diff --git a/library/test/src/formatters/json.rs b/library/test/src/formatters/json.rs index aa1c50641cb5..92c1c0716f1f 100644 --- a/library/test/src/formatters/json.rs +++ b/library/test/src/formatters/json.rs @@ -13,7 +13,7 @@ pub(crate) struct JsonFormatter { } impl JsonFormatter { - pub fn new(out: OutputLocation) -> Self { + pub(crate) fn new(out: OutputLocation) -> Self { Self { out } } diff --git a/library/test/src/formatters/junit.rs b/library/test/src/formatters/junit.rs index 96b432008404..57b1b0feceef 100644 --- a/library/test/src/formatters/junit.rs +++ b/library/test/src/formatters/junit.rs @@ -8,13 +8,13 @@ use crate::test_result::TestResult; use crate::time; use crate::types::{TestDesc, TestType}; -pub struct JunitFormatter { +pub(crate) struct JunitFormatter { out: OutputLocation, results: Vec<(TestDesc, TestResult, Duration, Vec)>, } impl JunitFormatter { - pub fn new(out: OutputLocation) -> Self { + pub(crate) fn new(out: OutputLocation) -> Self { Self { out, results: Vec::new() } } diff --git a/library/test/src/formatters/pretty.rs b/library/test/src/formatters/pretty.rs index 7089eae4330a..bf3fc40db411 100644 --- a/library/test/src/formatters/pretty.rs +++ b/library/test/src/formatters/pretty.rs @@ -20,7 +20,7 @@ pub(crate) struct PrettyFormatter { } impl PrettyFormatter { - pub fn new( + pub(crate) fn new( out: OutputLocation, use_color: bool, max_name_len: usize, @@ -31,19 +31,19 @@ impl PrettyFormatter { } #[cfg(test)] - pub fn output_location(&self) -> &OutputLocation { + pub(crate) fn output_location(&self) -> &OutputLocation { &self.out } - pub fn write_ok(&mut self) -> io::Result<()> { + pub(crate) fn write_ok(&mut self) -> io::Result<()> { self.write_short_result("ok", term::color::GREEN) } - pub fn write_failed(&mut self) -> io::Result<()> { + pub(crate) fn write_failed(&mut self) -> io::Result<()> { self.write_short_result("FAILED", term::color::RED) } - pub fn write_ignored(&mut self, message: Option<&'static str>) -> io::Result<()> { + pub(crate) fn write_ignored(&mut self, message: Option<&'static str>) -> io::Result<()> { if let Some(message) = message { self.write_short_result(&format!("ignored, {message}"), term::color::YELLOW) } else { @@ -51,15 +51,15 @@ impl PrettyFormatter { } } - pub fn write_time_failed(&mut self) -> io::Result<()> { + pub(crate) fn write_time_failed(&mut self) -> io::Result<()> { self.write_short_result("FAILED (time limit exceeded)", term::color::RED) } - pub fn write_bench(&mut self) -> io::Result<()> { + pub(crate) fn write_bench(&mut self) -> io::Result<()> { self.write_pretty("bench", term::color::CYAN) } - pub fn write_short_result( + pub(crate) fn write_short_result( &mut self, result: &str, color: term::color::Color, @@ -67,7 +67,7 @@ impl PrettyFormatter { self.write_pretty(result, color) } - pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { + pub(crate) fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { match self.out { OutputLocation::Pretty(ref mut term) => { if self.use_color { @@ -86,7 +86,7 @@ impl PrettyFormatter { } } - pub fn write_plain>(&mut self, s: S) -> io::Result<()> { + pub(crate) fn write_plain>(&mut self, s: S) -> io::Result<()> { let s = s.as_ref(); self.out.write_all(s.as_bytes())?; self.out.flush() @@ -154,15 +154,15 @@ impl PrettyFormatter { Ok(()) } - pub fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_successes(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.not_failures, "successes") } - pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.failures, "failures") } - pub fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_time_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_results(&state.time_failures, "failures (time limit exceeded)") } diff --git a/library/test/src/formatters/terse.rs b/library/test/src/formatters/terse.rs index 534aa2f33110..b28120ab56e6 100644 --- a/library/test/src/formatters/terse.rs +++ b/library/test/src/formatters/terse.rs @@ -25,7 +25,7 @@ pub(crate) struct TerseFormatter { } impl TerseFormatter { - pub fn new( + pub(crate) fn new( out: OutputLocation, use_color: bool, max_name_len: usize, @@ -42,11 +42,11 @@ impl TerseFormatter { } } - pub fn write_ok(&mut self) -> io::Result<()> { + pub(crate) fn write_ok(&mut self) -> io::Result<()> { self.write_short_result(".", term::color::GREEN) } - pub fn write_failed(&mut self, name: &str) -> io::Result<()> { + pub(crate) fn write_failed(&mut self, name: &str) -> io::Result<()> { // Put failed tests on their own line and include the test name, so that it's faster // to see which test failed without having to wait for them all to run. @@ -62,15 +62,15 @@ impl TerseFormatter { self.write_plain("\n") } - pub fn write_ignored(&mut self) -> io::Result<()> { + pub(crate) fn write_ignored(&mut self) -> io::Result<()> { self.write_short_result("i", term::color::YELLOW) } - pub fn write_bench(&mut self) -> io::Result<()> { + pub(crate) fn write_bench(&mut self) -> io::Result<()> { self.write_pretty("bench", term::color::CYAN) } - pub fn write_short_result( + pub(crate) fn write_short_result( &mut self, result: &str, color: term::color::Color, @@ -95,7 +95,7 @@ impl TerseFormatter { Ok(()) } - pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { + pub(crate) fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> { match self.out { OutputLocation::Pretty(ref mut term) => { if self.use_color { @@ -114,13 +114,13 @@ impl TerseFormatter { } } - pub fn write_plain>(&mut self, s: S) -> io::Result<()> { + pub(crate) fn write_plain>(&mut self, s: S) -> io::Result<()> { let s = s.as_ref(); self.out.write_all(s.as_bytes())?; self.out.flush() } - pub fn write_outputs(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_outputs(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_plain("\nsuccesses:\n")?; let mut successes = Vec::new(); let mut stdouts = String::new(); @@ -146,7 +146,7 @@ impl TerseFormatter { Ok(()) } - pub fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { + pub(crate) fn write_failures(&mut self, state: &ConsoleTestState) -> io::Result<()> { self.write_plain("\nfailures:\n")?; let mut failures = Vec::new(); let mut fail_out = String::new(); diff --git a/library/test/src/helpers/concurrency.rs b/library/test/src/helpers/concurrency.rs index b1545cbec438..6648b669125f 100644 --- a/library/test/src/helpers/concurrency.rs +++ b/library/test/src/helpers/concurrency.rs @@ -4,7 +4,7 @@ use std::num::NonZero; use std::{env, thread}; -pub fn get_concurrency() -> usize { +pub(crate) fn get_concurrency() -> usize { if let Ok(value) = env::var("RUST_TEST_THREADS") { match value.parse::>().ok() { Some(n) => n.get(), diff --git a/library/test/src/helpers/mod.rs b/library/test/src/helpers/mod.rs index 3c79b90b1675..2fb29b4c7bee 100644 --- a/library/test/src/helpers/mod.rs +++ b/library/test/src/helpers/mod.rs @@ -1,6 +1,6 @@ //! Module with common helpers not directly related to tests //! but used in `libtest`. -pub mod concurrency; -pub mod metrics; -pub mod shuffle; +pub(crate) mod concurrency; +pub(crate) mod metrics; +pub(crate) mod shuffle; diff --git a/library/test/src/helpers/shuffle.rs b/library/test/src/helpers/shuffle.rs index 14389eb0e37a..53d1d0e42d4e 100644 --- a/library/test/src/helpers/shuffle.rs +++ b/library/test/src/helpers/shuffle.rs @@ -4,7 +4,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use crate::cli::TestOpts; use crate::types::{TestDescAndFn, TestId, TestName}; -pub fn get_shuffle_seed(opts: &TestOpts) -> Option { +pub(crate) fn get_shuffle_seed(opts: &TestOpts) -> Option { opts.shuffle_seed.or_else(|| { if opts.shuffle { Some( @@ -19,7 +19,7 @@ pub fn get_shuffle_seed(opts: &TestOpts) -> Option { }) } -pub fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) { +pub(crate) fn shuffle_tests(shuffle_seed: u64, tests: &mut [(TestId, TestDescAndFn)]) { let test_names: Vec<&TestName> = tests.iter().map(|test| &test.1.desc.name).collect(); let test_names_hash = calculate_hash(&test_names); let mut rng = Rng::new(shuffle_seed, test_names_hash); diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 47407df909bd..54f7e4ae79f1 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -27,6 +27,7 @@ #![feature(thread_spawn_hook)] #![allow(internal_features)] #![warn(rustdoc::unescaped_backticks)] +#![warn(unreachable_pub)] pub use cli::TestOpts; diff --git a/library/test/src/options.rs b/library/test/src/options.rs index 3eaad59474a1..7a5c55f4e241 100644 --- a/library/test/src/options.rs +++ b/library/test/src/options.rs @@ -2,7 +2,7 @@ /// Number of times to run a benchmarked function #[derive(Clone, PartialEq, Eq)] -pub enum BenchMode { +pub(crate) enum BenchMode { Auto, Single, } diff --git a/library/test/src/stats/tests.rs b/library/test/src/stats/tests.rs index 4b209dcf214d..7804ddc92913 100644 --- a/library/test/src/stats/tests.rs +++ b/library/test/src/stats/tests.rs @@ -573,13 +573,13 @@ fn test_sum_f64_between_ints_that_sum_to_0() { } #[bench] -pub fn sum_three_items(b: &mut Bencher) { +fn sum_three_items(b: &mut Bencher) { b.iter(|| { [1e20f64, 1.5f64, -1e20f64].sum(); }) } #[bench] -pub fn sum_many_f64(b: &mut Bencher) { +fn sum_many_f64(b: &mut Bencher) { let nums = [-1e30f64, 1e60, 1e30, 1.0, -1e60]; let v = (0..500).map(|i| nums[i % 5]).collect::>(); @@ -589,4 +589,4 @@ pub fn sum_many_f64(b: &mut Bencher) { } #[bench] -pub fn no_iter(_: &mut Bencher) {} +fn no_iter(_: &mut Bencher) {} diff --git a/library/test/src/term.rs b/library/test/src/term.rs index e736e85d4696..d9880a776406 100644 --- a/library/test/src/term.rs +++ b/library/test/src/term.rs @@ -62,7 +62,7 @@ pub(crate) mod color { /// A terminal with similar capabilities to an ANSI Terminal /// (foreground/background colors etc). -pub trait Terminal: Write { +pub(crate) trait Terminal: Write { /// Sets the foreground color to the given color. /// /// If the color is a bright color, but the terminal only supports 8 colors, diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs index 79fe07bc1ac5..73dcc2e2a0cc 100644 --- a/library/test/src/test_result.rs +++ b/library/test/src/test_result.rs @@ -12,7 +12,7 @@ use super::types::TestDesc; // Return code for secondary process. // Start somewhere other than 0 so we know the return code means what we think // it means. -pub const TR_OK: i32 = 50; +pub(crate) const TR_OK: i32 = 50; // On Windows we use __fastfail to abort, which is documented to use this // exception code. @@ -39,7 +39,7 @@ pub enum TestResult { /// Creates a `TestResult` depending on the raw result of test execution /// and associated data. -pub fn calc_result<'a>( +pub(crate) fn calc_result<'a>( desc: &TestDesc, task_result: Result<(), &'a (dyn Any + 'static + Send)>, time_opts: Option<&time::TestTimeOptions>, @@ -93,7 +93,7 @@ pub fn calc_result<'a>( } /// Creates a `TestResult` depending on the exit code of test subprocess. -pub fn get_result_from_exit_code( +pub(crate) fn get_result_from_exit_code( desc: &TestDesc, status: ExitStatus, time_opts: Option<&time::TestTimeOptions>, diff --git a/library/test/src/tests.rs b/library/test/src/tests.rs index e85e61090a91..abeb36421697 100644 --- a/library/test/src/tests.rs +++ b/library/test/src/tests.rs @@ -78,7 +78,7 @@ fn one_ignored_one_unignored_test() -> Vec { } #[test] -pub fn do_not_run_ignored_tests() { +fn do_not_run_ignored_tests() { fn f() -> Result<(), String> { panic!(); } @@ -106,7 +106,7 @@ pub fn do_not_run_ignored_tests() { } #[test] -pub fn ignored_tests_result_in_ignored() { +fn ignored_tests_result_in_ignored() { fn f() -> Result<(), String> { Ok(()) } @@ -479,7 +479,7 @@ fn parse_include_ignored_flag() { } #[test] -pub fn filter_for_ignored_option() { +fn filter_for_ignored_option() { // When we run ignored tests the test filter should filter out all the // unignored tests and flip the ignore flag on the rest to false @@ -496,7 +496,7 @@ pub fn filter_for_ignored_option() { } #[test] -pub fn run_include_ignored_option() { +fn run_include_ignored_option() { // When we "--include-ignored" tests, the ignore flag should be set to false on // all tests and no test filtered out @@ -513,7 +513,7 @@ pub fn run_include_ignored_option() { } #[test] -pub fn exclude_should_panic_option() { +fn exclude_should_panic_option() { let mut opts = TestOpts::new(); opts.run_tests = true; opts.exclude_should_panic = true; @@ -544,7 +544,7 @@ pub fn exclude_should_panic_option() { } #[test] -pub fn exact_filter_match() { +fn exact_filter_match() { fn tests() -> Vec { ["base", "base::test", "base::test1", "base::test2"] .into_iter() @@ -667,7 +667,7 @@ fn sample_tests() -> Vec { } #[test] -pub fn shuffle_tests() { +fn shuffle_tests() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -686,7 +686,7 @@ pub fn shuffle_tests() { } #[test] -pub fn shuffle_tests_with_seed() { +fn shuffle_tests_with_seed() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -704,7 +704,7 @@ pub fn shuffle_tests_with_seed() { } #[test] -pub fn order_depends_on_more_than_seed() { +fn order_depends_on_more_than_seed() { let mut opts = TestOpts::new(); opts.shuffle = true; @@ -732,7 +732,7 @@ pub fn order_depends_on_more_than_seed() { } #[test] -pub fn test_metricmap_compare() { +fn test_metricmap_compare() { let mut m1 = MetricMap::new(); let mut m2 = MetricMap::new(); m1.insert_metric("in-both-noise", 1000.0, 200.0); @@ -755,7 +755,7 @@ pub fn test_metricmap_compare() { } #[test] -pub fn test_bench_once_no_iter() { +fn test_bench_once_no_iter() { fn f(_: &mut Bencher) -> Result<(), String> { Ok(()) } @@ -763,7 +763,7 @@ pub fn test_bench_once_no_iter() { } #[test] -pub fn test_bench_once_iter() { +fn test_bench_once_iter() { fn f(b: &mut Bencher) -> Result<(), String> { b.iter(|| {}); Ok(()) @@ -772,7 +772,7 @@ pub fn test_bench_once_iter() { } #[test] -pub fn test_bench_no_iter() { +fn test_bench_no_iter() { fn f(_: &mut Bencher) -> Result<(), String> { Ok(()) } @@ -799,7 +799,7 @@ pub fn test_bench_no_iter() { } #[test] -pub fn test_bench_iter() { +fn test_bench_iter() { fn f(b: &mut Bencher) -> Result<(), String> { b.iter(|| {}); Ok(()) diff --git a/library/test/src/time.rs b/library/test/src/time.rs index 02ae050db55b..f63b156b3dc5 100644 --- a/library/test/src/time.rs +++ b/library/test/src/time.rs @@ -11,7 +11,7 @@ use std::{env, fmt}; use super::types::{TestDesc, TestType}; -pub const TEST_WARN_TIMEOUT_S: u64 = 60; +pub(crate) const TEST_WARN_TIMEOUT_S: u64 = 60; /// This small module contains constants used by `report-time` option. /// Those constants values will be used if corresponding environment variables are not set. @@ -22,42 +22,42 @@ pub const TEST_WARN_TIMEOUT_S: u64 = 60; /// /// Example of the expected format is `RUST_TEST_TIME_xxx=100,200`, where 100 means /// warn time, and 200 means critical time. -pub mod time_constants { +pub(crate) mod time_constants { use std::time::Duration; use super::TEST_WARN_TIMEOUT_S; /// Environment variable for overriding default threshold for unit-tests. - pub const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; + pub(crate) const UNIT_ENV_NAME: &str = "RUST_TEST_TIME_UNIT"; // Unit tests are supposed to be really quick. - pub const UNIT_WARN: Duration = Duration::from_millis(50); - pub const UNIT_CRITICAL: Duration = Duration::from_millis(100); + pub(crate) const UNIT_WARN: Duration = Duration::from_millis(50); + pub(crate) const UNIT_CRITICAL: Duration = Duration::from_millis(100); /// Environment variable for overriding default threshold for unit-tests. - pub const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION"; + pub(crate) const INTEGRATION_ENV_NAME: &str = "RUST_TEST_TIME_INTEGRATION"; // Integration tests may have a lot of work, so they can take longer to execute. - pub const INTEGRATION_WARN: Duration = Duration::from_millis(500); - pub const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); + pub(crate) const INTEGRATION_WARN: Duration = Duration::from_millis(500); + pub(crate) const INTEGRATION_CRITICAL: Duration = Duration::from_millis(1000); /// Environment variable for overriding default threshold for unit-tests. - pub const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST"; + pub(crate) const DOCTEST_ENV_NAME: &str = "RUST_TEST_TIME_DOCTEST"; // Doctests are similar to integration tests, because they can include a lot of // initialization code. - pub const DOCTEST_WARN: Duration = INTEGRATION_WARN; - pub const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; + pub(crate) const DOCTEST_WARN: Duration = INTEGRATION_WARN; + pub(crate) const DOCTEST_CRITICAL: Duration = INTEGRATION_CRITICAL; // Do not suppose anything about unknown tests, base limits on the // `TEST_WARN_TIMEOUT_S` constant. - pub const UNKNOWN_WARN: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S); - pub const UNKNOWN_CRITICAL: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S * 2); + pub(crate) const UNKNOWN_WARN: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S); + pub(crate) const UNKNOWN_CRITICAL: Duration = Duration::from_secs(TEST_WARN_TIMEOUT_S * 2); } /// Returns an `Instance` object denoting when the test should be considered /// timed out. -pub fn get_default_test_timeout() -> Instant { +pub(crate) fn get_default_test_timeout() -> Instant { Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S) } @@ -73,7 +73,7 @@ impl fmt::Display for TestExecTime { /// The measured execution time of the whole test suite. #[derive(Debug, Clone, Default, PartialEq)] -pub struct TestSuiteExecTime(pub Duration); +pub(crate) struct TestSuiteExecTime(pub Duration); impl fmt::Display for TestSuiteExecTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs index 563b715fa640..98b8635132b0 100644 --- a/src/bootstrap/src/core/build_steps/gcc.rs +++ b/src/bootstrap/src/core/build_steps/gcc.rs @@ -12,6 +12,8 @@ use std::fs; use std::path::PathBuf; use std::sync::OnceLock; +use build_helper::ci::CiEnv; + use crate::Kind; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; use crate::core::config::TargetSelection; @@ -112,16 +114,60 @@ impl Step for Gcc { return true; } - command(root.join("contrib/download_prerequisites")).current_dir(&root).run(builder); - command(root.join("configure")) + // GCC creates files (e.g. symlinks to the downloaded dependencies) + // in the source directory, which does not work with our CI setup, where we mount + // source directories as read-only on Linux. + // Therefore, as a part of the build in CI, we first copy the whole source directory + // to the build directory, and perform the build from there. + let src_dir = if CiEnv::is_ci() { + let src_dir = builder.gcc_out(target).join("src"); + if src_dir.exists() { + builder.remove_dir(&src_dir); + } + builder.create_dir(&src_dir); + builder.cp_link_r(&root, &src_dir); + src_dir + } else { + root + }; + + command(src_dir.join("contrib/download_prerequisites")).current_dir(&src_dir).run(builder); + let mut configure_cmd = command(src_dir.join("configure")); + configure_cmd .current_dir(&out_dir) + // On CI, we compile GCC with Clang. + // The -Wno-everything flag is needed to make GCC compile with Clang 19. + // `-g -O2` are the default flags that are otherwise used by Make. + // FIXME(kobzol): change the flags once we have [gcc] configuration in config.toml. + .env("CXXFLAGS", "-Wno-everything -g -O2") + .env("CFLAGS", "-Wno-everything -g -O2") .arg("--enable-host-shared") .arg("--enable-languages=jit") .arg("--enable-checking=release") .arg("--disable-bootstrap") .arg("--disable-multilib") - .arg(format!("--prefix={}", install_dir.display())) - .run(builder); + .arg(format!("--prefix={}", install_dir.display())); + let cc = builder.build.cc(target).display().to_string(); + let cc = builder + .build + .config + .ccache + .as_ref() + .map_or_else(|| cc.clone(), |ccache| format!("{ccache} {cc}")); + configure_cmd.env("CC", cc); + + if let Ok(ref cxx) = builder.build.cxx(target) { + let cxx = cxx.display().to_string(); + let cxx = builder + .build + .config + .ccache + .as_ref() + .map_or_else(|| cxx.clone(), |ccache| format!("{ccache} {cxx}")); + configure_cmd.env("CXX", cxx); + } + configure_cmd.run(builder); + command("make").current_dir(&out_dir).arg(format!("-j{}", builder.jobs())).run(builder); command("make").current_dir(&out_dir).arg("install").run(builder); diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index dde6fe7f6d0f..3a3962305825 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -34,6 +34,7 @@ RUN yum upgrade -y && \ python3 \ unzip \ wget \ + flex \ xz \ zlib-devel.i686 \ zlib-devel.x86_64 \ diff --git a/src/ci/docker/scripts/build-zstd.sh b/src/ci/docker/scripts/build-zstd.sh index a3d37ccc3112..cffa7151e386 100755 --- a/src/ci/docker/scripts/build-zstd.sh +++ b/src/ci/docker/scripts/build-zstd.sh @@ -25,5 +25,11 @@ cd zstd-$ZSTD CFLAGS=-fPIC hide_output make -j$(nproc) VERBOSE=1 hide_output make install +# It doesn't seem to be possible to move destination directory +# of the `make install` above. We thus copy the built artifacts +# manually to our custom rustroot, so that it can be found through +# LD_LIBRARY_PATH. +cp /usr/local/lib/libzstd* /rustroot/lib64 + cd .. rm -rf zstd-$ZSTD diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 5e95e3721dfd..7730d29d28f6 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -52,6 +52,8 @@ runners: free_disk: true os: ubuntu-22.04-arm + - &job-aarch64-linux-8c + os: ubuntu-22.04-arm64-8core-32gb envs: env-x86_64-apple-tests: &env-x86_64-apple-tests SCRIPT: ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact @@ -144,7 +146,7 @@ auto: - name: dist-aarch64-linux env: CODEGEN_BACKENDS: llvm,cranelift - <<: *job-aarch64-linux + <<: *job-aarch64-linux-8c - name: dist-android <<: *job-linux-4c diff --git a/src/ci/scripts/free-disk-space.sh b/src/ci/scripts/free-disk-space.sh new file mode 100755 index 000000000000..4a7dad0090b2 --- /dev/null +++ b/src/ci/scripts/free-disk-space.sh @@ -0,0 +1,142 @@ +#!/bin/bash + +# Free disk space on Linux GitHub action runners +# Script inspired by https://github.com/jlumbroso/free-disk-space + +# print a line of the specified character +printSeparationLine() { + for ((i = 0; i < 80; i++)); do + printf "%s" "$1" + done + printf "\n" +} + +# compute available space +# REF: https://unix.stackexchange.com/a/42049/60849 +# REF: https://stackoverflow.com/a/450821/408734 +getAvailableSpace() { echo $(df -a | awk 'NR > 1 {avail+=$4} END {print avail}'); } + +# make Kb human readable (assume the input is Kb) +# REF: https://unix.stackexchange.com/a/44087/60849 +formatByteCount() { echo $(numfmt --to=iec-i --suffix=B --padding=7 $1'000'); } + +# macro to output saved space +printSavedSpace() { + # Disk space before the operation + local before=${1} + local title=${2:-} + + local after + after=$(getAvailableSpace) + local saved=$((after - before)) + + echo "" + printSeparationLine "*" + if [ -n "${title}" ]; then + echo "=> ${title}: Saved $(formatByteCount "$saved")" + else + echo "=> Saved $(formatByteCount "$saved")" + fi + printSeparationLine "*" + echo "" +} + +# macro to print output of df with caption +printDF() { + local caption=${1} + + printSeparationLine "=" + echo "${caption}" + echo "" + echo "$ df -h" + echo "" + df -h + printSeparationLine "=" +} + +removeDir() { + dir=${1} + + local before + before=$(getAvailableSpace) + + sudo rm -rf "$dir" || true + + printSavedSpace "$before" "$dir" +} + +execAndMeasureSpaceChange() { + local operation=${1} # Function to execute + local title=${2} + + local before + before=$(getAvailableSpace) + $operation + + printSavedSpace "$before" "$title" +} + +# Remove large packages +# REF: https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh +cleanPackages() { + sudo apt-get -qq remove -y --fix-missing \ + '^aspnetcore-.*' \ + '^dotnet-.*' \ + '^llvm-.*' \ + 'php.*' \ + '^mongodb-.*' \ + '^mysql-.*' \ + 'azure-cli' \ + 'google-chrome-stable' \ + 'firefox' \ + 'powershell' \ + 'mono-devel' \ + 'libgl1-mesa-dri' \ + 'google-cloud-sdk' \ + 'google-cloud-cli' + + sudo apt-get autoremove -y || echo "::warning::The command [sudo apt-get autoremove -y] failed" + sudo apt-get clean || echo "::warning::The command [sudo apt-get clean] failed failed" +} + +# Remove Docker images +cleanDocker() { + echo "Removing the following docker images:" + sudo docker image ls + echo "Removing docker images..." + sudo docker image prune --all --force || true +} + +# Remove Swap storage +cleanSwap() { + sudo swapoff -a || true + sudo rm -rf /mnt/swapfile || true + free -h +} + +# Display initial disk space stats + +AVAILABLE_INITIAL=$(getAvailableSpace) + +printDF "BEFORE CLEAN-UP:" +echo "" + +removeDir /usr/local/lib/android +removeDir /usr/share/dotnet + +# Haskell runtime +removeDir /opt/ghc +removeDir /usr/local/.ghcup + +execAndMeasureSpaceChange cleanPackages "Large misc. packages" +execAndMeasureSpaceChange cleanDocker "Docker images" +execAndMeasureSpaceChange cleanSwap "Swap storage" + +# Output saved space statistic +echo "" +printDF "AFTER CLEAN-UP:" + +echo "" +echo "" + +printSavedSpace "$AVAILABLE_INITIAL" "Total saved" diff --git a/src/doc/rustc-dev-guide/.github/workflows/ci.yml b/src/doc/rustc-dev-guide/.github/workflows/ci.yml index 07c20d8d88bf..006bcce44b3d 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/ci.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/ci.yml @@ -35,12 +35,13 @@ jobs: ~/.cargo/bin key: ${{ runner.os }}-${{ env.MDBOOK_VERSION }}--${{ env.MDBOOK_LINKCHECK2_VERSION }}--${{ env.MDBOOK_TOC_VERSION }}--${{ env.MDBOOK_MERMAID_VERSION }} - - name: Cache linkcheck - uses: actions/cache@v4 + - name: Restore cached Linkcheck + if: github.event_name == 'schedule' + id: cache-linkcheck-restore + uses: actions/cache/restore@v4 with: - path: | - ~/book/linkcheck - key: ${{ runner.os }}-${{ hashFiles('./book/linkcheck') }} + path: book/linkcheck/cache.json + key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }} - name: Install latest nightly Rust toolchain if: steps.mdbook-cache.outputs.cache-hit != 'true' @@ -59,6 +60,14 @@ jobs: - name: Check build run: ENABLE_LINKCHECK=1 mdbook build + - name: Save cached Linkcheck + id: cache-linkcheck-save + if: ${{ !cancelled() && github.event_name == 'schedule' }} + uses: actions/cache/save@v4 + with: + path: book/linkcheck/cache.json + key: linkcheck--${{ env.MDBOOK_LINKCHECK2_VERSION }} + - name: Deploy to gh-pages if: github.event_name == 'push' run: | diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml new file mode 100644 index 000000000000..87a3ee2e78f1 --- /dev/null +++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml @@ -0,0 +1,85 @@ +name: rustc-pull + +on: + workflow_dispatch: + schedule: + # Run at 04:00 UTC every Monday + - cron: '0 4 * * 1' + +jobs: + pull: + if: github.repository == 'rust-lang/rustc-dev-guide' + runs-on: ubuntu-latest + outputs: + pr_url: ${{ steps.update-pr.outputs.pr_url }} + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + # We need the full history for josh to work + fetch-depth: '0' + - name: Install stable Rust toolchain + run: rustup update stable + - uses: Swatinem/rust-cache@v2 + with: + workspaces: "josh-sync" + # Cache the josh directory with checked out rustc + cache-directories: "/home/runner/.cache/rustc-dev-guide-josh" + - name: Install josh + run: RUSTFLAGS="--cap-lints warn" cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 + - name: Setup bot git name and email + run: | + git config --global user.name 'The rustc-dev-guide Cronjob Bot' + git config --global user.email 'github-actions@github.com' + - name: Perform rustc-pull + run: cargo run --manifest-path josh-sync/Cargo.toml -- rustc-pull + - name: Push changes to a branch + run: | + # Update a sticky branch that is used only for rustc pulls + BRANCH="rustc-pull" + git switch -c $BRANCH + git push -u origin $BRANCH --force + - name: Create pull request + id: update-pr + run: | + # Check if an open pull request for an rustc pull update already exists + # If it does, the previous push has just updated it + # If not, we create it now + RESULT=`gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | length' --json title` + if [[ "$RESULT" -eq 0 ]]; then + echo "Creating new pull request" + PR_URL=gh pr create -B master --title 'Rustc pull update' --body 'Latest update from rustc.' + echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT + else + PR_URL=gh pr list --author github-actions[bot] --state open -q 'map(select(.title=="Rustc pull update")) | .[0].url' --json url,title + echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT + fi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + send-zulip-message: + needs: [pull] + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + - name: Compute message + id: message + run: | + if [ "${{ needs.pull.result }}" == "failure" ]; + then + WORKFLOW_URL="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + echo "message=Rustc pull sync failed. Check out the [workflow URL]($WORKFLOW_URL)." >> $GITHUB_OUTPUT + else + echo "message=Rustc pull sync succeeded. Check out the [PR](${{ needs.pull.outputs.pr_url }})." >> $GITHUB_OUTPUT + fi + - name: Send a Zulip message about updated PR + uses: zulip/github-actions-zulip/send-message@e4c8f27c732ba9bd98ac6be0583096dea82feea5 + with: + api-key: ${{ secrets.ZULIP_API_TOKEN }} + email: "rustc-dev-guide-gha-notif-bot@rust-lang.zulipchat.com" + organization-url: "https://rust-lang.zulipchat.com" + to: 196385 + type: "stream" + topic: "Subtree sync automation" + content: ${{ steps.message.outputs.message }} diff --git a/src/doc/rustc-dev-guide/.gitignore b/src/doc/rustc-dev-guide/.gitignore index 160c5f0fe5c7..f03fcae753f4 100644 --- a/src/doc/rustc-dev-guide/.gitignore +++ b/src/doc/rustc-dev-guide/.gitignore @@ -4,3 +4,5 @@ ci/date-check/target/ # Generated by check-in.sh pulls.json + +josh-sync/target diff --git a/src/doc/rustc-dev-guide/README.md b/src/doc/rustc-dev-guide/README.md index 7ce7d4ddf3e7..2464ffbbc503 100644 --- a/src/doc/rustc-dev-guide/README.md +++ b/src/doc/rustc-dev-guide/README.md @@ -74,6 +74,13 @@ including the `` marker at the place where you want the TOC. This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.github.io/josh/intro.html) subtree. You can use the following commands to synchronize the subtree in both directions. +You'll need to install `josh-proxy` locally via + +``` +cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 +``` +Older versions of `josh-proxy` may not round trip commits losslessly so it is important to install this exact version. + ### Pull changes from `rust-lang/rust` into this repository 1) Checkout a new branch that will be used to create a PR into `rust-lang/rustc-dev-guide` 2) Run the pull command diff --git a/src/doc/rustc-dev-guide/book.toml b/src/doc/rustc-dev-guide/book.toml index 2f67aecf6f05..67069d9930f5 100644 --- a/src/doc/rustc-dev-guide/book.toml +++ b/src/doc/rustc-dev-guide/book.toml @@ -52,7 +52,9 @@ exclude = [ # 500 is returned for HEAD request "code\\.visualstudio\\.com/docs/editor/tasks", ] -cache-timeout = 86400 +# The scheduled CI runs every day and so we need to reuse a part of the cache +# in order to face "Server returned 429 Too Many Requests" errors for github.com. +cache-timeout = 90000 warning-policy = "error" [output.html.redirect] diff --git a/src/doc/rustc-dev-guide/josh-sync/src/sync.rs b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs index da21a4c9a27c..eff80b1091d3 100644 --- a/src/doc/rustc-dev-guide/josh-sync/src/sync.rs +++ b/src/doc/rustc-dev-guide/josh-sync/src/sync.rs @@ -45,6 +45,11 @@ impl GitSync { let josh_url = format!("http://localhost:{JOSH_PORT}/{UPSTREAM_REPO}.git@{commit}{JOSH_FILTER}.git"); + let previous_base_commit = sh.read_file("rust-version")?.trim().to_string(); + if previous_base_commit == commit { + return Err(anyhow::anyhow!("No changes since last pull")); + } + // Update rust-version file. As a separate commit, since making it part of // the merge has confused the heck out of josh in the past. // We pass `--no-verify` to avoid running git hooks. @@ -76,12 +81,22 @@ impl GitSync { }; let num_roots_before = num_roots()?; + let sha = cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout; + // Merge the fetched commit. const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc"; cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}") .run() .context("FAILED to merge new commits, something went wrong")?; + let current_sha = cmd!(sh, "git rev-parse HEAD").output().context("FAILED to get current commit")?.stdout; + if current_sha == sha { + cmd!(sh, "git reset --hard HEAD^") + .run() + .expect("FAILED to clean up after creating the preparation commit"); + return Err(anyhow::anyhow!("No merge was performed, nothing to pull. Rolled back the preparation commit.")); + } + // Check that the number of roots did not increase. if num_roots()? != num_roots_before { bail!("Josh created a new root commit. This is probably not the history you want."); diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 876c32dcf434..9693bfd63e84 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -dcfa38fe234de9304169afc6638e81d0dd222c06 +ecda83b30f0f68cf5692855dddc0bc38ee8863fc diff --git a/src/doc/rustc-dev-guide/src/appendix/code-index.md b/src/doc/rustc-dev-guide/src/appendix/code-index.md index c33887d72941..b96ede68eab5 100644 --- a/src/doc/rustc-dev-guide/src/appendix/code-index.md +++ b/src/doc/rustc-dev-guide/src/appendix/code-index.md @@ -14,17 +14,16 @@ Item | Kind | Short description | Chapter | `Diag` | struct | A struct for a compiler diagnostic, such as an error or lint | [Emitting Diagnostics] | [compiler/rustc_errors/src/diagnostic.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html) `DocContext` | struct | A state container used by rustdoc when crawling through a crate to gather its documentation | [Rustdoc] | [src/librustdoc/core.rs](https://github.com/rust-lang/rust/blob/master/src/librustdoc/core.rs) `HirId` | struct | One of four types of HIR node identifiers | [Identifiers in the HIR] | [compiler/rustc_hir/src/hir_id.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir_id/struct.HirId.html) +`Lexer` | struct | This is the lexer used during parsing. It consumes characters from the raw source code being compiled and produces a series of tokens for use by the rest of the parser | [The parser] | [compiler/rustc_parse/src/lexer/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.Lexer.html) `NodeId` | struct | One of four types of HIR node identifiers. Being phased out | [Identifiers in the HIR] | [compiler/rustc_ast/src/ast.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/node_id/struct.NodeId.html) `P` | struct | An owned immutable smart pointer. By contrast, `&T` is not owned, and `Box` is not immutable. | None | [compiler/rustc_ast/src/ptr.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ptr/struct.P.html) `ParamEnv` | struct | Information about generic parameters or `Self`, useful for working with associated or generic items | [Parameter Environment] | [compiler/rustc_middle/src/ty/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.ParamEnv.html) `ParseSess` | struct | This struct contains information about a parsing session | [The parser] | [compiler/rustc_session/src/parse/parse.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.ParseSess.html) -`Query` | struct | Represents the result of query to the `Compiler` interface and allows stealing, borrowing, and returning the results of compiler passes. | [The Rustc Driver and Interface] | [compiler/rustc_interface/src/queries.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/queries/struct.Query.html) `Rib` | struct | Represents a single scope of names | [Name resolution] | [compiler/rustc_resolve/src/lib.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/struct.Rib.html) `Session` | struct | The data associated with a compilation session | [The parser], [The Rustc Driver and Interface] | [compiler/rustc_session/src/session.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/struct.Session.html) `SourceFile` | struct | Part of the `SourceMap`. Maps AST nodes to their source code for a single source file. Was previously called FileMap | [The parser] | [compiler/rustc_span/src/lib.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.SourceFile.html) `SourceMap` | struct | Maps AST nodes to their source code. It is composed of `SourceFile`s. Was previously called CodeMap | [The parser] | [compiler/rustc_span/src/source_map.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/source_map/struct.SourceMap.html) `Span` | struct | A location in the user's source code, used for error reporting primarily | [Emitting Diagnostics] | [compiler/rustc_span/src/span_encoding.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html) -`StringReader` | struct | This is the lexer used during parsing. It consumes characters from the raw source code being compiled and produces a series of tokens for use by the rest of the parser | [The parser] | [compiler/rustc_parse/src/lexer/mod.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html) `rustc_ast::token_stream::TokenStream` | struct | An abstract sequence of tokens, organized into `TokenTree`s | [The parser], [Macro expansion] | [compiler/rustc_ast/src/tokenstream.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/tokenstream/struct.TokenStream.html) `TraitDef` | struct | This struct contains a trait's definition with type information | [The `ty` modules] | [compiler/rustc_middle/src/ty/trait_def.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/trait_def/struct.TraitDef.html) `TraitRef` | struct | The combination of a trait and its input types (e.g. `P0: Trait`) | [Trait Solving: Goals and Clauses] | [compiler/rustc_middle/src/ty/sty.rs](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.TraitRef.html) diff --git a/src/doc/rustc-dev-guide/src/appendix/glossary.md b/src/doc/rustc-dev-guide/src/appendix/glossary.md index bf3475a984b0..a7c3236d356b 100644 --- a/src/doc/rustc-dev-guide/src/appendix/glossary.md +++ b/src/doc/rustc-dev-guide/src/appendix/glossary.md @@ -69,7 +69,7 @@ Term | Meaning rib | A data structure in the name resolver that keeps track of a single scope for names. ([see more](../name-resolution.md)) RPIT | A return-position `impl Trait`. ([see the reference](https://doc.rust-lang.org/reference/types/impl-trait.html#abstract-return-types)). RPITIT | A return-position `impl Trait` in trait. Unlike RPIT, this is desugared to a generic associated type (GAT). Introduced in [RFC 3425](https://rust-lang.github.io/rfcs/3425-return-position-impl-trait-in-traits.html). ([see more](../return-position-impl-trait-in-trait.md)) -scrutinee | A scrutinee is the expression that is matched on in `match` expressions and similar pattern matching constructs. For example, in `match x { A => 1, B => 2 }`, the expression `x` is the scrutinee. +scrutinee | A scrutinee is the expression that is matched on in `match` expressions and similar pattern matching constructs. For example, in `match x { A => 1, B => 2 }`, the expression `x` is the scrutinee. `sess` | The compiler _session_, which stores global data used throughout compilation side tables | Because the [AST](#ast) and HIR are immutable once created, we often carry extra information about them in the form of hashtables, indexed by the id of a particular node. sigil | Like a keyword but composed entirely of non-alphanumeric tokens. For example, `&` is a sigil for references. diff --git a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md index 3521c00f5cf2..0f81d3cb48a1 100644 --- a/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md +++ b/src/doc/rustc-dev-guide/src/backend/backend-agnostic.md @@ -45,7 +45,7 @@ heavily on other parts of the crate. The separation of the code must not affect the logic of the code nor its performance. For these reasons, the separation process involves two transformations that -have to be done at the same time for the resulting code to compile : +have to be done at the same time for the resulting code to compile: 1. replace all the LLVM-specific types by generics inside function signatures and structure definitions; diff --git a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/closure_constraints.md b/src/doc/rustc-dev-guide/src/borrow_check/region_inference/closure_constraints.md index be279b773cf8..b95c9f72306b 100644 --- a/src/doc/rustc-dev-guide/src/borrow_check/region_inference/closure_constraints.md +++ b/src/doc/rustc-dev-guide/src/borrow_check/region_inference/closure_constraints.md @@ -31,9 +31,9 @@ It starts by calling `fn try_promote_type_test_subject`. This function takes the We then promote the `lower_bound` into the context of the caller. If the lower bound is equal to a placeholder, we replace it with `'static` -We then look at all universal regions `uv` which are required to outlive `lower_bound`, i.e. for which borrow checking adding region constraints. For each of these we then emit a `ClosureOutlivesRequirement` for non-local universal regions which are known to outlive `uv`. +We then look at all universal regions `uv` which are required to be outlived by `lower_bound`, i.e. for which borrow checking added region constraints. For each of these we then emit a `ClosureOutlivesRequirement` for all non-local universal regions which are known to outlive `uv`. -As we've already built the region graph of the closure at this point and emitted errors if that one is inconsistent, we are also able to assume that the outlive constraints `uv: lower_bound` hold. +As we've already built the region graph of the closure at this point and separately check that it is consistent, we are also able to assume the outlive constraints `uv: lower_bound` here. So if we have a type-outlives bounds we can't prove, e.g. `T: 'local_infer`, we use the region graph to go to universal variables `'a` with `'a: local_infer`. In case `'a` are local, we then use the assumed outlived constraints to go to non-local ones. diff --git a/src/doc/rustc-dev-guide/src/bug-fix-procedure.md b/src/doc/rustc-dev-guide/src/bug-fix-procedure.md index 4857cf5e0bee..e6a16df6d2a9 100644 --- a/src/doc/rustc-dev-guide/src/bug-fix-procedure.md +++ b/src/doc/rustc-dev-guide/src/bug-fix-procedure.md @@ -227,7 +227,7 @@ that we use for unstable features: Ideally, breaking changes should have landed on the **stable branch** of the compiler before they are finalized. - + ### Removing a lint diff --git a/src/doc/rustc-dev-guide/src/closure.md b/src/doc/rustc-dev-guide/src/closure.md index 718a0e5d78b1..427919cd5799 100644 --- a/src/doc/rustc-dev-guide/src/closure.md +++ b/src/doc/rustc-dev-guide/src/closure.md @@ -157,7 +157,7 @@ The other option is to step through the code using lldb or gdb. 1. `rust-lldb build/host/stage1/bin/rustc test.rs` 2. In lldb: - 1. `b upvar.rs:134` // Setting the breakpoint on a certain line in the upvar.rs file` + 1. `b upvar.rs:134` // Setting the breakpoint on a certain line in the upvar.rs file 2. `r` // Run the program until it hits the breakpoint Let's start with [`upvar.rs`][upvar]. This file has something called diff --git a/src/doc/rustc-dev-guide/src/const-eval.md b/src/doc/rustc-dev-guide/src/const-eval.md index ee0269601af1..69329a3e0851 100644 --- a/src/doc/rustc-dev-guide/src/const-eval.md +++ b/src/doc/rustc-dev-guide/src/const-eval.md @@ -17,7 +17,7 @@ Prominent examples are: * need to be known to check for overlapping patterns Additionally constant evaluation can be used to reduce the workload or binary -size at runtime by precomputing complex operations at compiletime and only +size at runtime by precomputing complex operations at compile time and only storing the result. All uses of constant evaluation can either be categorized as "influencing the type system" diff --git a/src/doc/rustc-dev-guide/src/contributing.md b/src/doc/rustc-dev-guide/src/contributing.md index ad1d33265c55..9817326f07ba 100644 --- a/src/doc/rustc-dev-guide/src/contributing.md +++ b/src/doc/rustc-dev-guide/src/contributing.md @@ -422,68 +422,8 @@ Just a few things to keep in mind: ## Issue triage -Sometimes, an issue will stay open, even though the bug has been fixed. -And sometimes, the original bug may go stale because something has changed in the meantime. +Please see . -It can be helpful to go through older bug reports and make sure that they are still valid. -Load up an older issue, double check that it's still true, -and leave a comment letting us know if it is or is not. -The [least recently updated sort][lru] is good for finding issues like this. - -[Thanks to `@rustbot`][rustbot], anyone can help triage issues by adding -appropriate labels to issues that haven't been triaged yet: - -[lru]: https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc -[rustbot]: ./rustbot.md - - - -| Labels | Color | Description | -|--------|-------|-------------| -| [A-] |  Yellow | The **area** of the project an issue relates to. | -| [B-] |  Magenta | Issues which are **blockers**. | -| [beta-] |  Dark Blue | Tracks changes which need to be [backported to beta][beta-backport] | -| [C-] |  Light Purple | The **category** of an issue. | -| [D-] |  Mossy Green | Issues for **diagnostics**. | -| [E-] |  Green | The **experience** level necessary to fix an issue. | -| [F-] |  Peach | Issues for **nightly features**. | -| [I-] |  Red | The **importance** of the issue. | -| [I-\*-nominated] |  Red | The issue has been nominated for discussion at the next meeting of the corresponding team. | -| [I-prioritize] |  Red | The issue has been nominated for prioritization by the team tagged with a **T**-prefixed label. | -| [L-] |  Teal | The relevant **lint**. | -| [metabug] |  Purple | Bugs that collect other bugs. | -| [O-] |  Purple Grey | The **operating system** or platform that the issue is specific to. | -| [P-] |  Orange | The issue **priority**. These labels can be assigned by anyone that understand the issue and is able to prioritize it, and remove the [I-prioritize] label. | -| [regression-] |  Pink | Tracks regressions from a stable release. | -| [relnotes] |  Light Orange | Changes that should be documented in the release notes of the next release. | -| [S-] |  Gray | Tracks the **status** of pull requests. | -| [S-tracking-] |  Steel Blue | Tracks the **status** of [tracking issues]. | -| [stable-] |  Dark Blue | Tracks changes which need to be [backported to stable][stable-backport] in anticipation of a point release. | -| [T-] |  Blue | Denotes which **team** the issue belongs to. | -| [WG-] |  Green | Denotes which **working group** the issue belongs to. | - - -[A-]: https://github.com/rust-lang/rust/labels?q=A -[B-]: https://github.com/rust-lang/rust/labels?q=B -[C-]: https://github.com/rust-lang/rust/labels?q=C -[D-]: https://github.com/rust-lang/rust/labels?q=D -[E-]: https://github.com/rust-lang/rust/labels?q=E -[F-]: https://github.com/rust-lang/rust/labels?q=F -[I-]: https://github.com/rust-lang/rust/labels?q=I -[L-]: https://github.com/rust-lang/rust/labels?q=L -[O-]: https://github.com/rust-lang/rust/labels?q=O -[P-]: https://github.com/rust-lang/rust/labels?q=P -[S-]: https://github.com/rust-lang/rust/labels?q=S -[T-]: https://github.com/rust-lang/rust/labels?q=T -[WG-]: https://github.com/rust-lang/rust/labels?q=WG [stable-]: https://github.com/rust-lang/rust/labels?q=stable [beta-]: https://github.com/rust-lang/rust/labels?q=beta [I-\*-nominated]: https://github.com/rust-lang/rust/labels?q=nominated diff --git a/src/doc/rustc-dev-guide/src/conventions.md b/src/doc/rustc-dev-guide/src/conventions.md index 37af8121cd1d..4010e90821f9 100644 --- a/src/doc/rustc-dev-guide/src/conventions.md +++ b/src/doc/rustc-dev-guide/src/conventions.md @@ -1,4 +1,4 @@ -This file offers some tips on the coding conventions for rustc. This +This file offers some tips on the coding conventions for rustc. This chapter covers [formatting](#formatting), [coding for correctness](#cc), [using crates from crates.io](#cio), and some tips on [structuring your PR for easy review](#er). @@ -25,6 +25,7 @@ pass the `--edition=2021` argument yourself when c `rustfmt` directly. [fmt]: https://github.com/rust-dev-tools/fmt-rfcs + [`rustfmt`]:https://github.com/rust-lang/rustfmt ## Formatting C++ code @@ -40,6 +41,26 @@ When modifying that code, use this command to format it: This uses a pinned version of `clang-format`, to avoid relying on the local environment. +## Formatting and linting Python code + +The Rust repository contains quite a lof of Python code. We try to keep +it both linted and formatted by the [ruff][ruff] tool. + +When modifying Python code, use this command to format it: +```sh +./x test tidy --extra-checks=py:fmt --bless +``` + +and the following command to run lints: +```sh +./x test tidy --extra-checks=py:lint +``` + +This uses a pinned version of `ruff`, to avoid relying on the local +environment. + +[ruff]: https://github.com/astral-sh/ruff + @@ -84,7 +105,7 @@ Using `_` in a match is convenient, but it means that when new variants are added to the enum, they may not get handled correctly. Ask yourself: if a new variant were added to this enum, what's the chance that it would want to use the `_` code, versus having some -other treatment? Unless the answer is "low", then prefer an +other treatment? Unless the answer is "low", then prefer an exhaustive match. (The same advice applies to `if let` and `while let`, which are effectively tests for a single variant.) @@ -124,7 +145,7 @@ See the [crates.io dependencies][crates] section. # How to structure your PR How you prepare the commits in your PR can make a big difference for the -reviewer. Here are some tips. +reviewer. Here are some tips. **Isolate "pure refactorings" into their own commit.** For example, if you rename a method, then put that rename into its own commit, along @@ -165,4 +186,5 @@ to the compiler. crate-related, often the spelling is changed to `krate`. [tcx]: ./ty.md + [crates]: ./crates-io.md diff --git a/src/doc/rustc-dev-guide/src/diagnostics.md b/src/doc/rustc-dev-guide/src/diagnostics.md index ccd1011659d5..709e9d4f8893 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics.md +++ b/src/doc/rustc-dev-guide/src/diagnostics.md @@ -111,7 +111,7 @@ Here are a few examples: their crate, making this a hard error would make refactoring and development very painful. - [future-incompatible lints]: - these are silencable lints. + these are silenceable lints. It was decided that making them fixed errors would cause too much breakage, so warnings are instead emitted, and will eventually be turned into fixed (hard) errors. diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index a7ab3d773acb..f3170c9222d8 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -18,9 +18,11 @@ The following external projects are managed using some form of a `subtree`: * [clippy](https://github.com/rust-lang/rust-clippy) * [miri](https://github.com/rust-lang/miri) +* [portable-simd](https://github.com/rust-lang/portable-simd) * [rustfmt](https://github.com/rust-lang/rustfmt) * [rust-analyzer](https://github.com/rust-lang/rust-analyzer) * [rustc_codegen_cranelift](https://github.com/rust-lang/rustc_codegen_cranelift) +* [rustc-dev-guide](https://github.com/rust-lang/rustc-dev-guide) In contrast to `submodule` dependencies (see below for those), the `subtree` dependencies are just regular files and directories which can @@ -33,13 +35,15 @@ implement a new tool feature or test, that should happen in one collective rustc * Using `git subtree` * `clippy` ([sync guide](https://doc.rust-lang.org/nightly/clippy/development/infrastructure/sync.html#performing-the-sync-from-rust-langrust-to-clippy)) + * `portable-simd` ([sync script](https://github.com/rust-lang/portable-simd/blob/master/subtree-sync.sh)) * `rustfmt` * `rustc_codegen_cranelift` ([sync script](https://github.com/rust-lang/rustc_codegen_cranelift/blob/113af154d459e41b3dc2c5d7d878e3d3a8f33c69/scripts/rustup.sh#L7)) * Using the [josh] tool * `miri` ([sync guide](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#advanced-topic-syncing-with-the-rustc-repo)) * `rust-analyzer` ([sync script](https://github.com/rust-lang/rust-analyzer/blob/2e13684be123eca7181aa48e043e185d8044a84a/xtask/src/release.rs#L147)) + * `rustc-dev-guide` ([sync guide](https://github.com/rust-lang/rustc-dev-guide#synchronizing-josh-subtree-with-rustc)) -The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh, you can check out the `miri` or `rust-analyzer` scripts linked above for inspiration. If you want to migrate a subtree from `git subtree` to josh, you can check out [this guide](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg). +The [josh] tool is an alternative to git subtrees, which manages git history in a different way and scales better to larger repositories. Specific tooling is required to work with josh, you can check out the `miri` or `rust-analyzer` scripts linked above for inspiration. If you want to migrate a repository dependency from `git subtree` or `git submodule` to josh, you can check out [this guide](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg). Below you can find a guide on how to perform push and pull synchronization with the main rustc repo using `git subtree`, although these instructions might differ repo from repo. diff --git a/src/doc/rustc-dev-guide/src/guides/editions.md b/src/doc/rustc-dev-guide/src/guides/editions.md index 336e391df126..ea207167791b 100644 --- a/src/doc/rustc-dev-guide/src/guides/editions.md +++ b/src/doc/rustc-dev-guide/src/guides/editions.md @@ -91,7 +91,7 @@ stability. ## Edition parsing For the most part, the lexer is edition-agnostic. -Within [`StringReader`], tokens can be modified based on edition-specific behavior. +Within [`Lexer`], tokens can be modified based on edition-specific behavior. For example, C-String literals like `c"foo"` are split into multiple tokens in editions before 2021. This is also where things like reserved prefixes are handled for the 2021 edition. @@ -114,7 +114,7 @@ For example, the deprecated `start...end` pattern syntax emits the [`ellipsis_inclusive_range_patterns`] lint on editions before 2021, and in 2021 is an hard error via the `emit_err` method. -[`StringReader`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html +[`Lexer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.Lexer.html [`ParseSess::edition`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/parse/struct.ParseSess.html#structfield.edition [`ellipsis_inclusive_range_patterns`]: https://doc.rust-lang.org/nightly/rustc/lints/listing/warn-by-default.html#ellipsis-inclusive-range-patterns diff --git a/src/doc/rustc-dev-guide/src/opaque-types-impl-trait-inference.md b/src/doc/rustc-dev-guide/src/opaque-types-impl-trait-inference.md index c56d51a4bb71..bdf4e4cd870b 100644 --- a/src/doc/rustc-dev-guide/src/opaque-types-impl-trait-inference.md +++ b/src/doc/rustc-dev-guide/src/opaque-types-impl-trait-inference.md @@ -78,7 +78,7 @@ If that fails, we reveal the hidden type of the opaque type, but only to prove this specific trait bound, not in general. Revealing is done by invoking the `type_of` query on the `DefId` of the opaque type. The query will internally request the hidden types from the defining function(s) -and return that (see [the section on `type_of`](#Within-the-type_of-query) for more details). +and return that (see [the section on `type_of`](#within-the-type_of-query) for more details). #### Flowchart of type checking steps diff --git a/src/doc/rustc-dev-guide/src/overview.md b/src/doc/rustc-dev-guide/src/overview.md index cc17eaa9e48c..21ab0040646b 100644 --- a/src/doc/rustc-dev-guide/src/overview.md +++ b/src/doc/rustc-dev-guide/src/overview.md @@ -38,7 +38,7 @@ Unicode character encoding. The token stream passes through a higher-level lexer located in [`rustc_parse`] to prepare for the next stage of the compile process. The -[`StringReader`] `struct` is used at this stage to perform a set of validations +[`Lexer`] `struct` is used at this stage to perform a set of validations and turn strings into interned symbols (_interning_ is discussed later). [String interning] is a way of storing only one immutable copy of each distinct string value. @@ -153,7 +153,7 @@ the final binary. [`rustc_parse::parser::Parser`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/parser/struct.Parser.html [`rustc_parse`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html [`simplify_try`]: https://github.com/rust-lang/rust/pull/66282 -[`StringReader`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html +[`Lexer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.Lexer.html [`Ty<'tcx>`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html [borrow checking]: borrow_check.md [codegen]: backend/codegen.md diff --git a/src/doc/rustc-dev-guide/src/solve/caching.md b/src/doc/rustc-dev-guide/src/solve/caching.md index 72b90c59beed..e568d47ca252 100644 --- a/src/doc/rustc-dev-guide/src/solve/caching.md +++ b/src/doc/rustc-dev-guide/src/solve/caching.md @@ -98,8 +98,8 @@ TODO: write this :3 [req-depth-ck]: https://github.com/rust-lang/rust/blob/7606c13961ddc1174b70638e934df0439b7dc515/compiler/rustc_middle/src/traits/solve/cache.rs#L76-L86 [update-depth]: https://github.com/rust-lang/rust/blob/7606c13961ddc1174b70638e934df0439b7dc515/compiler/rustc_trait_selection/src/solve/search_graph.rs#L308 [rem-depth]: https://github.com/rust-lang/rust/blob/7606c13961ddc1174b70638e934df0439b7dc515/compiler/rustc_middle/src/traits/solve/cache.rs#L124 -[^1]: This is overly restrictive: if all nested goal return the overflow response with some -availabledepth `n`, then their result should be the same for any depths smaller than `n`. +[^1]: This is overly restrictive: if all nested goals return the overflow response with some +available depth `n`, then their result should be the same for any depths smaller than `n`. We can implement this optimization in the future. [chapter on coinduction]: ./coinduction.md diff --git a/src/doc/rustc-dev-guide/src/solve/invariants.md b/src/doc/rustc-dev-guide/src/solve/invariants.md index c16a3aeb2b37..fd12b1957574 100644 --- a/src/doc/rustc-dev-guide/src/solve/invariants.md +++ b/src/doc/rustc-dev-guide/src/solve/invariants.md @@ -23,9 +23,6 @@ well-formed after normalizing said aliases. We rely on this as otherwise we would have to re-check for well-formedness for these types. -This is unfortunately broken for `>::Output` due to implied bounds, -resulting in [#114936]. - ### Structural equality modulo regions implies semantic equality ✅ If you have a some type and equate it to itself after replacing any regions with unique @@ -33,7 +30,7 @@ inference variables in both the lhs and rhs, the now potentially structurally di types should still be equal to each other. Needed to prevent goals from succeeding in HIR typeck and then failing in MIR borrowck. -If this does invariant is broken MIR typeck ends up failing with an ICE. +If this invariant is broken MIR typeck ends up failing with an ICE. ### Applying inference results from a goal does not change its result ❌ @@ -91,7 +88,7 @@ it can easily result in unsoundness, e.g. [#57893](https://github.com/rust-lang/ If a trait goal holds with an empty environment, there should be a unique `impl`, either user-defined or builtin, which is used to prove that goal. This is -necessary to select a unique method. It +necessary to select a unique method. We do however break this invariant in few cases, some of which are due to bugs, some by design: diff --git a/src/doc/rustc-dev-guide/src/solve/the-solver.md b/src/doc/rustc-dev-guide/src/solve/the-solver.md index f7d82d117a05..006fb649d220 100644 --- a/src/doc/rustc-dev-guide/src/solve/the-solver.md +++ b/src/doc/rustc-dev-guide/src/solve/the-solver.md @@ -25,7 +25,7 @@ a separate "probe", to not leak inference constraints to the other candidates. We then try to merge the assembled candidates via `EvalCtxt::merge_candidates`. -## Important concepts and design pattern +## Important concepts and design patterns ### `EvalCtxt::add_goal` @@ -64,7 +64,7 @@ eagerly instantiates `'a` with a placeholder and then recursively proves Some goals can be proven in multiple ways. In these cases we try each option in a separate "probe" and then attempt to merge the resulting responses by using `EvalCtxt::try_merge_responses`. If merging the responses fails, we use -`EvalCtxt::flounder` instead, returning ambiguity. For some goals, we try +`EvalCtxt::flounder` instead, returning ambiguity. For some goals, we try to incompletely prefer some choices over others in case `EvalCtxt::try_merge_responses` fails. diff --git a/src/doc/rustc-dev-guide/src/stability.md b/src/doc/rustc-dev-guide/src/stability.md index 1bfe911c900c..230925252bac 100644 --- a/src/doc/rustc-dev-guide/src/stability.md +++ b/src/doc/rustc-dev-guide/src/stability.md @@ -34,7 +34,8 @@ Previously, due to a [rustc bug], stable items inside unstable modules were available to stable code in that location. As of September 2024, items with [accidentally stabilized paths] are marked with the `#[rustc_allowed_through_unstable_modules]` attribute -to prevent code dependent on those paths from breaking. +to prevent code dependent on those paths from breaking. Do *not* add this attribute +to any more items unless that is needed to avoid breaking changes. The `unstable` attribute may also have the `soft` value, which makes it a future-incompatible deny-by-default lint instead of a hard error. This is used diff --git a/src/doc/rustc-dev-guide/src/tests/ci.md b/src/doc/rustc-dev-guide/src/tests/ci.md index 0ad1d2a28ede..9dde407895e2 100644 --- a/src/doc/rustc-dev-guide/src/tests/ci.md +++ b/src/doc/rustc-dev-guide/src/tests/ci.md @@ -28,7 +28,7 @@ Our CI is primarily executed on [GitHub Actions], with a single workflow defined in [`.github/workflows/ci.yml`], which contains a bunch of steps that are unified for all CI jobs that we execute. When a commit is pushed to a corresponding branch or a PR, the workflow executes the -[`ci.py`] script, which dynamically generates the specific CI +[`src/ci/github-actions/ci.py`] script, which dynamically generates the specific CI jobs that should be executed. This script uses the [`jobs.yml`] file as an input, which contains a declarative configuration of all our CI jobs. @@ -409,10 +409,25 @@ To learn more about the dashboard, see the [Datadog CI docs]. [Datadog CI docs]: https://docs.datadoghq.com/continuous_integration/ [public dashboard]: https://p.datadoghq.com/sb/3a172e20-e9e1-11ed-80e3-da7ad0900002-b5f7bb7e08b664a06b08527da85f7e30 +## Determining the CI configuration + +If you want to determine which `config.toml` settings are used in CI for a +particular job, it is probably easiest to just look at the build log. To do +this: + +1. Go to + + to find the most recently successful build, and click on it. +2. Choose the job you are interested in on the left-hand side. +3. Click on the gear icon and choose "View raw logs" +4. Search for the string "Configure the build" +5. All of the build settings are listed below that starting with the + `configure:` prefix. + [GitHub Actions]: https://github.com/rust-lang/rust/actions [`jobs.yml`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/jobs.yml [`.github/workflows/ci.yml`]: https://github.com/rust-lang/rust/blob/master/.github/workflows/ci.yml -[`ci.py`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/ci.py +[`src/ci/github-actions/ci.py`]: https://github.com/rust-lang/rust/blob/master/src/ci/github-actions/ci.py [rust-lang-ci]: https://github.com/rust-lang-ci/rust/actions [bors]: https://github.com/bors [homu]: https://github.com/rust-lang/homu diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 7366e55d3146..9e0f8f9c279c 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -252,7 +252,7 @@ Consider writing the test as a proper incremental test instead. | Directive | Explanation | Supported test suites | Possible values | |-------------|--------------------------------------------------------------|------------------------------------------|---------------------------| -| `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `js-doc-test`, `rustdoc-json` | Any valid `rustdoc` flags | +| `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `rustdoc-js`, `rustdoc-json` | Any valid `rustdoc` flags | tests/fail/tail_calls/dangling-local-var.rs:LL:CC | -LL | } - | ^ +LL | become g(ptr) + | ^^^^^^^^^^^^^ = note: BACKTRACE (of the first span): = note: inside `g` at tests/fail/tail_calls/dangling-local-var.rs:LL:CC note: inside `main` diff --git a/src/tools/tidy/src/ext_tool_checks.rs b/src/tools/tidy/src/ext_tool_checks.rs index 6269d91c7ecc..2a4cff1d12ed 100644 --- a/src/tools/tidy/src/ext_tool_checks.rs +++ b/src/tools/tidy/src/ext_tool_checks.rs @@ -89,74 +89,45 @@ fn check_impl( if python_lint { eprintln!("linting python files"); - let mut cfg_args_ruff = cfg_args.clone(); - let mut file_args_ruff = file_args.clone(); - - let mut cfg_path = root_path.to_owned(); - cfg_path.extend(RUFF_CONFIG_PATH); - let mut cache_dir = outdir.to_owned(); - cache_dir.extend(RUFF_CACHE_PATH); - - cfg_args_ruff.extend([ - "--config".as_ref(), - cfg_path.as_os_str(), - "--cache-dir".as_ref(), - cache_dir.as_os_str(), - ]); - - if file_args_ruff.is_empty() { - file_args_ruff.push(root_path.as_os_str()); - } - - let mut args = merge_args(&cfg_args_ruff, &file_args_ruff); - args.insert(0, "check".as_ref()); - let res = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args); + let py_path = py_path.as_ref().unwrap(); + let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &["check".as_ref()]); if res.is_err() && show_diff { eprintln!("\npython linting failed! Printing diff suggestions:"); - args.insert(1, "--diff".as_ref()); - let _ = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args); + let _ = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &[ + "check".as_ref(), + "--diff".as_ref(), + ]); } // Rethrow error let _ = res?; } if python_fmt { - let mut cfg_args_ruff = cfg_args.clone(); - let mut file_args_ruff = file_args.clone(); - + let mut args: Vec<&OsStr> = vec!["format".as_ref()]; if bless { eprintln!("formatting python files"); } else { eprintln!("checking python file formatting"); - cfg_args_ruff.push("--check".as_ref()); + args.push("--check".as_ref()); } - let mut cfg_path = root_path.to_owned(); - cfg_path.extend(RUFF_CONFIG_PATH); - let mut cache_dir = outdir.to_owned(); - cache_dir.extend(RUFF_CACHE_PATH); + let py_path = py_path.as_ref().unwrap(); + let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &args); - cfg_args_ruff.extend(["--config".as_ref(), cfg_path.as_os_str()]); - - if file_args_ruff.is_empty() { - file_args_ruff.push(root_path.as_os_str()); - } - - let mut args = merge_args(&cfg_args_ruff, &file_args_ruff); - args.insert(0, "format".as_ref()); - let res = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args); - - if res.is_err() && show_diff { - eprintln!("\npython formatting does not match! Printing diff:"); - - args.insert(0, "--diff".as_ref()); - let _ = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args); - } if res.is_err() && !bless { + if show_diff { + eprintln!("\npython formatting does not match! Printing diff:"); + + let _ = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &[ + "format".as_ref(), + "--diff".as_ref(), + ]); + } eprintln!("rerun tidy with `--extra-checks=py:fmt --bless` to reformat Python code"); } + // Rethrow error let _ = res?; } @@ -247,6 +218,38 @@ fn check_impl( Ok(()) } +fn run_ruff( + root_path: &Path, + outdir: &Path, + py_path: &Path, + cfg_args: &[&OsStr], + file_args: &[&OsStr], + ruff_args: &[&OsStr], +) -> Result<(), Error> { + let mut cfg_args_ruff = cfg_args.into_iter().copied().collect::>(); + let mut file_args_ruff = file_args.into_iter().copied().collect::>(); + + let mut cfg_path = root_path.to_owned(); + cfg_path.extend(RUFF_CONFIG_PATH); + let mut cache_dir = outdir.to_owned(); + cache_dir.extend(RUFF_CACHE_PATH); + + cfg_args_ruff.extend([ + "--config".as_ref(), + cfg_path.as_os_str(), + "--cache-dir".as_ref(), + cache_dir.as_os_str(), + ]); + + if file_args_ruff.is_empty() { + file_args_ruff.push(root_path.as_os_str()); + } + + let mut args: Vec<&OsStr> = ruff_args.into_iter().copied().collect(); + args.extend(merge_args(&cfg_args_ruff, &file_args_ruff)); + py_runner(py_path, true, None, "ruff", &args) +} + /// Helper to create `cfg1 cfg2 -- file1 file2` output fn merge_args<'a>(cfg_args: &[&'a OsStr], file_args: &[&'a OsStr]) -> Vec<&'a OsStr> { let mut args = cfg_args.to_owned(); @@ -321,8 +324,16 @@ fn get_or_create_venv(venv_path: &Path, src_reqs_path: &Path) -> Result Result<(), Error> { /// Preferred python versions in order. Newest to oldest then current /// development versions - const TRY_PY: &[&str] = - &["python3.11", "python3.10", "python3.9", "python3", "python", "python3.12", "python3.13"]; + const TRY_PY: &[&str] = &[ + "python3.13", + "python3.12", + "python3.11", + "python3.10", + "python3.9", + "python3", + "python", + "python3.14", + ]; let mut sys_py = None; let mut found = Vec::new(); @@ -357,22 +368,40 @@ fn create_venv_at_path(path: &Path) -> Result<(), Error> { return Err(ret); }; - eprintln!("creating virtual environment at '{}' using '{sys_py}'", path.display()); - let out = Command::new(sys_py).args(["-m", "virtualenv"]).arg(path).output().unwrap(); + // First try venv, which should be packaged in the Python3 standard library. + // If it is not available, try to create the virtual environment using the + // virtualenv package. + if try_create_venv(sys_py, path, "venv").is_ok() { + return Ok(()); + } + try_create_venv(sys_py, path, "virtualenv") +} + +fn try_create_venv(python: &str, path: &Path, module: &str) -> Result<(), Error> { + eprintln!( + "creating virtual environment at '{}' using '{python}' and '{module}'", + path.display() + ); + let out = Command::new(python).args(["-m", module]).arg(path).output().unwrap(); if out.status.success() { return Ok(()); } let stderr = String::from_utf8_lossy(&out.stderr); - let err = if stderr.contains("No module named virtualenv") { + let err = if stderr.contains(&format!("No module named {module}")) { Error::Generic(format!( - "virtualenv not found: you may need to install it \ - (`{sys_py} -m pip install virtualenv`)" + r#"{module} not found: you may need to install it: +`{python} -m pip install {module}` +If you see an error about "externally managed environment" when running the above command, +either install `{module}` using your system package manager +(e.g. `sudo apt-get install {python}-{module}`) or create a virtual environment manually, install +`{module}` in it and then activate it before running tidy. +"# )) } else { Error::Generic(format!( - "failed to create venv at '{}' using {sys_py}: {stderr}", + "failed to create venv at '{}' using {python} -m {module}: {stderr}", path.display() )) }; diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 1c5452b38d17..0e5a7458b68c 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -3856,7 +3856,6 @@ ui/suggestions/issue-106443-sugg-clone-for-arg.rs ui/suggestions/issue-106443-sugg-clone-for-bound.rs ui/suggestions/issue-107860.rs ui/suggestions/issue-108470.rs -ui/suggestions/issue-109195.rs ui/suggestions/issue-109291.rs ui/suggestions/issue-109396.rs ui/suggestions/issue-109436.rs diff --git a/tests/assembly/powerpc64-struct-abi.rs b/tests/assembly/powerpc64-struct-abi.rs index 7052937acf66..db08a5148196 100644 --- a/tests/assembly/powerpc64-struct-abi.rs +++ b/tests/assembly/powerpc64-struct-abi.rs @@ -50,9 +50,9 @@ struct ThreeU8s(u8, u8, u8); // CHECK-LABEL: read_large // aix: lwz [[REG1:.*]], 16(4) -// aix-NEXT: lxvd2x 0, 0, 4 +// aix-NEXT: lxv{{d2x|w4x}} 0, 0, 4 // aix-NEXT: stw [[REG1]], 16(3) -// aix-NEXT: stxvd2x 0, 0, 3 +// aix-NEXT: stxv{{d2x|w4x}} 0, 0, 3 // be: lwz [[REG1:.*]], 16(4) // be-NEXT: stw [[REG1]], 16(3) // be-NEXT: ld [[REG2:.*]], 8(4) @@ -118,8 +118,8 @@ extern "C" fn read_small(x: &ThreeU8s) -> ThreeU8s { // aix-NEXT: std 4, 56(1) // aix-NEXT: stw [[REG1]], 16(6) // aix-NEXT: addi [[REG2:.*]], 1, 48 -// aix-NEXT: lxvd2x 0, 0, [[REG2]] -// aix-NEXT: stxvd2x 0, 0, 6 +// aix-NEXT: lxv{{d2x|w4x}} 0, 0, [[REG2]] +// aix-NEXT: stxv{{d2x|w4x}} 0, 0, 6 // elf: std 3, 0(6) // be-NEXT: rldicl [[REG1:.*]], 5, 32, 32 // elf-NEXT: std 4, 8(6) diff --git a/tests/assembly/targets/targets-elf.rs b/tests/assembly/targets/targets-elf.rs index acfa868c2df0..6bb3389c4095 100644 --- a/tests/assembly/targets/targets-elf.rs +++ b/tests/assembly/targets/targets-elf.rs @@ -66,6 +66,9 @@ //@ revisions: aarch64_unknown_teeos //@ [aarch64_unknown_teeos] compile-flags: --target aarch64-unknown-teeos //@ [aarch64_unknown_teeos] needs-llvm-components: aarch64 +//@ revisions: aarch64_unknown_nuttx +//@ [aarch64_unknown_nuttx] compile-flags: --target aarch64-unknown-nuttx +//@ [aarch64_unknown_nuttx] needs-llvm-components: aarch64 //@ revisions: aarch64_unknown_trusty //@ [aarch64_unknown_trusty] compile-flags: --target aarch64-unknown-trusty //@ [aarch64_unknown_trusty] needs-llvm-components: aarch64 @@ -177,6 +180,12 @@ //@ revisions: armv7a_none_eabihf //@ [armv7a_none_eabihf] compile-flags: --target armv7a-none-eabihf //@ [armv7a_none_eabihf] needs-llvm-components: arm +//@ revisions: armv7a_nuttx_eabi +//@ [armv7a_nuttx_eabi] compile-flags: --target armv7a-nuttx-eabi +//@ [armv7a_nuttx_eabi] needs-llvm-components: arm +//@ revisions: armv7a_nuttx_eabihf +//@ [armv7a_nuttx_eabihf] compile-flags: --target armv7a-nuttx-eabihf +//@ [armv7a_nuttx_eabihf] needs-llvm-components: arm //@ revisions: armv7r_none_eabi //@ [armv7r_none_eabi] compile-flags: --target armv7r-none-eabi //@ [armv7r_none_eabi] needs-llvm-components: arm @@ -621,6 +630,12 @@ //@ revisions: thumbv6m_nuttx_eabi //@ [thumbv6m_nuttx_eabi] compile-flags: --target thumbv6m-nuttx-eabi //@ [thumbv6m_nuttx_eabi] needs-llvm-components: arm +//@ revisions: thumbv7a_nuttx_eabi +//@ [thumbv7a_nuttx_eabi] compile-flags: --target thumbv7a-nuttx-eabi +//@ [thumbv7a_nuttx_eabi] needs-llvm-components: arm +//@ revisions: thumbv7a_nuttx_eabihf +//@ [thumbv7a_nuttx_eabihf] compile-flags: --target thumbv7a-nuttx-eabihf +//@ [thumbv7a_nuttx_eabihf] needs-llvm-components: arm //@ revisions: thumbv7m_nuttx_eabi //@ [thumbv7m_nuttx_eabi] compile-flags: --target thumbv7m-nuttx-eabi //@ [thumbv7m_nuttx_eabi] needs-llvm-components: arm diff --git a/tests/assembly/wasm32-naked-fn.rs b/tests/assembly/wasm32-naked-fn.rs new file mode 100644 index 000000000000..4911a6bd08f6 --- /dev/null +++ b/tests/assembly/wasm32-naked-fn.rs @@ -0,0 +1,199 @@ +// FIXME: add wasm32-unknown when the wasm32-unknown-unknown ABI is fixed +// see https://github.com/rust-lang/rust/issues/115666 +//@ revisions: wasm64-unknown wasm32-wasip1 +//@ add-core-stubs +//@ assembly-output: emit-asm +//@ [wasm64-unknown] compile-flags: --target wasm64-unknown-unknown +//@ [wasm32-wasip1] compile-flags: --target wasm32-wasip1 +//@ [wasm64-unknown] needs-llvm-components: webassembly +//@ [wasm32-wasip1] needs-llvm-components: webassembly + +#![crate_type = "lib"] +#![feature(no_core, naked_functions, asm_experimental_arch, f128, linkage, fn_align)] +#![no_core] + +extern crate minicore; +use minicore::*; + +// CHECK: .section .text.nop,"",@ +// CHECK: .globl nop +// CHECK-LABEL: nop: +// CHECK: .functype nop () -> () +// CHECK-NOT: .size +// CHECK: end_function +#[no_mangle] +#[naked] +unsafe extern "C" fn nop() { + naked_asm!("nop") +} + +// CHECK: .section .text.weak_aligned_nop,"",@ +// CHECK: .weak weak_aligned_nop +// CHECK-LABEL: nop: +// CHECK: .functype weak_aligned_nop () -> () +// CHECK-NOT: .size +// CHECK: end_function +#[no_mangle] +#[naked] +#[linkage = "weak"] +// wasm functions cannot be aligned, so this has no effect +#[repr(align(32))] +unsafe extern "C" fn weak_aligned_nop() { + naked_asm!("nop") +} + +// CHECK-LABEL: fn_i8_i8: +// CHECK-NEXT: .functype fn_i8_i8 (i32) -> (i32) +// +// CHECK-NEXT: local.get 0 +// CHECK-NEXT: local.get 0 +// CHECK-NEXT: i32.mul +// +// CHECK-NEXT: end_function +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_i8_i8(num: i8) -> i8 { + naked_asm!("local.get 0", "local.get 0", "i32.mul") +} + +// CHECK-LABEL: fn_i8_i8_i8: +// CHECK: .functype fn_i8_i8_i8 (i32, i32) -> (i32) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_i8_i8_i8(a: i8, b: i8) -> i8 { + naked_asm!("local.get 1", "local.get 0", "i32.mul") +} + +// CHECK-LABEL: fn_unit_i8: +// CHECK: .functype fn_unit_i8 () -> (i32) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_unit_i8() -> i8 { + naked_asm!("i32.const 42") +} + +// CHECK-LABEL: fn_i8_unit: +// CHECK: .functype fn_i8_unit (i32) -> () +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_i8_unit(_: i8) { + naked_asm!("nop") +} + +// CHECK-LABEL: fn_i32_i32: +// CHECK: .functype fn_i32_i32 (i32) -> (i32) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_i32_i32(num: i32) -> i32 { + naked_asm!("local.get 0", "local.get 0", "i32.mul") +} + +// CHECK-LABEL: fn_i64_i64: +// CHECK: .functype fn_i64_i64 (i64) -> (i64) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_i64_i64(num: i64) -> i64 { + naked_asm!("local.get 0", "local.get 0", "i64.mul") +} + +// CHECK-LABEL: fn_i128_i128: +// wasm32-wasip1: .functype fn_i128_i128 (i32, i64, i64) -> () +// wasm64-unknown: .functype fn_i128_i128 (i64, i64, i64) -> () +#[allow(improper_ctypes_definitions)] +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_i128_i128(num: i128) -> i128 { + naked_asm!( + "local.get 0", + "local.get 2", + "i64.store 8", + "local.get 0", + "local.get 1", + "i64.store 0", + ) +} + +// CHECK-LABEL: fn_f128_f128: +// wasm32-wasip1: .functype fn_f128_f128 (i32, i64, i64) -> () +// wasm64-unknown: .functype fn_f128_f128 (i64, i64, i64) -> () +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_f128_f128(num: f128) -> f128 { + naked_asm!( + "local.get 0", + "local.get 2", + "i64.store 8", + "local.get 0", + "local.get 1", + "i64.store 0", + ) +} + +#[repr(C)] +struct Compound { + a: u16, + b: i64, +} + +// CHECK-LABEL: fn_compound_compound: +// wasm32-wasip1: .functype fn_compound_compound (i32, i32) -> () +// wasm64-unknown: .functype fn_compound_compound (i64, i64) -> () +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_compound_compound(_: Compound) -> Compound { + // this is the wasm32-wasip1 assembly + naked_asm!( + "local.get 0", + "local.get 1", + "i64.load 8", + "i64.store 8", + "local.get 0", + "local.get 1", + "i32.load16_u 0", + "i32.store16 0", + ) +} + +#[repr(C)] +struct WrapperI32(i32); + +// CHECK-LABEL: fn_wrapperi32_wrapperi32: +// CHECK: .functype fn_wrapperi32_wrapperi32 (i32) -> (i32) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_wrapperi32_wrapperi32(_: WrapperI32) -> WrapperI32 { + naked_asm!("local.get 0") +} + +#[repr(C)] +struct WrapperI64(i64); + +// CHECK-LABEL: fn_wrapperi64_wrapperi64: +// CHECK: .functype fn_wrapperi64_wrapperi64 (i64) -> (i64) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_wrapperi64_wrapperi64(_: WrapperI64) -> WrapperI64 { + naked_asm!("local.get 0") +} + +#[repr(C)] +struct WrapperF32(f32); + +// CHECK-LABEL: fn_wrapperf32_wrapperf32: +// CHECK: .functype fn_wrapperf32_wrapperf32 (f32) -> (f32) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_wrapperf32_wrapperf32(_: WrapperF32) -> WrapperF32 { + naked_asm!("local.get 0") +} + +#[repr(C)] +struct WrapperF64(f64); + +// CHECK-LABEL: fn_wrapperf64_wrapperf64: +// CHECK: .functype fn_wrapperf64_wrapperf64 (f64) -> (f64) +#[no_mangle] +#[naked] +unsafe extern "C" fn fn_wrapperf64_wrapperf64(_: WrapperF64) -> WrapperF64 { + naked_asm!("local.get 0") +} diff --git a/tests/crashes/128097.rs b/tests/crashes/128097.rs deleted file mode 100644 index 6ffca640cbd6..000000000000 --- a/tests/crashes/128097.rs +++ /dev/null @@ -1,6 +0,0 @@ -//@ known-bug: #128097 -#![feature(explicit_tail_calls)] -fn f(x: &mut ()) { - let _y: String; - become f(x); -} diff --git a/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff index 148ff86354b5..a91d88984a8a 100644 --- a/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff +++ b/tests/mir-opt/coverage/instrument_coverage.bar.InstrumentCoverage.diff @@ -4,8 +4,8 @@ fn bar() -> bool { let mut _0: bool; -+ coverage body span: $DIR/instrument_coverage.rs:19:18: 21:2 (#0) -+ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:19:1: 21:2 (#0); ++ coverage body span: $DIR/instrument_coverage.rs:29:18: 31:2 (#0) ++ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:29:1: 31:2 (#0); + bb0: { + Coverage::CounterIncrement(0); diff --git a/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff b/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff index fa09cf0b83f8..d7ea442518eb 100644 --- a/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff +++ b/tests/mir-opt/coverage/instrument_coverage.main.InstrumentCoverage.diff @@ -7,13 +7,13 @@ let mut _2: bool; let mut _3: !; -+ coverage body span: $DIR/instrument_coverage.rs:10:11: 16:2 (#0) ++ coverage body span: $DIR/instrument_coverage.rs:14:11: 20:2 (#0) + coverage ExpressionId(0) => Expression { lhs: Counter(1), op: Subtract, rhs: Counter(0) }; -+ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:10:1: 10:11 (#0); -+ coverage Code(Counter(1)) => $DIR/instrument_coverage.rs:12:12: 12:17 (#0); -+ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:13:13: 13:18 (#0); -+ coverage Code(Expression(0)) => $DIR/instrument_coverage.rs:14:10: 14:10 (#0); -+ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:16:2: 16:2 (#0); ++ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:14:1: 14:11 (#0); ++ coverage Code(Counter(1)) => $DIR/instrument_coverage.rs:16:12: 16:17 (#0); ++ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:17:13: 17:18 (#0); ++ coverage Code(Expression(0)) => $DIR/instrument_coverage.rs:18:10: 18:10 (#0); ++ coverage Code(Counter(0)) => $DIR/instrument_coverage.rs:20:2: 20:2 (#0); + bb0: { + Coverage::CounterIncrement(0); diff --git a/tests/mir-opt/coverage/instrument_coverage.rs b/tests/mir-opt/coverage/instrument_coverage.rs index beb88b607f97..c49786f96151 100644 --- a/tests/mir-opt/coverage/instrument_coverage.rs +++ b/tests/mir-opt/coverage/instrument_coverage.rs @@ -6,7 +6,11 @@ //@ compile-flags: -Cinstrument-coverage -Zno-profiler-runtime // EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff -// EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff +// CHECK-LABEL: fn main() +// CHECK: coverage body span: +// CHECK: coverage Code(Counter({{[0-9]+}})) => +// CHECK: bb0: +// CHECK: Coverage::CounterIncrement fn main() { loop { if bar() { @@ -15,14 +19,13 @@ fn main() { } } +// EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff +// CHECK-LABEL: fn bar() +// CHECK: coverage body span: +// CHECK: coverage Code(Counter({{[0-9]+}})) => +// CHECK: bb0: +// CHECK: Coverage::CounterIncrement #[inline(never)] fn bar() -> bool { true } - -// CHECK: coverage ExpressionId({{[0-9]+}}) => -// CHECK-DAG: coverage Code(Counter({{[0-9]+}})) => -// CHECK-DAG: coverage Code(Expression({{[0-9]+}})) => -// CHECK: bb0: -// CHECK-DAG: Coverage::ExpressionUsed({{[0-9]+}}) -// CHECK-DAG: Coverage::CounterIncrement({{[0-9]+}}) 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 index 17c64d4baf0e..9a4f27a497d1 100644 --- a/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-abort.diff +++ b/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-abort.diff @@ -66,7 +66,6 @@ bb6: { + _8 = const false; StorageDead(_4); - StorageDead(_3); drop(_2) -> [return: bb7, unwind: bb12]; } 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 index 58d8a87986d6..f13ee78aa368 100644 --- a/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-unwind.diff +++ b/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-unwind.diff @@ -66,7 +66,6 @@ bb6: { + _8 = const false; StorageDead(_4); - StorageDead(_3); - drop(_2) -> [return: bb7, unwind continue]; + drop(_2) -> [return: bb7, unwind: bb12]; } 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 index 2c3d62491d71..e017424a4ccd 100644 --- 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 @@ -63,7 +63,6 @@ fn f() -> () { bb6: { StorageDead(_4); - StorageDead(_3); drop(_2) -> [return: bb7, unwind: bb17]; } 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 index 2c3d62491d71..e017424a4ccd 100644 --- 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 @@ -63,7 +63,6 @@ fn f() -> () { bb6: { StorageDead(_4); - StorageDead(_3); drop(_2) -> [return: bb7, unwind: bb17]; } 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 index 1a51601bc56f..a8c57d2cfe00 100644 --- 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 @@ -80,7 +80,6 @@ bb8: { + _12 = const false; StorageDead(_6); - StorageDead(_5); drop(_4) -> [return: bb9, unwind: bb16]; } 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 index 1a51601bc56f..a8c57d2cfe00 100644 --- 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 @@ -80,7 +80,6 @@ bb8: { + _12 = const false; StorageDead(_6); - StorageDead(_5); drop(_4) -> [return: bb9, unwind: bb16]; } 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 index 744f1989acc9..f89b98a32053 100644 --- 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 @@ -77,7 +77,6 @@ fn f_with_arg(_1: String, _2: String) -> () { bb8: { StorageDead(_6); - StorageDead(_5); drop(_4) -> [return: bb9, unwind: bb23]; } 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 index 744f1989acc9..f89b98a32053 100644 --- 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 @@ -77,7 +77,6 @@ fn f_with_arg(_1: String, _2: String) -> () { bb8: { StorageDead(_6); - StorageDead(_5); drop(_4) -> [return: bb9, unwind: bb23]; } diff --git a/tests/rustdoc-gui/search-tab.goml b/tests/rustdoc-gui/search-tab.goml index eea561e0c676..826e272e508e 100644 --- a/tests/rustdoc-gui/search-tab.goml +++ b/tests/rustdoc-gui/search-tab.goml @@ -78,7 +78,7 @@ call-function: ("check-colors", { set-window-size: (851, 600) // Check the size and count in tabs -assert-text: ("#search-tabs > button:nth-child(1) > .count", " (26) ") +assert-text: ("#search-tabs > button:nth-child(1) > .count", " (27) ") assert-text: ("#search-tabs > button:nth-child(2) > .count", " (7)  ") assert-text: ("#search-tabs > button:nth-child(3) > .count", " (0)  ") store-property: ("#search-tabs > button:nth-child(1)", {"offsetWidth": buttonWidth}) diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs index dae13d9ac180..1a9ffbe88985 100644 --- a/tests/rustdoc-gui/src/test_docs/lib.rs +++ b/tests/rustdoc-gui/src/test_docs/lib.rs @@ -691,3 +691,25 @@ impl ImplDoc { impl ImplDoc { pub fn bar5() {} } + +pub trait ItemsTrait { + /// You want doc, here is doc! + /// + /// blablala + type F; + + /// You want doc, here is doc! + /// + /// blablala + const X: u32; + + /// You want doc, here is doc! + /// + /// blablala + fn foo() {} + + /// You want doc, here is doc! + /// + /// blablala + fn bar(); +} diff --git a/tests/rustdoc-gui/toggle-docs-mobile.goml b/tests/rustdoc-gui/toggle-docs-mobile.goml index be12e4c19b3b..6a40ba83b843 100644 --- a/tests/rustdoc-gui/toggle-docs-mobile.goml +++ b/tests/rustdoc-gui/toggle-docs-mobile.goml @@ -31,3 +31,29 @@ assert-attribute: (".top-doc", {"open": ""}) // To ensure that the toggle isn't over the text, we check that the toggle isn't clicked. click: (3, 270) assert-attribute: (".top-doc", {"open": ""}) + +// Same check on trait items. +fail-on-request-error: false // To prevent downloads errors on "trait.impl/test_docs/trait.ItemsTrait.js" +go-to: "file://" + |DOC_PATH| + "/test_docs/trait.ItemsTrait.html" + +define-function: ( + "check-trait-item", + [nth, text], + block { + store-value: (selector, ".methods:nth-of-type(" + |nth| + ") > details summary") + assert-text: (|selector| + " h4", |text|) + assert-position: ( + |selector| + "::before", + {"x": 6}, + ) + }, +) + +// Assert the position of the toggle on an associated const. +call-function: ("check-trait-item", {"nth": 2, "text": "const X: u32"}) +// Assert the position of the toggle on an associated type. +call-function: ("check-trait-item", {"nth": 3, "text": "type F"}) +// Assert the position of the toggle on an associated required method. +call-function: ("check-trait-item", {"nth": 4, "text": "fn bar()"}) +// Assert the position of the toggle on an associated provided method. +call-function: ("check-trait-item", {"nth": 5, "text": "fn foo()"}) diff --git a/tests/rustdoc-json/auxiliary/defines_and_reexports.rs b/tests/rustdoc-json/auxiliary/defines_and_reexports.rs new file mode 100644 index 000000000000..72434ef152f3 --- /dev/null +++ b/tests/rustdoc-json/auxiliary/defines_and_reexports.rs @@ -0,0 +1,10 @@ +pub mod m1 { + pub struct InPubMod; +} + +mod m2 { + pub struct InPrivMod; +} + +pub use m1::{InPubMod, InPubMod as InPubMod2}; +pub use m2::{InPrivMod, InPrivMod as InPrivMod2}; diff --git a/tests/rustdoc-json/blanket_impls.rs b/tests/rustdoc-json/blanket_impls.rs index f2acabbe3725..bf0983e66a17 100644 --- a/tests/rustdoc-json/blanket_impls.rs +++ b/tests/rustdoc-json/blanket_impls.rs @@ -4,5 +4,5 @@ //@ has "$.index[*][?(@.name=='Error')].inner.assoc_type" //@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.type.resolved_path" -//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.type.resolved_path.name" \"Infallible\" +//@ has "$.index[*][?(@.name=='Error')].inner.assoc_type.type.resolved_path.path" \"Infallible\" pub struct ForBlanketTryFromImpl; diff --git a/tests/rustdoc-json/fns/async_return.rs b/tests/rustdoc-json/fns/async_return.rs index 18a8a586e76d..ff88fa99c61b 100644 --- a/tests/rustdoc-json/fns/async_return.rs +++ b/tests/rustdoc-json/fns/async_return.rs @@ -17,7 +17,7 @@ pub async fn get_int_async() -> i32 { 42 } -//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.name" '"Future"' +//@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.path" '"Future"' //@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name" '"Output"' //@ is "$.index[*][?(@.name=='get_int_future')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive" \"i32\" //@ is "$.index[*][?(@.name=='get_int_future')].inner.function.header.is_async" false @@ -25,7 +25,7 @@ pub fn get_int_future() -> impl Future { async { 42 } } -//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.name" '"Future"' +//@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.path" '"Future"' //@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name" '"Output"' //@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.sig.output.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive" \"i32\" //@ is "$.index[*][?(@.name=='get_int_future_async')].inner.function.header.is_async" true diff --git a/tests/rustdoc-json/impl-trait-in-assoc-type.rs b/tests/rustdoc-json/impl-trait-in-assoc-type.rs index 907a0f6c603e..14ea29507692 100644 --- a/tests/rustdoc-json/impl-trait-in-assoc-type.rs +++ b/tests/rustdoc-json/impl-trait-in-assoc-type.rs @@ -10,7 +10,7 @@ impl IntoIterator for AlwaysTrue { type Item = bool; //@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[*]' 1 - //@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.name' '"Iterator"' + //@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.path' '"Iterator"' //@ count '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[*]' 1 //@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].name' '"Item"' //@ is '$.index[*][?(@.docs=="type IntoIter")].inner.assoc_type.type.impl_trait[0].trait_bound.trait.args.angle_bracketed.constraints[0].binding.equality.type.primitive' '"bool"' diff --git a/tests/rustdoc-json/path_name.rs b/tests/rustdoc-json/path_name.rs new file mode 100644 index 000000000000..67843dfc8ff3 --- /dev/null +++ b/tests/rustdoc-json/path_name.rs @@ -0,0 +1,83 @@ +// Test for the Path::name field within a single crate. +// +// See https://github.com/rust-lang/rust/issues/135600 +// and https://github.com/rust-lang/rust/pull/134880#issuecomment-2596386111 +// +// ignore-tidy-linelength +//@ aux-build: defines_and_reexports.rs +extern crate defines_and_reexports; + +mod priv_mod { + pub struct InPrivMod; +} + +pub mod pub_mod { + pub struct InPubMod; +} + +use priv_mod::InPrivMod as InPrivMod3; +pub use priv_mod::{InPrivMod, InPrivMod as InPrivMod2}; +use pub_mod::InPubMod as InPubMod3; +pub use pub_mod::{InPubMod, InPubMod as InPubMod2}; + +//@ is "$.index[*][?(@.name=='T0')].inner.type_alias.type.resolved_path.path" '"priv_mod::InPrivMod"' +pub type T0 = priv_mod::InPrivMod; +//@ is "$.index[*][?(@.name=='T1')].inner.type_alias.type.resolved_path.path" '"InPrivMod"' +pub type T1 = InPrivMod; +//@ is "$.index[*][?(@.name=='T2')].inner.type_alias.type.resolved_path.path" '"InPrivMod2"' +pub type T2 = InPrivMod2; +//@ is "$.index[*][?(@.name=='T3')].inner.type_alias.type.resolved_path.path" '"priv_mod::InPrivMod"' +pub type T3 = InPrivMod3; + +//@ is "$.index[*][?(@.name=='U0')].inner.type_alias.type.resolved_path.path" '"pub_mod::InPubMod"' +pub type U0 = pub_mod::InPubMod; +//@ is "$.index[*][?(@.name=='U1')].inner.type_alias.type.resolved_path.path" '"InPubMod"' +pub type U1 = InPubMod; +//@ is "$.index[*][?(@.name=='U2')].inner.type_alias.type.resolved_path.path" '"InPubMod2"' +pub type U2 = InPubMod2; +//@ is "$.index[*][?(@.name=='U3')].inner.type_alias.type.resolved_path.path" '"pub_mod::InPubMod"' +pub type U3 = InPubMod3; + +// Check we only have paths for structs at their original path +//@ ismany "$.paths[*][?(@.crate_id==0 && @.kind=='struct')].path" '["path_name", "priv_mod", "InPrivMod"]' '["path_name", "pub_mod", "InPubMod"]' + +pub use defines_and_reexports::{InPrivMod as XPrivMod, InPubMod as XPubMod}; +use defines_and_reexports::{InPrivMod as XPrivMod2, InPubMod as XPubMod2}; + +//@ is "$.index[*][?(@.name=='X0')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::m1::InPubMod"' +pub type X0 = defines_and_reexports::m1::InPubMod; +//@ is "$.index[*][?(@.name=='X1')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::InPubMod"' +pub type X1 = defines_and_reexports::InPubMod; +//@ is "$.index[*][?(@.name=='X2')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::InPubMod2"' +pub type X2 = defines_and_reexports::InPubMod2; +//@ is "$.index[*][?(@.name=='X3')].inner.type_alias.type.resolved_path.path" '"XPubMod"' +pub type X3 = XPubMod; +// N.B. This isn't the path as used *or* the original path! +//@ is "$.index[*][?(@.name=='X4')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::InPubMod"' +pub type X4 = XPubMod2; + +//@ is "$.index[*][?(@.name=='Y1')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::InPrivMod"' +pub type Y1 = defines_and_reexports::InPrivMod; +//@ is "$.index[*][?(@.name=='Y2')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::InPrivMod2"' +pub type Y2 = defines_and_reexports::InPrivMod2; +//@ is "$.index[*][?(@.name=='Y3')].inner.type_alias.type.resolved_path.path" '"XPrivMod"' +pub type Y3 = XPrivMod; +//@ is "$.index[*][?(@.name=='Y4')].inner.type_alias.type.resolved_path.path" '"defines_and_reexports::InPrivMod"' +pub type Y4 = XPrivMod2; + +// For foreign items, $.paths contains the *origional* path, even if it's not publicly +// assessable. This should probably be changed. + +//@ has "$.paths[*].path" '["defines_and_reexports", "m1", "InPubMod"]' +//@ has "$.paths[*].path" '["defines_and_reexports", "m2", "InPrivMod"]' +//@ !has "$.paths[*].path" '["defines_and_reexports", "InPubMod"]' +//@ !has "$.paths[*].path" '["defines_and_reexports", "InPrivMod"]' + +// Tests for the example in the docs of Path::name. +// If these change, chage the docs. +//@ is "$.index[*][?(@.name=='Vec1')].inner.type_alias.type.resolved_path.path" '"std::vec::Vec"' +pub type Vec1 = std::vec::Vec; +//@ is "$.index[*][?(@.name=='Vec2')].inner.type_alias.type.resolved_path.path" '"Vec"' +pub type Vec2 = Vec; +//@ is "$.index[*][?(@.name=='Vec3')].inner.type_alias.type.resolved_path.path" '"std::prelude::v1::Vec"' +pub type Vec3 = std::prelude::v1::Vec; diff --git a/tests/rustdoc-json/return_private.rs b/tests/rustdoc-json/return_private.rs index c238a536e0d8..bfcbed890400 100644 --- a/tests/rustdoc-json/return_private.rs +++ b/tests/rustdoc-json/return_private.rs @@ -2,11 +2,13 @@ // ignore-tidy-linelength mod secret { + //@ set struct_secret = "$.index[*][?(@.name == 'Secret' && @.inner.struct)].id" pub struct Secret; } //@ has "$.index[*][?(@.name=='get_secret')].inner.function" -//@ is "$.index[*][?(@.name=='get_secret')].inner.function.sig.output.resolved_path.name" \"Secret\" +//@ is "$.index[*][?(@.name=='get_secret')].inner.function.sig.output.resolved_path.path" '"secret::Secret"' +//@ is "$.index[*][?(@.name=='get_secret')].inner.function.sig.output.resolved_path.id" $struct_secret pub fn get_secret() -> secret::Secret { secret::Secret } diff --git a/tests/rustdoc-json/type/dyn.rs b/tests/rustdoc-json/type/dyn.rs index 97c8689a7c80..f990a2cb53a7 100644 --- a/tests/rustdoc-json/type/dyn.rs +++ b/tests/rustdoc-json/type/dyn.rs @@ -10,7 +10,7 @@ use std::fmt::Debug; //@ has "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias" //@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.generics" '{"params": [], "where_predicates": []}' //@ has "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path" -//@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.name" \"Box\" +//@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.path" \"Box\" //@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.constraints" [] //@ count "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args" 1 //@ has "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait" @@ -19,9 +19,9 @@ use std::fmt::Debug; //@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].generic_params" [] //@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[1].generic_params" [] //@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[2].generic_params" [] -//@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].trait.name" '"Fn"' -//@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[1].trait.name" '"Send"' -//@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[2].trait.name" '"Sync"' +//@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].trait.path" '"Fn"' +//@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[1].trait.path" '"Send"' +//@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[2].trait.path" '"Sync"' //@ is "$.index[*][?(@.name=='SyncIntGen')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].trait.args" '{"parenthesized": {"inputs": [],"output": {"primitive": "i32"}}}' pub type SyncIntGen = Box i32 + Send + Sync + 'static>; @@ -34,13 +34,13 @@ pub type SyncIntGen = Box i32 + Send + Sync + 'static>; //@ is "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.lifetime" null //@ count "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[*]" 1 //@ is "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]' -//@ is "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].trait.name" '"Fn"' +//@ is "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].trait.path" '"Fn"' //@ has "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].trait.args.parenthesized.inputs[0].borrowed_ref" //@ is "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].trait.args.parenthesized.inputs[0].borrowed_ref.lifetime" "\"'b\"" //@ has "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].trait.args.parenthesized.output.borrowed_ref" //@ is "$.index[*][?(@.name=='RefFn')].inner.type_alias.type.borrowed_ref.type.dyn_trait.traits[0].trait.args.parenthesized.output.borrowed_ref.lifetime" "\"'b\"" pub type RefFn<'a> = &'a dyn for<'b> Fn(&'b i32) -> &'b i32; -//@ is "$.index[*][?(@.name=='WeirdOrder')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].trait.name" '"Send"' -//@ is "$.index[*][?(@.name=='WeirdOrder')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[1].trait.name" '"Debug"' +//@ is "$.index[*][?(@.name=='WeirdOrder')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[0].trait.path" '"Send"' +//@ is "$.index[*][?(@.name=='WeirdOrder')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.dyn_trait.traits[1].trait.path" '"Debug"' pub type WeirdOrder = Box; diff --git a/tests/rustdoc-json/type/generic_default.rs b/tests/rustdoc-json/type/generic_default.rs index c1a05805014a..7eaa299af5c7 100644 --- a/tests/rustdoc-json/type/generic_default.rs +++ b/tests/rustdoc-json/type/generic_default.rs @@ -21,10 +21,10 @@ pub struct MyError {} //@ is "$.index[*][?(@.name=='MyResult')].inner.type_alias.generics.params[0].kind.type.default" null //@ has "$.index[*][?(@.name=='MyResult')].inner.type_alias.generics.params[1].kind.type.default.resolved_path" //@ is "$.index[*][?(@.name=='MyResult')].inner.type_alias.generics.params[1].kind.type.default.resolved_path.id" $my_error -//@ is "$.index[*][?(@.name=='MyResult')].inner.type_alias.generics.params[1].kind.type.default.resolved_path.name" \"MyError\" +//@ is "$.index[*][?(@.name=='MyResult')].inner.type_alias.generics.params[1].kind.type.default.resolved_path.path" \"MyError\" //@ has "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path" //@ is "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path.id" $result -//@ is "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path.name" \"Result\" +//@ is "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path.path" \"Result\" //@ is "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path.args.angle_bracketed.constraints" [] //@ has "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[0].type.generic" //@ has "$.index[*][?(@.name=='MyResult')].inner.type_alias.type.resolved_path.args.angle_bracketed.args[1].type.generic" diff --git a/tests/rustdoc-json/type/hrtb.rs b/tests/rustdoc-json/type/hrtb.rs index 825720e9198b..e71d9fc1e1e9 100644 --- a/tests/rustdoc-json/type/hrtb.rs +++ b/tests/rustdoc-json/type/hrtb.rs @@ -15,7 +15,7 @@ where //@ is "$.index[*][?(@.name=='dynfn')].inner.function.sig.inputs[0][1].borrowed_ref.type.dyn_trait.lifetime" null //@ count "$.index[*][?(@.name=='dynfn')].inner.function.sig.inputs[0][1].borrowed_ref.type.dyn_trait.traits[*]" 1 //@ is "$.index[*][?(@.name=='dynfn')].inner.function.sig.inputs[0][1].borrowed_ref.type.dyn_trait.traits[0].generic_params" '[{"kind": {"lifetime": {"outlives": []}},"name": "'\''a"},{"kind": {"lifetime": {"outlives": []}},"name": "'\''b"}]' -//@ is "$.index[*][?(@.name=='dynfn')].inner.function.sig.inputs[0][1].borrowed_ref.type.dyn_trait.traits[0].trait.name" '"Fn"' +//@ is "$.index[*][?(@.name=='dynfn')].inner.function.sig.inputs[0][1].borrowed_ref.type.dyn_trait.traits[0].trait.path" '"Fn"' pub fn dynfn(f: &dyn for<'a, 'b> Fn(&'a i32, &'b i32)) { let zero = 0; f(&zero, &zero); diff --git a/tests/ui/closures/binder/forbid_ambig_const_infers.rs b/tests/ui/closures/binder/forbid_ambig_const_infers.rs new file mode 100644 index 000000000000..e9d783711ee3 --- /dev/null +++ b/tests/ui/closures/binder/forbid_ambig_const_infers.rs @@ -0,0 +1,9 @@ +#![feature(generic_arg_infer, closure_lifetime_binder)] + +struct Foo([u32; N]); + +fn main() { + let c = for<'a> |b: &'a Foo<_>| -> u32 { b.0[0] }; + //~^ ERROR: implicit types in closure signatures are forbidden when `for<...>` is present + c(&Foo([1_u32; 1])); +} diff --git a/tests/ui/closures/binder/forbid_ambig_const_infers.stderr b/tests/ui/closures/binder/forbid_ambig_const_infers.stderr new file mode 100644 index 000000000000..396c9e8c916e --- /dev/null +++ b/tests/ui/closures/binder/forbid_ambig_const_infers.stderr @@ -0,0 +1,10 @@ +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/forbid_ambig_const_infers.rs:6:33 + | +LL | let c = for<'a> |b: &'a Foo<_>| -> u32 { b.0[0] }; + | ------- ^ + | | + | `for<...>` is here + +error: aborting due to 1 previous error + diff --git a/tests/ui/closures/binder/forbid_ambig_type_infers.rs b/tests/ui/closures/binder/forbid_ambig_type_infers.rs new file mode 100644 index 000000000000..4e717ef3a179 --- /dev/null +++ b/tests/ui/closures/binder/forbid_ambig_type_infers.rs @@ -0,0 +1,9 @@ +#![feature(generic_arg_infer, closure_lifetime_binder)] + +struct Foo(T); + +fn main() { + let c = for<'a> |b: &'a Foo<_>| -> u32 { b.0 }; + //~^ ERROR: implicit types in closure signatures are forbidden when `for<...>` is present + c(&Foo(1_u32)); +} diff --git a/tests/ui/closures/binder/forbid_ambig_type_infers.stderr b/tests/ui/closures/binder/forbid_ambig_type_infers.stderr new file mode 100644 index 000000000000..8f19d710073b --- /dev/null +++ b/tests/ui/closures/binder/forbid_ambig_type_infers.stderr @@ -0,0 +1,10 @@ +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/forbid_ambig_type_infers.rs:6:33 + | +LL | let c = for<'a> |b: &'a Foo<_>| -> u32 { b.0 }; + | ------- ^ + | | + | `for<...>` is here + +error: aborting due to 1 previous error + diff --git a/tests/ui/closures/binder/forbid_const_infer.rs b/tests/ui/closures/binder/forbid_const_infer.rs new file mode 100644 index 000000000000..f5b8bf188dfe --- /dev/null +++ b/tests/ui/closures/binder/forbid_const_infer.rs @@ -0,0 +1,7 @@ +#![feature(generic_arg_infer, closure_lifetime_binder)] + +fn main() { + let c = for<'a> |b: &'a [u32; _]| -> u32 { b[0] }; + //~^ ERROR: implicit types in closure signatures are forbidden when `for<...>` is present + c(&[1_u32; 2]); +} diff --git a/tests/ui/closures/binder/forbid_const_infer.stderr b/tests/ui/closures/binder/forbid_const_infer.stderr new file mode 100644 index 000000000000..e93685d400e2 --- /dev/null +++ b/tests/ui/closures/binder/forbid_const_infer.stderr @@ -0,0 +1,10 @@ +error: implicit types in closure signatures are forbidden when `for<...>` is present + --> $DIR/forbid_const_infer.rs:4:35 + | +LL | let c = for<'a> |b: &'a [u32; _]| -> u32 { b[0] }; + | ------- ^ + | | + | `for<...>` is here + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/generic_arg_infer/parend_infer.rs b/tests/ui/const-generics/generic_arg_infer/parend_infer.rs new file mode 100644 index 000000000000..81c42183b383 --- /dev/null +++ b/tests/ui/const-generics/generic_arg_infer/parend_infer.rs @@ -0,0 +1,12 @@ +//@ check-pass +//@ revisions: gate nogate +#![cfg_attr(gate, feature(generic_arg_infer))] + +fn main() { + // AST Types preserve parens for pretty printing reasons. This means + // that this is parsed as a `TyKind::Paren(TyKind::Infer)`. Generic + // arg lowering therefore needs to take into account not just `TyKind::Infer` + // but `TyKind::Infer` wrapped in arbitrarily many `TyKind::Paren`. + let a: Vec<(_)> = vec![1_u8]; + let a: Vec<(((((_)))))> = vec![1_u8]; +} diff --git a/tests/ui/const-generics/issues/issue-62878.min.stderr b/tests/ui/const-generics/issues/issue-62878.min.stderr index bd17d70a50ba..1bb111b188df 100644 --- a/tests/ui/const-generics/issues/issue-62878.min.stderr +++ b/tests/ui/const-generics/issues/issue-62878.min.stderr @@ -18,19 +18,17 @@ help: add `#![feature(adt_const_params)]` to the crate attributes to enable more LL + #![feature(adt_const_params)] | -error[E0747]: type provided when a constant was expected +error[E0658]: const arguments cannot yet be inferred with `_` --> $DIR/issue-62878.rs:10:11 | LL | foo::<_, { [1] }>(); | ^ | - = help: const arguments cannot yet be inferred with `_` -help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable - | -LL + #![feature(generic_arg_infer)] - | + = note: see issue #85077 for more information + = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 3 previous errors -Some errors have detailed explanations: E0747, E0770. -For more information about an error, try `rustc --explain E0747`. +Some errors have detailed explanations: E0658, E0770. +For more information about an error, try `rustc --explain E0658`. diff --git a/tests/ui/const-generics/issues/issue-62878.rs b/tests/ui/const-generics/issues/issue-62878.rs index 0b5269df85ee..c80b46ddbc44 100644 --- a/tests/ui/const-generics/issues/issue-62878.rs +++ b/tests/ui/const-generics/issues/issue-62878.rs @@ -8,5 +8,5 @@ fn foo() {} fn main() { foo::<_, { [1] }>(); - //[min]~^ ERROR: type provided when a constant was expected + //[min]~^ ERROR: const arguments cannot yet be inferred with `_` } diff --git a/tests/ui/diagnostic-width/secondary-label-with-long-type.rs b/tests/ui/diagnostic-width/secondary-label-with-long-type.rs new file mode 100644 index 000000000000..6ed600c48aca --- /dev/null +++ b/tests/ui/diagnostic-width/secondary-label-with-long-type.rs @@ -0,0 +1,17 @@ +//@ compile-flags: --diagnostic-width=100 -Zwrite-long-types-to-disk=yes +//@ normalize-stderr: "long-type-\d+" -> "long-type-hash" +type A = (i32, i32, i32, i32); +type B = (A, A, A, A); +type C = (B, B, B, B); +type D = (C, C, C, C); + +fn foo(x: D) { + let () = x; //~ ERROR mismatched types + //~^ NOTE this expression has type `((..., + //~| NOTE expected `((..., + //~| NOTE expected tuple + //~| NOTE the full type name has been written to + //~| NOTE consider using `--verbose` to print the full type name to the console +} + +fn main() {} diff --git a/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr b/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr new file mode 100644 index 000000000000..1e8904551569 --- /dev/null +++ b/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr @@ -0,0 +1,16 @@ +error[E0308]: mismatched types + --> $DIR/secondary-label-with-long-type.rs:9:9 + | +LL | let () = x; + | ^^ - this expression has type `((..., ..., ..., ...), ..., ..., ...)` + | | + | expected `((..., ..., ..., ...), ..., ..., ...)`, found `()` + | + = note: expected tuple `((..., ..., ..., ...), ..., ..., ...)` + found unit type `()` + = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/secondary-label-with-long-type/secondary-label-with-long-type.long-type-hash.txt' + = note: consider using `--verbose` to print the full type name to the console + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/did_you_mean/bad-assoc-ty.stderr b/tests/ui/did_you_mean/bad-assoc-ty.stderr index 41039ae82a6f..5fc2f7c1fe68 100644 --- a/tests/ui/did_you_mean/bad-assoc-ty.stderr +++ b/tests/ui/did_you_mean/bad-assoc-ty.stderr @@ -233,11 +233,6 @@ LL | fn foo>(x: X) {} | ^ ^ not allowed in type signatures | | | not allowed in type signatures - | -help: use type parameters instead - | -LL | fn foo, T>(x: X) {} - | ~ ~ +++ error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/bad-assoc-ty.rs:54:34 diff --git a/tests/ui/drop/drop-order-comparisons.e2021.fixed b/tests/ui/drop/drop-order-comparisons.e2021.fixed new file mode 100644 index 000000000000..78cf421cfbf3 --- /dev/null +++ b/tests/ui/drop/drop-order-comparisons.e2021.fixed @@ -0,0 +1,575 @@ +// This tests various aspects of the drop order with a focus on: +// +// - The lifetime of temporaries with the `if let` construct (and with +// various similar constructs) and how these lifetimes were shortened +// for `if let` in Rust 2024. +// +// - The shortening of the lifetimes of temporaries in tail +// expressions in Rust 2024. +// +// - The behavior of `let` chains and how this behavior compares to +// nested `if let` expressions and chained `let .. else` statements. +// +// In the tests below, `Events` tracks a sequence of numbered events. +// Calling `e.mark(..)` logs a numbered event immediately. Calling +// `e.ok(..)` or `e.err(..)` returns an `Ok(_)` or `Err(_)` value, +// respectively, and logs the numbered event when that value is +// dropped. Calling `e.assert()` verifies that the correct number of +// events were logged and that they were logged in the correct order. + +//@ revisions: e2021 e2024 +//@ [e2021] edition: 2021 +//@ [e2021] run-rustfix +//@ [e2021] rustfix-only-machine-applicable +//@ [e2024] edition: 2024 +//@ run-pass + +#![feature(let_chains)] +#![cfg_attr(e2021, warn(rust_2024_compatibility))] + +fn t_bindings() { + let e = Events::new(); + _ = { + e.mark(1); + let _v = e.ok(8); + let _v = e.ok(2).is_ok(); + let _ = e.ok(3); + let Ok(_) = e.ok(4) else { unreachable!() }; + let Ok(_) = e.ok(5).as_ref() else { unreachable!() }; + let _v = e.ok(7); + e.mark(6); + }; + e.assert(8); +} + +fn t_tuples() { + let e = Events::new(); + _ = (e.ok(1), e.ok(4).is_ok(), e.ok(2), e.ok(3).is_ok()); + e.assert(4); +} + +fn t_arrays() { + let e = Events::new(); + trait Tr {} + impl Tr for T {} + fn b<'a, T: 'a>(x: T) -> Box { + Box::new(x) + } + _ = [b(e.ok(1)), b(e.ok(4).is_ok()), b(e.ok(2)), b(e.ok(3).is_ok())]; + e.assert(4); +} + +fn t_fncalls() { + let e = Events::new(); + let f = |_, _, _, _| {}; + _ = f(e.ok(2), e.ok(4).is_ok(), e.ok(1), e.ok(3).is_ok()); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_tailexpr_bindings() { + let e = Events::new(); + _ = ({ + let _v = e.ok(2); + let _v = e.ok(1); + e.ok(5).is_ok() + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + }, e.mark(3), e.ok(4)); + e.assert(5); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_tailexpr_bindings() { + let e = Events::new(); + _ = ({ + let _v = e.ok(3); + let _v = e.ok(2); + e.ok(1).is_ok() + }, e.mark(4), e.ok(5)); + e.assert(5); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_tailexpr_tuples() { + let e = Events::new(); + _ = ({ + (e.ok(2), e.ok(6).is_ok(), e.ok(3), e.ok(5).is_ok()) + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + //[e2021]~| WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + }, e.mark(1), e.ok(4)); + e.assert(6); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_tailexpr_tuples() { + let e = Events::new(); + _ = ({ + (e.ok(4), e.ok(2).is_ok(), e.ok(5), e.ok(1).is_ok()) + }, e.mark(3), e.ok(6)); + e.assert(6); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_then() { + let e = Events::new(); + _ = (match e.ok(4).as_ref() { Ok(_) => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + } _ => {}}, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_then() { + let e = Events::new(); + _ = (if let Ok(_) = e.ok(2).as_ref() { + e.mark(1); + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_else() { + let e = Events::new(); + _ = (match e.err(4).as_ref() { Ok(_) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + }}, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_else() { + let e = Events::new(); + _ = (if let Ok(_) = e.err(1).as_ref() {} else { + e.mark(2); + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_match_then() { + let e = Events::new(); + _ = (match e.ok(4).as_ref() { + Ok(_) => e.mark(1), + _ => unreachable!(), + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_match_else() { + let e = Events::new(); + _ = (match e.err(4).as_ref() { + Ok(_) => unreachable!(), + _ => e.mark(1), + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_let_else_then() { + let e = Events::new(); + _ = ('top: { + 'chain: { + let Ok(_) = e.ok(1).as_ref() else { break 'chain }; + // The "then" branch: + e.mark(2); + break 'top; + } + // The "else" branch: + unreachable!() + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_let_else_else() { + let e = Events::new(); + _ = ('top: { + 'chain: { + let Ok(_) = e.err(1).as_ref() else { break 'chain }; + // The "then" branch: + unreachable!(); + #[allow(unreachable_code)] + break 'top; + } + // The "else" branch: + e.mark(2); + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_then_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.ok(4).as_ref() { + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + } + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_then_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.ok(2).as_ref() { + e.mark(1); + } + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_else_tailexpr() { + let e = Events::new(); + _ = ({ + match e.err(4).as_ref() { Ok(_) => {} _ => { + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + //[e2021]~| WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + }} + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_else_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.err(1).as_ref() {} else { + e.mark(2); + } + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_if_let_nested_then() { + let e = Events::new(); + _ = { + // The unusual formatting, here and below, is to make the + // comparison with `let` chains more direct. + if e.ok(1).is_ok() { + if let true = e.ok(9).is_ok() { + if let Ok(_v) = e.ok(8) { + if let Ok(_) = e.ok(7) { + if let Ok(_) = e.ok(6).as_ref() { + if e.ok(2).is_ok() { + if let Ok(_v) = e.ok(5) { + if let Ok(_) = e.ok(4).as_ref() { + e.mark(3); + }}}}}}}} + }; + e.assert(9); +} + +#[rustfmt::skip] +fn t_let_else_chained_then() { + let e = Events::new(); + _ = 'top: { + 'chain: { + if e.ok(1).is_ok() {} else { break 'chain }; + let true = e.ok(2).is_ok() else { break 'chain }; + let Ok(_v) = e.ok(9) else { break 'chain }; + let Ok(_) = e.ok(3) else { break 'chain }; + let Ok(_) = e.ok(4).as_ref() else { break 'chain }; + if e.ok(5).is_ok() {} else { break 'chain }; + let Ok(_v) = e.ok(8) else { break 'chain }; + let Ok(_) = e.ok(6).as_ref() else { break 'chain }; + // The "then" branch: + e.mark(7); + break 'top; + } + // The "else" branch: + unreachable!() + }; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_chains_then() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(5) + && let Ok(_) = e.ok(8) + && let Ok(_) = e.ok(7).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.ok(6).as_ref() { + e.mark(3); + }; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_chains_then() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(8) + && let Ok(_) = e.ok(7) + && let Ok(_) = e.ok(6).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(5) + && let Ok(_) = e.ok(4).as_ref() { + e.mark(3); + }; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_nested_else() { + let e = Events::new(); + _ = if e.err(1).is_ok() {} else { + match e.err(9).is_ok() { true => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + match e.err(8) { Ok(_v) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + match e.err(7) { Ok(_) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + match e.err(6).as_ref() { Ok(_) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + if e.err(2).is_ok() {} else { + match e.err(5) { Ok(_v) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + match e.err(4) { Ok(_) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(3); + }}}}}}}}}}}}}}; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_nested_else() { + let e = Events::new(); + _ = if e.err(1).is_ok() {} else { + if let true = e.err(2).is_ok() {} else { + if let Ok(_v) = e.err(3) {} else { + if let Ok(_) = e.err(4) {} else { + if let Ok(_) = e.err(5).as_ref() {} else { + if e.err(6).is_ok() {} else { + if let Ok(_v) = e.err(7) {} else { + if let Ok(_) = e.err(8) {} else { + e.mark(9); + }}}}}}}}; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_nested_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() { + if let true = e.ok(9).is_ok() { + if let Ok(_v) = e.ok(8) { + if let Ok(_) = e.ok(7) { + if let Ok(_) = e.ok(6).as_ref() { + if e.ok(2).is_ok() { + if let Ok(_v) = e.ok(5) { + match e.err(4).as_ref() { Ok(_) => {} _ => { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(3); + }}}}}}}}}; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_nested_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() { + if let true = e.ok(9).is_ok() { + if let Ok(_v) = e.ok(8) { + if let Ok(_) = e.ok(7) { + if let Ok(_) = e.ok(6).as_ref() { + if e.ok(2).is_ok() { + if let Ok(_v) = e.ok(5) { + if let Ok(_) = e.err(3).as_ref() {} else { + e.mark(4); + }}}}}}}}; + e.assert(9); +} + +#[rustfmt::skip] +fn t_let_else_chained_then_else() { + let e = Events::new(); + _ = 'top: { + 'chain: { + if e.ok(1).is_ok() {} else { break 'chain }; + let true = e.ok(2).is_ok() else { break 'chain }; + let Ok(_v) = e.ok(8) else { break 'chain }; + let Ok(_) = e.ok(3) else { break 'chain }; + let Ok(_) = e.ok(4).as_ref() else { break 'chain }; + if e.ok(5).is_ok() {} else { break 'chain }; + let Ok(_v) = e.ok(7) else { break 'chain }; + let Ok(_) = e.err(6).as_ref() else { break 'chain }; + // The "then" branch: + unreachable!(); + #[allow(unreachable_code)] + break 'top; + } + // The "else" branch: + e.mark(9); + }; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_chains_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.ok(8) + && let Ok(_) = e.ok(7).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(3) + && let Ok(_) = e.err(6) {} else { + e.mark(5); + }; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_chains_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(8).is_ok() + && let Ok(_v) = e.ok(7) + && let Ok(_) = e.ok(6) + && let Ok(_) = e.ok(5).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.err(3) {} else { + e.mark(9); + }; + e.assert(9); +} + +fn main() { + t_bindings(); + t_tuples(); + t_arrays(); + t_fncalls(); + t_tailexpr_bindings(); + t_tailexpr_tuples(); + t_if_let_then(); + t_if_let_else(); + t_match_then(); + t_match_else(); + t_let_else_then(); + t_let_else_else(); + t_if_let_then_tailexpr(); + t_if_let_else_tailexpr(); + t_if_let_nested_then(); + t_let_else_chained_then(); + t_if_let_chains_then(); + t_if_let_nested_else(); + t_if_let_nested_then_else(); + t_let_else_chained_then_else(); + t_if_let_chains_then_else(); +} + +// # Test scaffolding + +use core::cell::RefCell; +use std::collections::HashSet; + +/// A buffer to track the order of events. +/// +/// First, numbered events are logged into this buffer. +/// +/// Then, `assert` is called to verify that the correct number of +/// events were logged, and that they were logged in the expected +/// order. +struct Events(RefCell>>); + +impl Events { + const fn new() -> Self { + Self(RefCell::new(Some(Vec::new()))) + } + #[track_caller] + fn assert(&self, max: u64) { + let buf = &self.0; + let v1 = buf.borrow().as_ref().unwrap().clone(); + let mut v2 = buf.borrow().as_ref().unwrap().clone(); + *buf.borrow_mut() = None; + v2.sort(); + let uniq_len = v2.iter().collect::>().len(); + // Check that the sequence is sorted. + assert_eq!(v1, v2); + // Check that there are no duplicates. + assert_eq!(v2.len(), uniq_len); + // Check that the length is the expected one. + assert_eq!(max, uniq_len as u64); + // Check that the last marker is the expected one. + assert_eq!(v2.last().unwrap(), &max); + } + /// Return an `Ok` value that logs its drop. + fn ok(&self, m: u64) -> Result, LogDrop<'_>> { + Ok(LogDrop(self, m)) + } + /// Return an `Err` value that logs its drop. + fn err(&self, m: u64) -> Result { + Err(LogDrop(self, m)) + } + /// Log an event. + fn mark(&self, m: u64) { + self.0.borrow_mut().as_mut().unwrap().push(m); + } +} + +impl Drop for Events { + fn drop(&mut self) { + if self.0.borrow().is_some() { + panic!("failed to call `Events::assert()`"); + } + } +} + +/// A type that logs its drop events. +struct LogDrop<'b>(&'b Events, u64); + +impl<'b> Drop for LogDrop<'b> { + fn drop(&mut self) { + self.0.mark(self.1); + } +} diff --git a/tests/ui/drop/drop-order-comparisons.e2021.stderr b/tests/ui/drop/drop-order-comparisons.e2021.stderr new file mode 100644 index 000000000000..158d18f68826 --- /dev/null +++ b/tests/ui/drop/drop-order-comparisons.e2021.stderr @@ -0,0 +1,477 @@ +warning: relative drop order changing in Rust 2024 + --> $DIR/drop-order-comparisons.rs:76:9 + | +LL | _ = ({ + | _________- +LL | | let _v = e.ok(2); + | | -- + | | | + | | `_v` calls a custom destructor + | | `_v` will be dropped later as of Edition 2024 +LL | | let _v = e.ok(1); + | | -- + | | | + | | this value will be stored in a temporary; let us call it `#2` + | | `#2` will be dropped later as of Edition 2024 +LL | | e.ok(5).is_ok() + | | ^^^^^^^ + | | | + | | this value will be stored in a temporary; let us call it `#3` + | | up until Edition 2021 `#3` is dropped last but will be dropped earlier in Edition 2024 +... | +LL | | }, e.mark(3), e.ok(4)); + | | - + | | | + | | now the temporary value is dropped here, before the local variables in the block or statement + | |__________________________this value will be stored in a temporary; let us call it `#1` + | `#1` will be dropped later as of Edition 2024 + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: `#3` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `#1` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `_v` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `#2` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages +note: the lint level is defined here + --> $DIR/drop-order-comparisons.rs:28:25 + | +LL | #![cfg_attr(e2021, warn(rust_2024_compatibility))] + | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: `#[warn(tail_expr_drop_order)]` implied by `#[warn(rust_2024_compatibility)]` + +warning: relative drop order changing in Rust 2024 + --> $DIR/drop-order-comparisons.rs:100:45 + | +LL | _ = ({ + | _________- +LL | | (e.ok(2), e.ok(6).is_ok(), e.ok(3), e.ok(5).is_ok()) + | | ^^^^^^^ + | | | + | | this value will be stored in a temporary; let us call it `#2` + | | up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024 +... | +LL | | }, e.mark(1), e.ok(4)); + | | - + | | | + | | now the temporary value is dropped here, before the local variables in the block or statement + | |__________________________this value will be stored in a temporary; let us call it `#1` + | `#1` will be dropped later as of Edition 2024 + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: `#2` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `#1` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + +warning: relative drop order changing in Rust 2024 + --> $DIR/drop-order-comparisons.rs:100:19 + | +LL | _ = ({ + | _________- +LL | | (e.ok(2), e.ok(6).is_ok(), e.ok(3), e.ok(5).is_ok()) + | | ^^^^^^^ + | | | + | | this value will be stored in a temporary; let us call it `#2` + | | up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024 +... | +LL | | }, e.mark(1), e.ok(4)); + | | - + | | | + | | now the temporary value is dropped here, before the local variables in the block or statement + | |__________________________this value will be stored in a temporary; let us call it `#1` + | `#1` will be dropped later as of Edition 2024 + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: `#2` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `#1` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + +warning: relative drop order changing in Rust 2024 + --> $DIR/drop-order-comparisons.rs:221:24 + | +LL | _ = ({ + | _________- +LL | | if let Ok(_) = e.ok(4).as_ref() { + | | ^^^^^^^ + | | | + | | this value will be stored in a temporary; let us call it `#2` + | | up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024 +... | +LL | | }, e.mark(2), e.ok(3)); + | | - + | | | + | | now the temporary value is dropped here, before the local variables in the block or statement + | |__________________________this value will be stored in a temporary; let us call it `#1` + | `#1` will be dropped later as of Edition 2024 + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: `#2` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `#1` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + +warning: relative drop order changing in Rust 2024 + --> $DIR/drop-order-comparisons.rs:247:24 + | +LL | _ = ({ + | _________- +LL | | if let Ok(_) = e.err(4).as_ref() {} else { + | | ^^^^^^^^ + | | | + | | this value will be stored in a temporary; let us call it `#2` + | | up until Edition 2021 `#2` is dropped last but will be dropped earlier in Edition 2024 +... | +LL | | }, e.mark(2), e.ok(3)); + | | - + | | | + | | now the temporary value is dropped here, before the local variables in the block or statement + | |__________________________this value will be stored in a temporary; let us call it `#1` + | `#1` will be dropped later as of Edition 2024 + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +note: `#2` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ +note: `#1` invokes this custom destructor + --> $DIR/drop-order-comparisons.rs:571:1 + | +LL | / impl<'b> Drop for LogDrop<'b> { +LL | | fn drop(&mut self) { +LL | | self.0.mark(self.1); +LL | | } +LL | | } + | |_^ + = note: most of the time, changing drop order is harmless; inspect the `impl Drop`s for side effects like releasing locks or sending messages + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:123:13 + | +LL | _ = (if let Ok(_) = e.ok(4).as_ref() { + | ^^^^^^^^^^^^-------^^^^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:127:5 + | +LL | }, e.mark(2), e.ok(3)); + | ^ + = note: `#[warn(if_let_rescope)]` implied by `#[warn(rust_2024_compatibility)]` +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ _ = (match e.ok(4).as_ref() { Ok(_) => { +LL | +LL | +LL | e.mark(1); +LL ~ } _ => {}}, e.mark(2), e.ok(3)); + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:145:13 + | +LL | _ = (if let Ok(_) = e.err(4).as_ref() {} else { + | ^^^^^^^^^^^^--------^^^^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:145:44 + | +LL | _ = (if let Ok(_) = e.err(4).as_ref() {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ _ = (match e.err(4).as_ref() { Ok(_) => {} _ => { +LL | +LL | +LL | e.mark(1); +LL ~ }}, e.mark(2), e.ok(3)); + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:247:12 + | +LL | if let Ok(_) = e.err(4).as_ref() {} else { + | ^^^^^^^^^^^^--------^^^^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:247:43 + | +LL | if let Ok(_) = e.err(4).as_ref() {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(4).as_ref() { Ok(_) => {} _ => { +LL | +... +LL | e.mark(1); +LL ~ }} + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:352:12 + | +LL | if let true = e.err(9).is_ok() {} else { + | ^^^^^^^^^^^--------^^^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:352:41 + | +LL | if let true = e.err(9).is_ok() {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(9).is_ok() { true => {} _ => { +LL | +... +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:355:12 + | +LL | if let Ok(_v) = e.err(8) {} else { + | ^^^^^^^^^^^^^-------- + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:355:35 + | +LL | if let Ok(_v) = e.err(8) {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(8) { Ok(_v) => {} _ => { +LL | +... +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:358:12 + | +LL | if let Ok(_) = e.err(7) {} else { + | ^^^^^^^^^^^^-------- + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:358:34 + | +LL | if let Ok(_) = e.err(7) {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(7) { Ok(_) => {} _ => { +LL | +... +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:361:12 + | +LL | if let Ok(_) = e.err(6).as_ref() {} else { + | ^^^^^^^^^^^^--------^^^^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:361:43 + | +LL | if let Ok(_) = e.err(6).as_ref() {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(6).as_ref() { Ok(_) => {} _ => { +LL | +... +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:365:12 + | +LL | if let Ok(_v) = e.err(5) {} else { + | ^^^^^^^^^^^^^-------- + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:365:35 + | +LL | if let Ok(_v) = e.err(5) {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(5) { Ok(_v) => {} _ => { +LL | +... +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:368:12 + | +LL | if let Ok(_) = e.err(4) {} else { + | ^^^^^^^^^^^^-------- + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:368:34 + | +LL | if let Ok(_) = e.err(4) {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(4) { Ok(_) => {} _ => { +LL | +LL | +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/drop-order-comparisons.rs:404:12 + | +LL | if let Ok(_) = e.err(4).as_ref() {} else { + | ^^^^^^^^^^^^--------^^^^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see +help: the value is now dropped here in Edition 2024 + --> $DIR/drop-order-comparisons.rs:404:43 + | +LL | if let Ok(_) = e.err(4).as_ref() {} else { + | ^ +help: a `match` with a single arm can preserve the drop order up to Edition 2021 + | +LL ~ match e.err(4).as_ref() { Ok(_) => {} _ => { +LL | +LL | +LL | e.mark(3); +LL ~ }}}}}}}}}; + | + +warning: 15 warnings emitted + diff --git a/tests/ui/drop/drop-order-comparisons.rs b/tests/ui/drop/drop-order-comparisons.rs new file mode 100644 index 000000000000..78c75a9449f1 --- /dev/null +++ b/tests/ui/drop/drop-order-comparisons.rs @@ -0,0 +1,575 @@ +// This tests various aspects of the drop order with a focus on: +// +// - The lifetime of temporaries with the `if let` construct (and with +// various similar constructs) and how these lifetimes were shortened +// for `if let` in Rust 2024. +// +// - The shortening of the lifetimes of temporaries in tail +// expressions in Rust 2024. +// +// - The behavior of `let` chains and how this behavior compares to +// nested `if let` expressions and chained `let .. else` statements. +// +// In the tests below, `Events` tracks a sequence of numbered events. +// Calling `e.mark(..)` logs a numbered event immediately. Calling +// `e.ok(..)` or `e.err(..)` returns an `Ok(_)` or `Err(_)` value, +// respectively, and logs the numbered event when that value is +// dropped. Calling `e.assert()` verifies that the correct number of +// events were logged and that they were logged in the correct order. + +//@ revisions: e2021 e2024 +//@ [e2021] edition: 2021 +//@ [e2021] run-rustfix +//@ [e2021] rustfix-only-machine-applicable +//@ [e2024] edition: 2024 +//@ run-pass + +#![feature(let_chains)] +#![cfg_attr(e2021, warn(rust_2024_compatibility))] + +fn t_bindings() { + let e = Events::new(); + _ = { + e.mark(1); + let _v = e.ok(8); + let _v = e.ok(2).is_ok(); + let _ = e.ok(3); + let Ok(_) = e.ok(4) else { unreachable!() }; + let Ok(_) = e.ok(5).as_ref() else { unreachable!() }; + let _v = e.ok(7); + e.mark(6); + }; + e.assert(8); +} + +fn t_tuples() { + let e = Events::new(); + _ = (e.ok(1), e.ok(4).is_ok(), e.ok(2), e.ok(3).is_ok()); + e.assert(4); +} + +fn t_arrays() { + let e = Events::new(); + trait Tr {} + impl Tr for T {} + fn b<'a, T: 'a>(x: T) -> Box { + Box::new(x) + } + _ = [b(e.ok(1)), b(e.ok(4).is_ok()), b(e.ok(2)), b(e.ok(3).is_ok())]; + e.assert(4); +} + +fn t_fncalls() { + let e = Events::new(); + let f = |_, _, _, _| {}; + _ = f(e.ok(2), e.ok(4).is_ok(), e.ok(1), e.ok(3).is_ok()); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_tailexpr_bindings() { + let e = Events::new(); + _ = ({ + let _v = e.ok(2); + let _v = e.ok(1); + e.ok(5).is_ok() + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + }, e.mark(3), e.ok(4)); + e.assert(5); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_tailexpr_bindings() { + let e = Events::new(); + _ = ({ + let _v = e.ok(3); + let _v = e.ok(2); + e.ok(1).is_ok() + }, e.mark(4), e.ok(5)); + e.assert(5); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_tailexpr_tuples() { + let e = Events::new(); + _ = ({ + (e.ok(2), e.ok(6).is_ok(), e.ok(3), e.ok(5).is_ok()) + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + //[e2021]~| WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + }, e.mark(1), e.ok(4)); + e.assert(6); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_tailexpr_tuples() { + let e = Events::new(); + _ = ({ + (e.ok(4), e.ok(2).is_ok(), e.ok(5), e.ok(1).is_ok()) + }, e.mark(3), e.ok(6)); + e.assert(6); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_then() { + let e = Events::new(); + _ = (if let Ok(_) = e.ok(4).as_ref() { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_then() { + let e = Events::new(); + _ = (if let Ok(_) = e.ok(2).as_ref() { + e.mark(1); + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_else() { + let e = Events::new(); + _ = (if let Ok(_) = e.err(4).as_ref() {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_else() { + let e = Events::new(); + _ = (if let Ok(_) = e.err(1).as_ref() {} else { + e.mark(2); + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_match_then() { + let e = Events::new(); + _ = (match e.ok(4).as_ref() { + Ok(_) => e.mark(1), + _ => unreachable!(), + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_match_else() { + let e = Events::new(); + _ = (match e.err(4).as_ref() { + Ok(_) => unreachable!(), + _ => e.mark(1), + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_let_else_then() { + let e = Events::new(); + _ = ('top: { + 'chain: { + let Ok(_) = e.ok(1).as_ref() else { break 'chain }; + // The "then" branch: + e.mark(2); + break 'top; + } + // The "else" branch: + unreachable!() + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_let_else_else() { + let e = Events::new(); + _ = ('top: { + 'chain: { + let Ok(_) = e.err(1).as_ref() else { break 'chain }; + // The "then" branch: + unreachable!(); + #[allow(unreachable_code)] + break 'top; + } + // The "else" branch: + e.mark(2); + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_then_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.ok(4).as_ref() { + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + } + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_then_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.ok(2).as_ref() { + e.mark(1); + } + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_else_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.err(4).as_ref() {} else { + //[e2021]~^ WARN relative drop order changing in Rust 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + //[e2021]~| WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(1); + } + }, e.mark(2), e.ok(3)); + e.assert(4); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_else_tailexpr() { + let e = Events::new(); + _ = ({ + if let Ok(_) = e.err(1).as_ref() {} else { + e.mark(2); + } + }, e.mark(3), e.ok(4)); + e.assert(4); +} + +#[rustfmt::skip] +fn t_if_let_nested_then() { + let e = Events::new(); + _ = { + // The unusual formatting, here and below, is to make the + // comparison with `let` chains more direct. + if e.ok(1).is_ok() { + if let true = e.ok(9).is_ok() { + if let Ok(_v) = e.ok(8) { + if let Ok(_) = e.ok(7) { + if let Ok(_) = e.ok(6).as_ref() { + if e.ok(2).is_ok() { + if let Ok(_v) = e.ok(5) { + if let Ok(_) = e.ok(4).as_ref() { + e.mark(3); + }}}}}}}} + }; + e.assert(9); +} + +#[rustfmt::skip] +fn t_let_else_chained_then() { + let e = Events::new(); + _ = 'top: { + 'chain: { + if e.ok(1).is_ok() {} else { break 'chain }; + let true = e.ok(2).is_ok() else { break 'chain }; + let Ok(_v) = e.ok(9) else { break 'chain }; + let Ok(_) = e.ok(3) else { break 'chain }; + let Ok(_) = e.ok(4).as_ref() else { break 'chain }; + if e.ok(5).is_ok() {} else { break 'chain }; + let Ok(_v) = e.ok(8) else { break 'chain }; + let Ok(_) = e.ok(6).as_ref() else { break 'chain }; + // The "then" branch: + e.mark(7); + break 'top; + } + // The "else" branch: + unreachable!() + }; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_chains_then() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(5) + && let Ok(_) = e.ok(8) + && let Ok(_) = e.ok(7).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.ok(6).as_ref() { + e.mark(3); + }; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_chains_then() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(8) + && let Ok(_) = e.ok(7) + && let Ok(_) = e.ok(6).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(5) + && let Ok(_) = e.ok(4).as_ref() { + e.mark(3); + }; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_nested_else() { + let e = Events::new(); + _ = if e.err(1).is_ok() {} else { + if let true = e.err(9).is_ok() {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + if let Ok(_v) = e.err(8) {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + if let Ok(_) = e.err(7) {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + if let Ok(_) = e.err(6).as_ref() {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + if e.err(2).is_ok() {} else { + if let Ok(_v) = e.err(5) {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + if let Ok(_) = e.err(4) {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(3); + }}}}}}}}; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_nested_else() { + let e = Events::new(); + _ = if e.err(1).is_ok() {} else { + if let true = e.err(2).is_ok() {} else { + if let Ok(_v) = e.err(3) {} else { + if let Ok(_) = e.err(4) {} else { + if let Ok(_) = e.err(5).as_ref() {} else { + if e.err(6).is_ok() {} else { + if let Ok(_v) = e.err(7) {} else { + if let Ok(_) = e.err(8) {} else { + e.mark(9); + }}}}}}}}; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_nested_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() { + if let true = e.ok(9).is_ok() { + if let Ok(_v) = e.ok(8) { + if let Ok(_) = e.ok(7) { + if let Ok(_) = e.ok(6).as_ref() { + if e.ok(2).is_ok() { + if let Ok(_v) = e.ok(5) { + if let Ok(_) = e.err(4).as_ref() {} else { + //[e2021]~^ WARN if let` assigns a shorter lifetime since Edition 2024 + //[e2021]~| WARN this changes meaning in Rust 2024 + e.mark(3); + }}}}}}}}; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_nested_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() { + if let true = e.ok(9).is_ok() { + if let Ok(_v) = e.ok(8) { + if let Ok(_) = e.ok(7) { + if let Ok(_) = e.ok(6).as_ref() { + if e.ok(2).is_ok() { + if let Ok(_v) = e.ok(5) { + if let Ok(_) = e.err(3).as_ref() {} else { + e.mark(4); + }}}}}}}}; + e.assert(9); +} + +#[rustfmt::skip] +fn t_let_else_chained_then_else() { + let e = Events::new(); + _ = 'top: { + 'chain: { + if e.ok(1).is_ok() {} else { break 'chain }; + let true = e.ok(2).is_ok() else { break 'chain }; + let Ok(_v) = e.ok(8) else { break 'chain }; + let Ok(_) = e.ok(3) else { break 'chain }; + let Ok(_) = e.ok(4).as_ref() else { break 'chain }; + if e.ok(5).is_ok() {} else { break 'chain }; + let Ok(_v) = e.ok(7) else { break 'chain }; + let Ok(_) = e.err(6).as_ref() else { break 'chain }; + // The "then" branch: + unreachable!(); + #[allow(unreachable_code)] + break 'top; + } + // The "else" branch: + e.mark(9); + }; + e.assert(9); +} + +#[cfg(e2021)] +#[rustfmt::skip] +fn t_if_let_chains_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(9).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.ok(8) + && let Ok(_) = e.ok(7).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(3) + && let Ok(_) = e.err(6) {} else { + e.mark(5); + }; + e.assert(9); +} + +#[cfg(e2024)] +#[rustfmt::skip] +fn t_if_let_chains_then_else() { + let e = Events::new(); + _ = if e.ok(1).is_ok() + && let true = e.ok(8).is_ok() + && let Ok(_v) = e.ok(7) + && let Ok(_) = e.ok(6) + && let Ok(_) = e.ok(5).as_ref() + && e.ok(2).is_ok() + && let Ok(_v) = e.ok(4) + && let Ok(_) = e.err(3) {} else { + e.mark(9); + }; + e.assert(9); +} + +fn main() { + t_bindings(); + t_tuples(); + t_arrays(); + t_fncalls(); + t_tailexpr_bindings(); + t_tailexpr_tuples(); + t_if_let_then(); + t_if_let_else(); + t_match_then(); + t_match_else(); + t_let_else_then(); + t_let_else_else(); + t_if_let_then_tailexpr(); + t_if_let_else_tailexpr(); + t_if_let_nested_then(); + t_let_else_chained_then(); + t_if_let_chains_then(); + t_if_let_nested_else(); + t_if_let_nested_then_else(); + t_let_else_chained_then_else(); + t_if_let_chains_then_else(); +} + +// # Test scaffolding + +use core::cell::RefCell; +use std::collections::HashSet; + +/// A buffer to track the order of events. +/// +/// First, numbered events are logged into this buffer. +/// +/// Then, `assert` is called to verify that the correct number of +/// events were logged, and that they were logged in the expected +/// order. +struct Events(RefCell>>); + +impl Events { + const fn new() -> Self { + Self(RefCell::new(Some(Vec::new()))) + } + #[track_caller] + fn assert(&self, max: u64) { + let buf = &self.0; + let v1 = buf.borrow().as_ref().unwrap().clone(); + let mut v2 = buf.borrow().as_ref().unwrap().clone(); + *buf.borrow_mut() = None; + v2.sort(); + let uniq_len = v2.iter().collect::>().len(); + // Check that the sequence is sorted. + assert_eq!(v1, v2); + // Check that there are no duplicates. + assert_eq!(v2.len(), uniq_len); + // Check that the length is the expected one. + assert_eq!(max, uniq_len as u64); + // Check that the last marker is the expected one. + assert_eq!(v2.last().unwrap(), &max); + } + /// Return an `Ok` value that logs its drop. + fn ok(&self, m: u64) -> Result, LogDrop<'_>> { + Ok(LogDrop(self, m)) + } + /// Return an `Err` value that logs its drop. + fn err(&self, m: u64) -> Result { + Err(LogDrop(self, m)) + } + /// Log an event. + fn mark(&self, m: u64) { + self.0.borrow_mut().as_mut().unwrap().push(m); + } +} + +impl Drop for Events { + fn drop(&mut self) { + if self.0.borrow().is_some() { + panic!("failed to call `Events::assert()`"); + } + } +} + +/// A type that logs its drop events. +struct LogDrop<'b>(&'b Events, u64); + +impl<'b> Drop for LogDrop<'b> { + fn drop(&mut self) { + self.0.mark(self.1); + } +} diff --git a/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr b/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr index 75fb13c378c2..ece581dc6263 100644 --- a/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr +++ b/tests/ui/explicit-tail-calls/ctfe-arg-bad-borrow.stderr @@ -4,10 +4,9 @@ error[E0597]: `local` does not live long enough 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 + | ^^^^^^- `local` dropped here while still borrowed + | | + | borrowed value does not live long enough error: aborting due to 1 previous error diff --git a/tests/ui/explicit-tail-calls/two-phase.rs b/tests/ui/explicit-tail-calls/two-phase.rs new file mode 100644 index 000000000000..91365b5e739c --- /dev/null +++ b/tests/ui/explicit-tail-calls/two-phase.rs @@ -0,0 +1,14 @@ +// regression test for . +// this test used to ICE because we tried to run drop glue of `x` +// if dropping `_y` (happening at the `become` site) panicked and caused an unwind. +// +//@ check-pass +#![expect(incomplete_features)] +#![feature(explicit_tail_calls)] + +fn f(x: &mut ()) { + let _y = String::new(); + become f(x); +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr b/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr index 96fb4a53609e..73e6988b09cc 100644 --- a/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr +++ b/tests/ui/feature-gates/feature-gate-generic_arg_infer.normal.stderr @@ -8,17 +8,15 @@ LL | let _y: [u8; _] = [0; 3]; = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error[E0747]: type provided when a constant was expected +error[E0658]: const arguments cannot yet be inferred with `_` --> $DIR/feature-gate-generic_arg_infer.rs:18:20 | -LL | let _x = foo::<_>([1,2]); +LL | let _x = foo::<_>([1, 2]); | ^ | - = help: const arguments cannot yet be inferred with `_` -help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable - | -LL + #![feature(generic_arg_infer)] - | + = note: see issue #85077 for more information + = help: add `#![feature(generic_arg_infer)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0658]: using `_` for array lengths is unstable --> $DIR/feature-gate-generic_arg_infer.rs:11:27 @@ -32,5 +30,4 @@ LL | let _x: [u8; 3] = [0; _]; error: aborting due to 3 previous errors -Some errors have detailed explanations: E0658, E0747. -For more information about an error, try `rustc --explain E0658`. +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/feature-gates/feature-gate-generic_arg_infer.rs b/tests/ui/feature-gates/feature-gate-generic_arg_infer.rs index de4b7078ea68..147978b0557a 100644 --- a/tests/ui/feature-gates/feature-gate-generic_arg_infer.rs +++ b/tests/ui/feature-gates/feature-gate-generic_arg_infer.rs @@ -4,7 +4,7 @@ #![cfg_attr(feature, feature(generic_arg_infer))] fn foo(_: [u8; N]) -> [u8; N] { - [0; N] + [0; N] } fn bar() { @@ -15,7 +15,7 @@ fn bar() { } fn main() { - let _x = foo::<_>([1,2]); - //[normal]~^ ERROR: type provided when a constant was expected + let _x = foo::<_>([1, 2]); + //[normal]~^ ERROR: const arguments cannot yet be inferred with `_` bar(); } diff --git a/tests/ui/generics/issue-79605.stderr b/tests/ui/generics/issue-79605.stderr index 67fed200f969..049f77a65840 100644 --- a/tests/ui/generics/issue-79605.stderr +++ b/tests/ui/generics/issue-79605.stderr @@ -3,11 +3,6 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures | LL | impl X<'_, _> {} | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | impl X<'_, T> {} - | +++ ~ error: aborting due to 1 previous error diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs index a44ed9e5ef59..34548e2487e0 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs @@ -43,9 +43,9 @@ fn main() { } foo(bar, "string", |s| s.len() == 5); - //~^ ERROR implementation of `FnOnce` is not general enough - //~| ERROR implementation of `FnOnce` is not general enough + //~^ ERROR implementation of `Parser` is not general enough + //~| ERROR implementation of `Parser` is not general enough foo(baz, "string", |s| s.0.len() == 5); - //~^ ERROR implementation of `FnOnce` is not general enough - //~| ERROR implementation of `FnOnce` is not general enough + //~^ ERROR implementation of `Parser` is not general enough + //~| ERROR implementation of `Parser` is not general enough } diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr index b2bb417a8f01..23fc6e2f7f40 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr @@ -1,39 +1,39 @@ -error: implementation of `FnOnce` is not general enough +error: implementation of `Parser` is not general enough --> $DIR/issue-71955.rs:45:5 | LL | foo(bar, "string", |s| s.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough | - = note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`... - = note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2` + = note: `for<'a> fn(&'a str) -> (&'a str, &'a str) {bar}` must implement `Parser<'0>`, for any lifetime `'0`... + = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1` -error: implementation of `FnOnce` is not general enough +error: implementation of `Parser` is not general enough --> $DIR/issue-71955.rs:45:5 | LL | foo(bar, "string", |s| s.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough | - = note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`... - = note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2` + = note: `for<'a> fn(&'a str) -> (&'a str, &'a str) {bar}` must implement `Parser<'0>`, for any lifetime `'0`... + = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: implementation of `FnOnce` is not general enough +error: implementation of `Parser` is not general enough --> $DIR/issue-71955.rs:48:5 | LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough | - = note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`... - = note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2` + = note: `for<'a> fn(&'a str) -> (&'a str, Wrapper<'a>) {baz}` must implement `Parser<'0>`, for any lifetime `'0`... + = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1` -error: implementation of `FnOnce` is not general enough +error: implementation of `Parser` is not general enough --> $DIR/issue-71955.rs:48:5 | LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough | - = note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`... - = note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2` + = note: `for<'a> fn(&'a str) -> (&'a str, Wrapper<'a>) {baz}` must implement `Parser<'0>`, for any lifetime `'0`... + = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 4 previous errors diff --git a/tests/ui/intrinsics/reify-intrinsic.rs b/tests/ui/intrinsics/reify-intrinsic.rs index 0d047ccf4a32..5b2324235c1a 100644 --- a/tests/ui/intrinsics/reify-intrinsic.rs +++ b/tests/ui/intrinsics/reify-intrinsic.rs @@ -3,17 +3,17 @@ #![feature(core_intrinsics, intrinsics)] fn a() { - let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::transmute; + let _: unsafe fn(isize) -> usize = std::mem::transmute; //~^ ERROR cannot coerce } fn b() { - let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize; + let _ = std::mem::transmute as unsafe fn(isize) -> usize; //~^ ERROR casting } fn c() { - let _: [unsafe extern "rust-intrinsic" fn(f32) -> f32; 2] = [ + let _: [unsafe fn(f32) -> f32; 2] = [ std::intrinsics::floorf32, //~ ERROR cannot coerce std::intrinsics::log2f32, ]; diff --git a/tests/ui/intrinsics/reify-intrinsic.stderr b/tests/ui/intrinsics/reify-intrinsic.stderr index aea6d263a727..aea6f838e0d4 100644 --- a/tests/ui/intrinsics/reify-intrinsic.stderr +++ b/tests/ui/intrinsics/reify-intrinsic.stderr @@ -1,19 +1,19 @@ error[E0308]: cannot coerce intrinsics to function pointers - --> $DIR/reify-intrinsic.rs:6:64 + --> $DIR/reify-intrinsic.rs:6:40 | -LL | let _: unsafe extern "rust-intrinsic" fn(isize) -> usize = std::mem::transmute; - | ------------------------------------------------- ^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers +LL | let _: unsafe fn(isize) -> usize = std::mem::transmute; + | ------------------------- ^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers | | | expected due to this | - = note: expected fn pointer `unsafe extern "rust-intrinsic" fn(isize) -> usize` + = note: expected fn pointer `unsafe fn(isize) -> usize` found fn item `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` -error[E0606]: casting `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` as `unsafe extern "rust-intrinsic" fn(isize) -> usize` is invalid +error[E0606]: casting `unsafe fn(_) -> _ {std::intrinsics::transmute::<_, _>}` as `unsafe fn(isize) -> usize` is invalid --> $DIR/reify-intrinsic.rs:11:13 | -LL | let _ = std::mem::transmute as unsafe extern "rust-intrinsic" fn(isize) -> usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let _ = std::mem::transmute as unsafe fn(isize) -> usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0308]: cannot coerce intrinsics to function pointers --> $DIR/reify-intrinsic.rs:17:9 @@ -21,7 +21,7 @@ error[E0308]: cannot coerce intrinsics to function pointers LL | std::intrinsics::floorf32, | ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot coerce intrinsics to function pointers | - = note: expected fn pointer `unsafe extern "rust-intrinsic" fn(_) -> _` + = note: expected fn pointer `unsafe fn(_) -> _` found fn item `unsafe fn(_) -> _ {floorf32}` error: aborting due to 3 previous errors diff --git a/tests/ui/macros/macro-span-issue-116502.stderr b/tests/ui/macros/macro-span-issue-116502.stderr index da02855660a4..2a581f7031b9 100644 --- a/tests/ui/macros/macro-span-issue-116502.stderr +++ b/tests/ui/macros/macro-span-issue-116502.stderr @@ -17,13 +17,6 @@ LL | T: Trait; | ---- in this macro invocation | = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) -help: use type parameters instead - | -LL ~ U -LL | }; -LL | } -LL ~ struct S(m!(), T) - | error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-14303-fncall.full.stderr b/tests/ui/parser/issues/issue-14303-fncall.full.stderr index 1986f70bf672..5a017c85c164 100644 --- a/tests/ui/parser/issues/issue-14303-fncall.full.stderr +++ b/tests/ui/parser/issues/issue-14303-fncall.full.stderr @@ -1,8 +1,8 @@ -error[E0747]: type provided when a lifetime was expected - --> $DIR/issue-14303-fncall.rs:15:26 +error[E0747]: placeholder provided when a lifetime was expected + --> $DIR/issue-14303-fncall.rs:12:77 | -LL | .collect::>>(); - | ^ +LL | let _x = (*start..*end).map(|x| S { a: start, b: end }).collect::>>(); + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-14303-fncall.generic_arg.stderr b/tests/ui/parser/issues/issue-14303-fncall.generic_arg.stderr index 2de59b8c746c..5a017c85c164 100644 --- a/tests/ui/parser/issues/issue-14303-fncall.generic_arg.stderr +++ b/tests/ui/parser/issues/issue-14303-fncall.generic_arg.stderr @@ -1,8 +1,8 @@ -error[E0747]: inferred provided when a lifetime was expected - --> $DIR/issue-14303-fncall.rs:15:26 +error[E0747]: placeholder provided when a lifetime was expected + --> $DIR/issue-14303-fncall.rs:12:77 | -LL | .collect::>>(); - | ^ +LL | let _x = (*start..*end).map(|x| S { a: start, b: end }).collect::>>(); + | ^ error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-14303-fncall.rs b/tests/ui/parser/issues/issue-14303-fncall.rs index 59d4eab06d6f..8f7fbec94706 100644 --- a/tests/ui/parser/issues/issue-14303-fncall.rs +++ b/tests/ui/parser/issues/issue-14303-fncall.rs @@ -3,18 +3,15 @@ // we need the above to avoid ast borrowck failure in recovered code #![cfg_attr(generic_arg, feature(generic_arg_infer))] - struct S<'a, T> { a: &'a T, b: &'a T, } fn foo<'a, 'b>(start: &'a usize, end: &'a usize) { - let _x = (*start..*end) - .map(|x| S { a: start, b: end }) - .collect::>>(); - //[generic_arg]~^ ERROR inferred provided when a lifetime was expected - //[full]~^^ ERROR type provided when a lifetime was expected + let _x = (*start..*end).map(|x| S { a: start, b: end }).collect::>>(); + //[generic_arg]~^ ERROR placeholder provided when a lifetime was expected + //[full]~^^ ERROR placeholder provided when a lifetime was expected } fn main() {} diff --git a/tests/ui/self/arbitrary-self-from-method-substs-with-receiver.stderr b/tests/ui/self/arbitrary-self-from-method-substs-with-receiver.stderr index 9af2a08f3712..bafa290a3cfd 100644 --- a/tests/ui/self/arbitrary-self-from-method-substs-with-receiver.stderr +++ b/tests/ui/self/arbitrary-self-from-method-substs-with-receiver.stderr @@ -53,7 +53,7 @@ LL | assert_eq!(smart_ptr.a::<&Foo>(), 2); | ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>` | = note: expected reference `&Foo` - found struct `SmartPtr<'_, Foo, >` + found struct `SmartPtr<'_, Foo>` error[E0308]: mismatched types --> $DIR/arbitrary-self-from-method-substs-with-receiver.rs:62:16 @@ -62,7 +62,7 @@ LL | assert_eq!(smart_ptr.b::<&Foo>(), 1); | ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>` | = note: expected reference `&Foo` - found struct `SmartPtr<'_, Foo, >` + found struct `SmartPtr<'_, Foo>` error: aborting due to 8 previous errors diff --git a/tests/ui/self/arbitrary-self-from-method-substs.feature.stderr b/tests/ui/self/arbitrary-self-from-method-substs.feature.stderr index 6e864f44aa34..f67918a2577a 100644 --- a/tests/ui/self/arbitrary-self-from-method-substs.feature.stderr +++ b/tests/ui/self/arbitrary-self-from-method-substs.feature.stderr @@ -83,7 +83,7 @@ LL | smart_ptr.get::<&Foo>(); | ^^^^^^^^^ expected `&Foo`, found `SmartPtr<'_, Foo>` | = note: expected reference `&Foo` - found struct `SmartPtr<'_, Foo, >` + found struct `SmartPtr<'_, Foo>` error[E0271]: type mismatch resolving `::Receiver == Foo` --> $DIR/arbitrary-self-from-method-substs.rs:92:9 diff --git a/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr b/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr index a4b652565740..6559845c23ec 100644 --- a/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr +++ b/tests/ui/span/issue-42234-unknown-receiver-type.generic_arg.stderr @@ -17,10 +17,6 @@ error[E0282]: type annotations needed LL | .sum::<_>() | ^^^ cannot infer type of the type parameter `S` declared on the method `sum` | -help: consider specifying the generic argument - | -LL | .sum::() - | ~~~~~ error: aborting due to 2 previous errors diff --git a/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.rs b/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.rs new file mode 100644 index 000000000000..a9c2c20ef374 --- /dev/null +++ b/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.rs @@ -0,0 +1,51 @@ +// https://github.com/rust-lang/rust/issues/109195 +struct Foo; + +impl Foo { + fn bar_baz() {} +} + +impl Foo { + fn bar_quux() {} +} + +fn main() { + String::from::utf8; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `from_utf8` + String::from::utf8(); + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `from_utf8` + String::from::utf16(); + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `from_utf16` + String::from::method_that_doesnt_exist(); + //~^ ERROR ambiguous associated type [E0223] + //~| HELP if there were a trait named `Example` with associated type `from` + str::into::string(); + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `into_string` + str::char::indices(); + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `char_indices` + Foo::bar::baz; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `bar_baz` + Foo::bar::quux; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `bar_quux` + Foo::bar::fizz; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP if there were a trait named `Example` with associated type `bar` + i32::wrapping::add; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP there is an associated function with a similar name: `wrapping_add` + i32::wrapping::method_that_doesnt_exist; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP if there were a trait named `Example` with associated type `wrapping` + + // this one ideally should suggest `downcast_mut_unchecked` + ::downcast::mut_unchecked; + //~^ ERROR ambiguous associated type [E0223] + //~| HELP if there were a trait named `Example` with associated type `downcast` +} diff --git a/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.stderr b/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.stderr new file mode 100644 index 000000000000..5863aa28f41f --- /dev/null +++ b/tests/ui/suggestions/ambiguous-assoc-type-path-suggest-similar-item.stderr @@ -0,0 +1,135 @@ +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:13:5 + | +LL | String::from::utf8; + | ^^^^^^^^^^^^ + | +help: there is an associated function with a similar name: `from_utf8` + | +LL | String::from_utf8; + | ~~~~~~~~~ + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:16:5 + | +LL | String::from::utf8(); + | ^^^^^^^^^^^^ + | +help: there is an associated function with a similar name: `from_utf8` + | +LL | String::from_utf8(); + | ~~~~~~~~~ + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:19:5 + | +LL | String::from::utf16(); + | ^^^^^^^^^^^^ + | +help: there is an associated function with a similar name: `from_utf16` + | +LL | String::from_utf16(); + | ~~~~~~~~~~ + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:22:5 + | +LL | String::from::method_that_doesnt_exist(); + | ^^^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated type `from` implemented for `String`, you could use the fully-qualified path + | +LL | ::from::method_that_doesnt_exist(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:25:5 + | +LL | str::into::string(); + | ^^^^^^^^^ + | +help: there is an associated function with a similar name: `into_string` + | +LL | str::into_string(); + | ~~~~~~~~~~~ + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:28:5 + | +LL | str::char::indices(); + | ^^^^^^^^^ + | +help: there is an associated function with a similar name: `char_indices` + | +LL | str::char_indices(); + | ~~~~~~~~~~~~ + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:31:5 + | +LL | Foo::bar::baz; + | ^^^^^^^^ + | +help: there is an associated function with a similar name: `bar_baz` + | +LL | Foo::bar_baz; + | ~~~~~~~ + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:34:5 + | +LL | Foo::bar::quux; + | ^^^^^^^^ + | +help: there is an associated function with a similar name: `bar_quux` + | +LL | Foo::bar_quux; + | ~~~~~~~~ + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:37:5 + | +LL | Foo::bar::fizz; + | ^^^^^^^^ + | +help: if there were a trait named `Example` with associated type `bar` implemented for `Foo`, you could use the fully-qualified path + | +LL | ::bar::fizz; + | ~~~~~~~~~~~~~~~~~~~~~ + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:40:5 + | +LL | i32::wrapping::add; + | ^^^^^^^^^^^^^ + | +help: there is an associated function with a similar name: `wrapping_add` + | +LL | i32::wrapping_add; + | ~~~~~~~~~~~~ + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:43:5 + | +LL | i32::wrapping::method_that_doesnt_exist; + | ^^^^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated type `wrapping` implemented for `i32`, you could use the fully-qualified path + | +LL | ::wrapping::method_that_doesnt_exist; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0223]: ambiguous associated type + --> $DIR/ambiguous-assoc-type-path-suggest-similar-item.rs:48:5 + | +LL | ::downcast::mut_unchecked; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated type `downcast` implemented for `(dyn Any + 'static)`, you could use the fully-qualified path + | +LL | <(dyn Any + 'static) as Example>::downcast::mut_unchecked; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 12 previous errors + +For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/suggestions/issue-109195.rs b/tests/ui/suggestions/issue-109195.rs deleted file mode 100644 index cc499b0d7761..000000000000 --- a/tests/ui/suggestions/issue-109195.rs +++ /dev/null @@ -1,20 +0,0 @@ -fn main() { - String::from::utf8; - //~^ ERROR ambiguous associated type [E0223] - //~| HELP there is an associated function with a similar name: `from_utf8` - String::from::utf8(); - //~^ ERROR ambiguous associated type [E0223] - //~| HELP there is an associated function with a similar name: `from_utf8` - String::from::utf16(); - //~^ ERROR ambiguous associated type [E0223] - //~| HELP there is an associated function with a similar name: `from_utf16` - String::from::method_that_doesnt_exist(); - //~^ ERROR ambiguous associated type [E0223] - //~| HELP if there were a trait named `Example` with associated type `from` - str::from::utf8(); - //~^ ERROR ambiguous associated type [E0223] - //~| HELP if there were a trait named `Example` with associated type `from` - str::from::utf8_mut(); - //~^ ERROR ambiguous associated type [E0223] - //~| HELP if there were a trait named `Example` with associated type `from` -} diff --git a/tests/ui/suggestions/issue-109195.stderr b/tests/ui/suggestions/issue-109195.stderr deleted file mode 100644 index 10cf9cfd28ca..000000000000 --- a/tests/ui/suggestions/issue-109195.stderr +++ /dev/null @@ -1,69 +0,0 @@ -error[E0223]: ambiguous associated type - --> $DIR/issue-109195.rs:2:5 - | -LL | String::from::utf8; - | ^^^^^^^^^^^^ - | -help: there is an associated function with a similar name: `from_utf8` - | -LL | String::from_utf8; - | ~~~~~~~~~ - -error[E0223]: ambiguous associated type - --> $DIR/issue-109195.rs:5:5 - | -LL | String::from::utf8(); - | ^^^^^^^^^^^^ - | -help: there is an associated function with a similar name: `from_utf8` - | -LL | String::from_utf8(); - | ~~~~~~~~~ - -error[E0223]: ambiguous associated type - --> $DIR/issue-109195.rs:8:5 - | -LL | String::from::utf16(); - | ^^^^^^^^^^^^ - | -help: there is an associated function with a similar name: `from_utf16` - | -LL | String::from_utf16(); - | ~~~~~~~~~~ - -error[E0223]: ambiguous associated type - --> $DIR/issue-109195.rs:11:5 - | -LL | String::from::method_that_doesnt_exist(); - | ^^^^^^^^^^^^ - | -help: if there were a trait named `Example` with associated type `from` implemented for `String`, you could use the fully-qualified path - | -LL | ::from::method_that_doesnt_exist(); - | ~~~~~~~~~~~~~~~~~~~~~~~~~ - -error[E0223]: ambiguous associated type - --> $DIR/issue-109195.rs:14:5 - | -LL | str::from::utf8(); - | ^^^^^^^^^ - | -help: if there were a trait named `Example` with associated type `from` implemented for `str`, you could use the fully-qualified path - | -LL | ::from::utf8(); - | ~~~~~~~~~~~~~~~~~~~~~~ - -error[E0223]: ambiguous associated type - --> $DIR/issue-109195.rs:17:5 - | -LL | str::from::utf8_mut(); - | ^^^^^^^^^ - | -help: if there were a trait named `Example` with associated type `from` implemented for `str`, you could use the fully-qualified path - | -LL | ::from::utf8_mut(); - | ~~~~~~~~~~~~~~~~~~~~~~ - -error: aborting due to 6 previous errors - -For more information about this error, try `rustc --explain E0223`. diff --git a/tests/ui/type-alias-impl-trait/issue-77179.stderr b/tests/ui/type-alias-impl-trait/issue-77179.stderr index 85a943c26e2d..16bbc996d909 100644 --- a/tests/ui/type-alias-impl-trait/issue-77179.stderr +++ b/tests/ui/type-alias-impl-trait/issue-77179.stderr @@ -28,10 +28,7 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures --> $DIR/issue-77179.rs:18:25 | LL | fn bar() -> Pointer<_>; - | ^ - | | - | not allowed in type signatures - | help: use type parameters instead: `T` + | ^ not allowed in type signatures error: aborting due to 3 previous errors diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs index 55f45ade3880..c5f8b2764ecb 100644 --- a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs +++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.rs @@ -4,7 +4,7 @@ type Pat = std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); //~^ ERROR type and const arguments are not allowed on const parameter `START` -//~| ERROR type arguments are not allowed on const parameter `END` +//~| ERROR generic arguments are not allowed on const parameter `END` //~| ERROR associated item constraints are not allowed here fn main() {} diff --git a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr index 7f4e6e314f53..f31809bf397c 100644 --- a/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr +++ b/tests/ui/type/pattern_types/bad_const_generics_args_on_const_param.stderr @@ -12,11 +12,11 @@ note: const parameter `START` defined here LL | type Pat = | ^^^^^ -error[E0109]: type arguments are not allowed on const parameter `END` +error[E0109]: generic arguments are not allowed on const parameter `END` --> $DIR/bad_const_generics_args_on_const_param.rs:5:64 | LL | std::pat::pattern_type!(u32 is START::<(), i32, 2>..=END::<_, Assoc = ()>); - | --- ^ type argument not allowed + | --- ^ generic argument not allowed | | | not allowed on const parameter `END` | diff --git a/tests/ui/typeck/ice-self-mismatch-const-generics.stderr b/tests/ui/typeck/ice-self-mismatch-const-generics.stderr index c502ea4565f6..068cf3ee9036 100644 --- a/tests/ui/typeck/ice-self-mismatch-const-generics.stderr +++ b/tests/ui/typeck/ice-self-mismatch-const-generics.stderr @@ -8,8 +8,8 @@ LL | pub fn new(thing: T) -> GenericStruct<1, T> { LL | Self { thing } | ^^^^^^^^^^^^^^ expected `1`, found `0` | - = note: expected struct `GenericStruct<_, 1>` - found struct `GenericStruct<_, 0>` + = note: expected struct `GenericStruct<1, _>` + found struct `GenericStruct<0, _>` help: use the type name directly | LL | GenericStruct::<1, T> { thing } @@ -25,8 +25,8 @@ LL | pub fn new(thing: T) -> GenericStruct2<1, T> { LL | Self { 0: thing } | ^^^^^^^^^^^^^^^^^ expected `1`, found `0` | - = note: expected struct `GenericStruct2<_, 1>` - found struct `GenericStruct2<_, 0>` + = note: expected struct `GenericStruct2<1, _>` + found struct `GenericStruct2<0, _>` help: use the type name directly | LL | GenericStruct2::<1, T> { 0: thing } diff --git a/tests/ui/typeck/typeck_type_placeholder_item.stderr b/tests/ui/typeck/typeck_type_placeholder_item.stderr index c97b9312076d..d2a850d7dbfb 100644 --- a/tests/ui/typeck/typeck_type_placeholder_item.stderr +++ b/tests/ui/typeck/typeck_type_placeholder_item.stderr @@ -507,22 +507,12 @@ LL | impl BadTrait<_> for BadStruct<_> {} | ^ ^ not allowed in type signatures | | | not allowed in type signatures - | -help: use type parameters instead - | -LL | impl BadTrait for BadStruct {} - | +++ ~ ~ error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:162:34 | LL | fn impl_trait() -> impl BadTrait<_> { | ^ not allowed in type signatures - | -help: use type parameters instead - | -LL | fn impl_trait() -> impl BadTrait { - | +++ ~ error[E0121]: the placeholder `_` is not allowed within types on item signatures for structs --> $DIR/typeck_type_placeholder_item.rs:167:25