diff options
Diffstat (limited to 'compiler')
175 files changed, 4755 insertions, 3734 deletions
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 8f7dd774207..ca0b7f2ac3a 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -120,6 +120,9 @@ ast_passes_fn_without_body = ast_passes_forbidden_bound = bounds cannot be used in this context +ast_passes_forbidden_const_param = + late-bound const parameters cannot be used currently + ast_passes_forbidden_default = `default` is only allowed on items in trait impls .label = `default` because of this diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 783bca6b695..215ccd2ab4d 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -70,6 +70,13 @@ pub struct ForbiddenBound { } #[derive(Diagnostic)] +#[diag(ast_passes_forbidden_const_param)] +pub struct ForbiddenConstParam { + #[primary_span] + pub const_param_spans: Vec<Span>, +} + +#[derive(Diagnostic)] #[diag(ast_passes_fn_param_too_many)] pub struct FnParamTooMany { #[primary_span] diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 2178b65727d..e91dfb27766 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -162,6 +162,22 @@ impl<'a> PostExpansionVisitor<'a> { crate::fluent_generated::ast_passes_forbidden_non_lifetime_param ); + // FIXME(non_lifetime_binders): Const bound params are pretty broken. + // Let's keep users from using this feature accidentally. + if self.features.non_lifetime_binders { + let const_param_spans: Vec<_> = params + .iter() + .filter_map(|param| match param.kind { + ast::GenericParamKind::Const { .. } => Some(param.ident.span), + _ => None, + }) + .collect(); + + if !const_param_spans.is_empty() { + self.sess.dcx().emit_err(errors::ForbiddenConstParam { const_param_spans }); + } + } + for param in params { if !param.bounds.is_empty() { let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect(); diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 8eb44458137..80deea14685 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -1,7 +1,9 @@ #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +use rustc_errors::Applicability; use rustc_errors::{codes::*, struct_span_code_err, Diag, DiagCtxtHandle}; +use rustc_hir as hir; use rustc_middle::span_bug; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::Span; @@ -382,13 +384,35 @@ impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { yield_span: Span, ) -> Diag<'infcx> { let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind; - struct_span_code_err!( + let mut diag = struct_span_code_err!( self.dcx(), span, E0626, "borrow may still be in use when {coroutine_kind:#} yields", - ) - .with_span_label(yield_span, "possible yield occurs here") + ); + diag.span_label( + self.infcx.tcx.def_span(self.body.source.def_id()), + format!("within this {coroutine_kind:#}"), + ); + diag.span_label(yield_span, "possible yield occurs here"); + if matches!(coroutine_kind, hir::CoroutineKind::Coroutine(_)) { + let hir::Closure { capture_clause, fn_decl_span, .. } = self + .infcx + .tcx + .hir_node_by_def_id(self.body.source.def_id().expect_local()) + .expect_closure(); + let span = match capture_clause { + rustc_hir::CaptureBy::Value { move_kw } => move_kw.shrink_to_lo(), + rustc_hir::CaptureBy::Ref => fn_decl_span.shrink_to_lo(), + }; + diag.span_suggestion_verbose( + span, + "add `static` to mark this coroutine as unmovable", + "static ", + Applicability::MaybeIncorrect, + ); + } + diag } pub(crate) fn cannot_borrow_across_destructor(&self, borrow_span: Span) -> Diag<'infcx> { diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 4cd0d9cb294..cbee01f2e2d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -1,6 +1,5 @@ use rustc_errors::Diag; use rustc_hir::def_id::LocalDefId; -use rustc_infer::error_reporting::infer::nice_region_error::NiceRegionError; use rustc_infer::infer::canonical::Canonical; use rustc_infer::infer::region_constraints::Constraint; use rustc_infer::infer::region_constraints::RegionConstraintData; @@ -14,6 +13,8 @@ use rustc_middle::ty::RegionVid; use rustc_middle::ty::UniverseIndex; use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc_span::Span; +use rustc_trait_selection::error_reporting::infer::nice_region_error::NiceRegionError; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::query::type_op; use rustc_trait_selection::traits::ObligationCtxt; use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_with_cause}; diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index f7e4bba3712..b147567001d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -35,8 +35,8 @@ use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{BytePos, Span, Symbol}; -use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt; use rustc_trait_selection::error_reporting::traits::FindExprBySpan; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; use std::iter; @@ -456,10 +456,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { if let Some(def_id) = def_id && self.infcx.tcx.def_kind(def_id).is_fn_like() && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id) - && let ty::Param(_) = - self.infcx.tcx.fn_sig(def_id).skip_binder().skip_binder().inputs() - [pos + offset] - .kind() + && let Some(arg) = self + .infcx + .tcx + .fn_sig(def_id) + .skip_binder() + .skip_binder() + .inputs() + .get(pos + offset) + && let ty::Param(_) = arg.kind() { let place = &self.move_data.move_paths[mpi].place; let ty = place.ty(self.body, self.infcx.tcx).ty; diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index f97459d16ba..d505d9c004e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -27,7 +27,7 @@ use rustc_span::def_id::LocalDefId; use rustc_span::source_map::Spanned; use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; use rustc_target::abi::{FieldIdx, VariantIdx}; -use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{ type_known_to_meet_bound_modulo_regions, FulfillmentErrorCode, diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 26b0d23b166..a7bf6d636c1 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -16,7 +16,7 @@ use rustc_middle::{ use rustc_span::symbol::{kw, Symbol}; use rustc_span::{sym, BytePos, DesugaringKind, Span}; use rustc_target::abi::FieldIdx; -use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 6cf797b4761..6b7bd7dc0d8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -10,11 +10,6 @@ use rustc_hir::GenericBound::Trait; use rustc_hir::QPath::Resolved; use rustc_hir::WherePredicate::BoundPredicate; use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate}; -use rustc_infer::error_reporting::infer::nice_region_error::{ - self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params, - HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, -}; -use rustc_infer::error_reporting::infer::region::unexpected_hidden_region_diagnostic; use rustc_infer::infer::{NllRegionVariableOrigin, RelateParamBound}; use rustc_middle::bug; use rustc_middle::hir::place::PlaceBase; @@ -25,6 +20,12 @@ use rustc_middle::ty::{self, RegionVid, Ty}; use rustc_middle::ty::{Region, TyCtxt}; use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; +use rustc_trait_selection::error_reporting::infer::nice_region_error::{ + self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params, + HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor, +}; +use rustc_trait_selection::error_reporting::infer::region::unexpected_hidden_region_diagnostic; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{Obligation, ObligationCtxt}; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index 356416d1a75..6443c5e92e8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -14,6 +14,7 @@ use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use crate::{universal_regions::DefiningTy, MirBorrowckCtxt}; @@ -457,8 +458,11 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { ) -> RegionNameHighlight { let mut highlight = RegionHighlightMode::default(); highlight.highlighting_region_vid(self.infcx.tcx, needle_fr, counter); - let type_name = - self.infcx.extract_inference_diagnostics_data(ty.into(), Some(highlight)).name; + let type_name = self + .infcx + .err_ctxt() + .extract_inference_diagnostics_data(ty.into(), Some(highlight)) + .name; debug!( "highlight_if_we_cannot_match_hir_ty: type_name={:?} needle_fr={:?}", @@ -872,8 +876,11 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> { let mut highlight = RegionHighlightMode::default(); highlight.highlighting_region_vid(tcx, fr, *self.next_region_name.try_borrow().unwrap()); - let type_name = - self.infcx.extract_inference_diagnostics_data(yield_ty.into(), Some(highlight)).name; + let type_name = self + .infcx + .err_ctxt() + .extract_inference_diagnostics_data(yield_ty.into(), Some(highlight)) + .name; let yield_span = match tcx.hir_node(self.mir_hir_id()) { hir::Node::Expr(hir::Expr { diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index c0e91ce32e3..cf28ba224d6 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -11,7 +11,7 @@ use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable}; use rustc_middle::ty::{GenericArgKind, GenericArgs}; use rustc_span::Span; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::ObligationCtxt; use crate::session_diagnostics::LifetimeMismatchOpaqueParam; diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 431a704687d..e4c2e0fced7 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -11,7 +11,7 @@ use rustc_middle::traits::query::OutlivesBound; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt}; use rustc_span::{ErrorGuaranteed, Span}; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::solve::deeply_normalize; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use std::rc::Rc; diff --git a/compiler/rustc_builtin_macros/src/deriving/bounds.rs b/compiler/rustc_builtin_macros/src/deriving/bounds.rs index 97e2344ff30..f6b54335829 100644 --- a/compiler/rustc_builtin_macros/src/deriving/bounds.rs +++ b/compiler/rustc_builtin_macros/src/deriving/bounds.rs @@ -38,7 +38,44 @@ pub(crate) fn expand_deriving_const_param_ty( ) { let trait_def = TraitDef { span, - path: path_std!(marker::ConstParamTy), + path: path_std!(marker::ConstParamTy_), + skip_path_as_bound: false, + needs_copy_as_bound_if_packed: false, + additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))], + supports_unions: false, + methods: Vec::new(), + associated_types: Vec::new(), + is_const, + }; + + trait_def.expand(cx, mitem, item, push); + + let trait_def = TraitDef { + span, + path: path_std!(marker::UnsizedConstParamTy), + skip_path_as_bound: false, + needs_copy_as_bound_if_packed: false, + additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))], + supports_unions: false, + methods: Vec::new(), + associated_types: Vec::new(), + is_const, + }; + + trait_def.expand(cx, mitem, item, push); +} + +pub(crate) fn expand_deriving_unsized_const_param_ty( + cx: &ExtCtxt<'_>, + span: Span, + mitem: &MetaItem, + item: &Annotatable, + push: &mut dyn FnMut(Annotatable), + is_const: bool, +) { + let trait_def = TraitDef { + span, + path: path_std!(marker::UnsizedConstParamTy), skip_path_as_bound: false, needs_copy_as_bound_if_packed: false, additional_bounds: vec![ty::Ty::Path(path_std!(cmp::Eq))], diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index f8d93666145..c77ff9eb13c 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -118,6 +118,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { Clone: clone::expand_deriving_clone, Copy: bounds::expand_deriving_copy, ConstParamTy: bounds::expand_deriving_const_param_ty, + UnsizedConstParamTy: bounds::expand_deriving_unsized_const_param_ty, Debug: debug::expand_deriving_debug, Default: default::expand_deriving_default, Eq: eq::expand_deriving_eq, diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md index eb21e027dd0..3b3c86a1bd1 100644 --- a/compiler/rustc_codegen_cranelift/Readme.md +++ b/compiler/rustc_codegen_cranelift/Readme.md @@ -70,7 +70,7 @@ For more docs on how to build and test see [build_system/usage.txt](build_system |FreeBSD|✅[^no-rustup]|❓|❓|❓| |AIX|❌[^xcoff]|N/A|N/A|❌[^xcoff]| |Other unixes|❓|❓|❓|❓| -|macOS|✅|✅[^no-rustup]|N/A|N/A| +|macOS|✅|✅|N/A|N/A| |Windows|✅[^no-rustup]|❌|N/A|N/A| ✅: Fully supported and tested diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index fa0de6f9de5..24cf3f061a5 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -10,12 +10,12 @@ use std::mem; use cranelift_codegen::ir::{ArgumentPurpose, SigRef}; use cranelift_codegen::isa::CallConv; use cranelift_module::ModuleError; +use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::TypeVisitableExt; -use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_session::Session; use rustc_span::source_map::Spanned; use rustc_target::abi::call::{Conv, FnAbi, PassMode}; @@ -505,7 +505,7 @@ pub(crate) fn codegen_terminator_call<'tcx>( let nop_inst = fx.bcx.ins().nop(); fx.add_comment( nop_inst, - format!("virtual call; self arg pass mode: {:?}", &fn_abi.args[0]), + format!("virtual call; self arg pass mode: {:?}", fn_abi.args[0]), ); } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 5adbbb09ac8..9bc7b57c537 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -5,13 +5,13 @@ use cranelift_codegen::CodegenError; use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; use cranelift_module::ModuleError; use rustc_ast::InlineAsmOptions; +use rustc_codegen_ssa::base::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_index::IndexVec; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::TypeVisitableExt; -use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use crate::constant::ConstantCx; use crate::debuginfo::{FunctionDebugContext, TypeDebugContext}; diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 192e6c91ea3..8d3d5ac98e1 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -24,7 +24,6 @@ extern crate rustc_hir; extern crate rustc_incremental; extern crate rustc_index; extern crate rustc_metadata; -extern crate rustc_monomorphize; extern crate rustc_session; extern crate rustc_span; extern crate rustc_target; diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index e7669470026..3877460fcdb 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -271,6 +271,17 @@ fn stackprotector_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { Some(sspattr.create_attr(cx.llcx)) } +fn backchain_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> { + if cx.sess().target.arch != "s390x" { + return None; + } + + let requested_features = cx.sess().opts.cg.target_feature.split(','); + let found_positive = requested_features.clone().any(|r| r == "+backchain"); + + if found_positive { Some(llvm::CreateAttrString(cx.llcx, "backchain")) } else { None } +} + pub fn target_cpu_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> &'ll Attribute { let target_cpu = llvm_util::target_cpu(cx.tcx.sess); llvm::CreateAttrStringValue(cx.llcx, "target-cpu", target_cpu) @@ -447,6 +458,9 @@ pub fn from_fn_attrs<'ll, 'tcx>( if let Some(align) = codegen_fn_attrs.alignment { llvm::set_alignment(llfn, align); } + if let Some(backchain) = backchain_attr(cx) { + to_add.push(backchain); + } to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize)); to_add.extend(patchable_function_entry_attrs(cx, codegen_fn_attrs.patchable_function_entry)); diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 98dc8ac86d2..4d56d1d3b1a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -14,7 +14,7 @@ use rustc_session::config::{PrintKind, PrintRequest}; use rustc_session::Session; use rustc_span::symbol::Symbol; use rustc_target::spec::{MergeFunctions, PanicStrategy}; -use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; +use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES}; use std::ffi::{c_char, c_void, CStr, CString}; use std::fmt::Write; @@ -321,6 +321,10 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> { } }) .filter(|feature| { + // skip checking special features, as LLVM may not understands them + if RUSTC_SPECIAL_FEATURES.contains(feature) { + return true; + } // check that all features in a given smallvec are enabled for llvm_feature in to_llvm_features(sess, feature) { let cstr = SmallCStr::new(llvm_feature); @@ -546,6 +550,7 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str // -Ctarget-features let supported_features = sess.target.supported_target_features(); + let (llvm_major, _, _) = get_version(); let mut featsmap = FxHashMap::default(); let feats = sess .opts @@ -604,6 +609,13 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str if RUSTC_SPECIFIC_FEATURES.contains(&feature) { return None; } + + // if the target-feature is "backchain" and LLVM version is greater than 18 + // then we also need to add "+backchain" to the target-features attribute. + // otherwise, we will only add the naked `backchain` attribute to the attribute-group. + if feature == "backchain" && llvm_major < 18 { + return None; + } // ... otherwise though we run through `to_llvm_features` when // passing requests down to LLVM. This means that all in-language // features also work on the command line instead of having two diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 8e07d128dbd..8c582fac0d8 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -759,7 +759,7 @@ fn link_natively( sess.dcx().abort_if_errors(); // Invoke the system linker - info!("{:?}", &cmd); + info!("{cmd:?}"); let retry_on_segfault = env::var("RUSTC_RETRY_LINKER_ON_SEGFAULT").is_ok(); let unknown_arg_regex = Regex::new(r"(unknown|unrecognized) (command line )?(option|argument)").unwrap(); @@ -796,7 +796,7 @@ fn link_natively( cmd.arg(arg); } } - info!("{:?}", &cmd); + info!("{cmd:?}"); continue; } @@ -817,7 +817,7 @@ fn link_natively( cmd.arg(arg); } } - info!("{:?}", &cmd); + info!("{cmd:?}"); continue; } @@ -878,7 +878,7 @@ fn link_natively( cmd.arg(arg); } } - info!("{:?}", &cmd); + info!("{cmd:?}"); continue; } @@ -996,7 +996,7 @@ fn link_natively( sess.dcx().emit_err(errors::UnableToExeLinker { linker_path, error: e, - command_formatted: format!("{:?}", &cmd), + command_formatted: format!("{cmd:?}"), }); } @@ -1567,7 +1567,7 @@ fn print_native_static_libs( sess.dcx().emit_note(errors::StaticLibraryNativeArtifacts); // Prefix for greppability // Note: This must not be translated as tools are allowed to depend on this exact string. - sess.dcx().note(format!("native-static-libs: {}", &lib_args.join(" "))); + sess.dcx().note(format!("native-static-libs: {}", lib_args.join(" "))); } } } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 137f14fe706..399ac485850 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -806,6 +806,34 @@ pub fn codegen_crate<B: ExtraBackendMethods>( ongoing_codegen } +/// Returns whether a call from the current crate to the [`Instance`] would produce a call +/// from `compiler_builtins` to a symbol the linker must resolve. +/// +/// Such calls from `compiler_bultins` are effectively impossible for the linker to handle. Some +/// linkers will optimize such that dead calls to unresolved symbols are not an error, but this is +/// not guaranteed. So we used this function in codegen backends to ensure we do not generate any +/// unlinkable calls. +/// +/// Note that calls to LLVM intrinsics are uniquely okay because they won't make it to the linker. +pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, +) -> bool { + fn is_llvm_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + if let Some(name) = tcx.codegen_fn_attrs(def_id).link_name { + name.as_str().starts_with("llvm.") + } else { + false + } + } + + let def_id = instance.def_id(); + !def_id.is_local() + && tcx.is_compiler_builtins(LOCAL_CRATE) + && !is_llvm_intrinsic(tcx, def_id) + && !tcx.should_codegen_locally(instance) +} + impl CrateInfo { pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo { let crate_types = tcx.crate_types().to_vec(); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 56a893738df..bfa4c683d56 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -328,7 +328,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { sym::link_section => { if let Some(val) = attr.value_str() { if val.as_str().bytes().any(|b| b == 0) { - let msg = format!("illegal null byte in link_section value: `{}`", &val); + let msg = format!("illegal null byte in link_section value: `{val}`"); tcx.dcx().span_err(attr.span, msg); } else { codegen_fn_attrs.link_section = Some(val); @@ -726,7 +726,7 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> { if *ordinal <= u16::MAX as u128 { Some(ordinal.get() as u16) } else { - let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal); + let msg = format!("ordinal value in `link_ordinal` is too large: `{ordinal}`"); tcx.dcx() .struct_span_err(attr.span, msg) .with_note("the value may not exceed `u16::MAX`") diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 6a5525dc2b3..c9c8f02c491 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -3,7 +3,7 @@ use super::operand::OperandValue::{Immediate, Pair, Ref, ZeroSized}; use super::place::{PlaceRef, PlaceValue}; use super::{CachedLlbb, FunctionCx, LocalRef}; -use crate::base; +use crate::base::{self, is_call_from_compiler_builtins_to_upstream_monomorphization}; use crate::common::{self, IntPredicate}; use crate::errors::CompilerBuiltinsCannotCall; use crate::meth; @@ -18,7 +18,6 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, span_bug}; -use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization; use rustc_session::config::OptLevel; use rustc_span::{source_map::Spanned, sym, Span}; use rustc_target::abi::call::{ArgAbi, FnAbi, PassMode, Reg}; diff --git a/compiler/rustc_codegen_ssa/src/mono_item.rs b/compiler/rustc_codegen_ssa/src/mono_item.rs index 0fbcb938d1a..559ec400577 100644 --- a/compiler/rustc_codegen_ssa/src/mono_item.rs +++ b/compiler/rustc_codegen_ssa/src/mono_item.rs @@ -130,7 +130,7 @@ impl<'a, 'tcx: 'a> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { let symbol_name = self.symbol_name(cx.tcx()).name; - debug!("symbol {}", &symbol_name); + debug!("symbol {symbol_name}"); match *self { MonoItem::Static(def_id) => { diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index cea164df617..e7cee5220d6 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -82,6 +82,7 @@ pub fn from_target_feature( Some(sym::prfchw_target_feature) => rust_features.prfchw_target_feature, Some(sym::x86_amx_intrinsics) => rust_features.x86_amx_intrinsics, Some(sym::xop_target_feature) => rust_features.xop_target_feature, + Some(sym::s390x_target_feature) => rust_features.s390x_target_feature, Some(name) => bug!("unknown target feature gate {}", name), None => true, }; diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 523d55fe2d0..8700ec4c210 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt}; use rustc_middle::ty::{Instance, InstanceKind, TypeVisitableExt}; use rustc_mir_dataflow::Analysis; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt}; use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitor}; diff --git a/compiler/rustc_error_codes/src/error_codes/E0373.md b/compiler/rustc_error_codes/src/error_codes/E0373.md index effa597aad9..d4d26007aa5 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0373.md +++ b/compiler/rustc_error_codes/src/error_codes/E0373.md @@ -70,4 +70,4 @@ fn spawn<F: Future + Send + 'static>(future: F) { Similarly to closures, `async` blocks are not executed immediately and may capture closed-over data by reference. For more information, see -https://rust-lang.github.io/async-book/03_async_await/01_chapter.html. +<https://rust-lang.github.io/async-book/03_async_await/01_chapter.html>. diff --git a/compiler/rustc_error_codes/src/error_codes/E0378.md b/compiler/rustc_error_codes/src/error_codes/E0378.md index c6fe997f3dc..7d939b99b04 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0378.md +++ b/compiler/rustc_error_codes/src/error_codes/E0378.md @@ -20,7 +20,7 @@ where The `DispatchFromDyn` trait currently can only be implemented for builtin pointer types and structs that are newtype wrappers around them -— that is, the struct must have only one field (except for`PhantomData`), +— that is, the struct must have only one field (except for `PhantomData`), and that field must itself implement `DispatchFromDyn`. ``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0626.md b/compiler/rustc_error_codes/src/error_codes/E0626.md index 28d543350ff..71c1f811aa7 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0626.md +++ b/compiler/rustc_error_codes/src/error_codes/E0626.md @@ -1,4 +1,4 @@ -This error occurs because a borrow in a coroutine persists across a +This error occurs because a borrow in a movable coroutine persists across a yield point. Erroneous code example: @@ -15,19 +15,35 @@ let mut b = #[coroutine] || { Pin::new(&mut b).resume(()); ``` -At present, it is not permitted to have a yield that occurs while a -borrow is still in scope. To resolve this error, the borrow must -either be "contained" to a smaller scope that does not overlap the -yield or else eliminated in another way. So, for example, we might -resolve the previous example by removing the borrow and just storing -the integer by value: +Coroutines may be either unmarked, or marked with `static`. If it is unmarked, +then the coroutine is considered "movable". At present, it is not permitted to +have a yield in a movable coroutine that occurs while a borrow is still in +scope. To resolve this error, the coroutine may be marked `static`: + +``` +# #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] +# use std::ops::Coroutine; +# use std::pin::Pin; +let mut b = #[coroutine] static || { // <-- note the static keyword + let a = &String::from("hello, world"); + yield (); + println!("{}", a); +}; +let mut b = std::pin::pin!(b); +b.as_mut().resume(()); +``` + +If the coroutine must remain movable, for example to be used as `Unpin` +without pinning it on the stack or in an allocation, we can alternatively +resolve the previous example by removing the borrow and just storing the +type by value: ``` # #![feature(coroutines, coroutine_trait, stmt_expr_attributes)] # use std::ops::Coroutine; # use std::pin::Pin; let mut b = #[coroutine] || { - let a = 3; + let a = String::from("hello, world"); yield (); println!("{}", a); }; diff --git a/compiler/rustc_error_codes/src/error_codes/E0771.md b/compiler/rustc_error_codes/src/error_codes/E0771.md index 4f36590025b..74149eb79f6 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0771.md +++ b/compiler/rustc_error_codes/src/error_codes/E0771.md @@ -6,7 +6,7 @@ allowed. Erroneous code example: ```compile_fail,E0770 -#![feature(adt_const_params)] +#![feature(adt_const_params, unsized_const_params)] fn function_with_str<'a, const STRING: &'a str>() {} // error! ``` @@ -15,7 +15,7 @@ To fix this issue, the lifetime in the const generic need to be changed to `'static`: ``` -#![feature(adt_const_params)] +#![feature(adt_const_params, unsized_const_params)] fn function_with_str<const STRING: &'static str>() {} // ok! ``` diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 2086d4030f9..2a850d9303c 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -15,6 +15,7 @@ #![feature(box_patterns)] #![feature(error_reporter)] #![feature(extract_if)] +#![feature(if_let_guard)] #![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] diff --git a/compiler/rustc_errors/src/markdown/parse.rs b/compiler/rustc_errors/src/markdown/parse.rs index 67e4963fddf..69e7120e714 100644 --- a/compiler/rustc_errors/src/markdown/parse.rs +++ b/compiler/rustc_errors/src/markdown/parse.rs @@ -10,15 +10,15 @@ const CBK: &[u8] = b"```"; const CIL: &[u8] = b"`"; const CMT_E: &[u8] = b"-->"; const CMT_S: &[u8] = b"<!--"; -const EMP: &[u8] = b"_"; +const EMP_U: &[u8] = b"_"; +const EMP_A: &[u8] = b"*"; const HDG: &[u8] = b"#"; const LNK_CHARS: &str = "$-_.+!*'()/&?=:%"; const LNK_E: &[u8] = b"]"; const LNK_S: &[u8] = b"["; -const STG: &[u8] = b"**"; +const STG_U: &[u8] = b"__"; +const STG_A: &[u8] = b"**"; const STK: &[u8] = b"~~"; -const UL1: &[u8] = b"* "; -const UL2: &[u8] = b"- "; /// Pattern replacements const REPLACEMENTS: &[(&str, &str)] = &[ @@ -100,22 +100,29 @@ fn parse_recursive<'a>(buf: &'a [u8], ctx: Context) -> MdStream<'_> { }; let res: ParseResult<'_> = match (top_blk, prev) { - (_, Newline | Whitespace) if loop_buf.starts_with(CMT_S) => { + _ if loop_buf.starts_with(CMT_S) => { parse_simple_pat(loop_buf, CMT_S, CMT_E, Po::TrimNoEsc, MdTree::Comment) } (true, Newline) if loop_buf.starts_with(CBK) => Some(parse_codeblock(loop_buf)), - (_, Newline | Whitespace) if loop_buf.starts_with(CIL) => parse_codeinline(loop_buf), + _ if loop_buf.starts_with(CIL) => parse_codeinline(loop_buf), (true, Newline | Whitespace) if loop_buf.starts_with(HDG) => parse_heading(loop_buf), (true, Newline) if loop_buf.starts_with(BRK) => { Some((MdTree::HorizontalRule, parse_to_newline(loop_buf).1)) } - (_, Newline | Whitespace) if loop_buf.starts_with(EMP) => { - parse_simple_pat(loop_buf, EMP, EMP, Po::None, MdTree::Emphasis) + (_, Newline) if unordered_list_start(loop_buf) => Some(parse_unordered_li(loop_buf)), + (_, Newline | Whitespace) if loop_buf.starts_with(STG_U) => { + parse_simple_pat(loop_buf, STG_U, STG_U, Po::None, MdTree::Strong) } - (_, Newline | Whitespace) if loop_buf.starts_with(STG) => { - parse_simple_pat(loop_buf, STG, STG, Po::None, MdTree::Strong) + _ if loop_buf.starts_with(STG_A) => { + parse_simple_pat(loop_buf, STG_A, STG_A, Po::None, MdTree::Strong) } - (_, Newline | Whitespace) if loop_buf.starts_with(STK) => { + (_, Newline | Whitespace) if loop_buf.starts_with(EMP_U) => { + parse_simple_pat(loop_buf, EMP_U, EMP_U, Po::None, MdTree::Emphasis) + } + _ if loop_buf.starts_with(EMP_A) => { + parse_simple_pat(loop_buf, EMP_A, EMP_A, Po::None, MdTree::Emphasis) + } + _ if loop_buf.starts_with(STK) => { parse_simple_pat(loop_buf, STK, STK, Po::None, MdTree::Strikethrough) } (_, Newline | Whitespace) if loop_buf.starts_with(ANC_S) => { @@ -130,11 +137,8 @@ fn parse_recursive<'a>(buf: &'a [u8], ctx: Context) -> MdStream<'_> { _ => None, } } - (_, Newline) if (loop_buf.starts_with(UL1) || loop_buf.starts_with(UL2)) => { - Some(parse_unordered_li(loop_buf)) - } (_, Newline) if ord_list_start(loop_buf).is_some() => Some(parse_ordered_li(loop_buf)), - (_, Newline | Whitespace) if loop_buf.starts_with(LNK_S) => { + _ if loop_buf.starts_with(LNK_S) => { parse_any_link(loop_buf, top_blk && prev == Prev::Newline) } (_, Escape | _) => None, @@ -251,7 +255,6 @@ fn parse_heading(buf: &[u8]) -> ParseResult<'_> { /// Bulleted list fn parse_unordered_li(buf: &[u8]) -> Parsed<'_> { - debug_assert!(buf.starts_with(b"* ") || buf.starts_with(b"- ")); let (txt, rest) = get_indented_section(&buf[2..]); let ctx = Context { top_block: false, prev: Prev::Whitespace }; let stream = parse_recursive(trim_ascii_start(txt), ctx); @@ -267,25 +270,28 @@ fn parse_ordered_li(buf: &[u8]) -> Parsed<'_> { (MdTree::OrderedListItem(num, stream), rest) } -/// Find first line that isn't empty or doesn't start with whitespace, that will -/// be our contents fn get_indented_section(buf: &[u8]) -> (&[u8], &[u8]) { - let mut end = buf.len(); - for (idx, window) in buf.windows(2).enumerate() { - let &[ch, next_ch] = window else { unreachable!("always 2 elements") }; - if idx >= buf.len().saturating_sub(2) && next_ch == b'\n' { - // End of stream - end = buf.len().saturating_sub(1); - break; - } else if ch == b'\n' && (!next_ch.is_ascii_whitespace() || next_ch == b'\n') { - end = idx; - break; + let mut lines = buf.split(|&byte| byte == b'\n'); + let mut end = lines.next().map_or(0, |line| line.len()); + for line in lines { + if let Some(first) = line.first() { + if unordered_list_start(line) || !first.is_ascii_whitespace() { + break; + } } + end += line.len() + 1; } (&buf[..end], &buf[end..]) } +fn unordered_list_start(mut buf: &[u8]) -> bool { + while let [b' ', rest @ ..] = buf { + buf = rest; + } + matches!(buf, [b'*' | b'-', b' ', ..]) +} + /// Verify a valid ordered list start (e.g. `1.`) and parse it. Returns the /// parsed number and offset of character after the dot. fn ord_list_start(buf: &[u8]) -> Option<(u16, usize)> { diff --git a/compiler/rustc_errors/src/markdown/tests/parse.rs b/compiler/rustc_errors/src/markdown/tests/parse.rs index e39e8c89b35..e2e3f354ff6 100644 --- a/compiler/rustc_errors/src/markdown/tests/parse.rs +++ b/compiler/rustc_errors/src/markdown/tests/parse.rs @@ -4,13 +4,13 @@ use ParseOpt as PO; #[test] fn test_parse_simple() { let buf = "**abcd** rest"; - let (t, r) = parse_simple_pat(buf.as_bytes(), STG, STG, PO::None, MdTree::Strong).unwrap(); + let (t, r) = parse_simple_pat(buf.as_bytes(), b"**", b"**", PO::None, MdTree::Strong).unwrap(); assert_eq!(t, MdTree::Strong("abcd")); assert_eq!(r, b" rest"); // Escaping should fail let buf = r"**abcd\** rest"; - let res = parse_simple_pat(buf.as_bytes(), STG, STG, PO::None, MdTree::Strong); + let res = parse_simple_pat(buf.as_bytes(), b"**", b"**", PO::None, MdTree::Strong); assert!(res.is_none()); } @@ -141,12 +141,12 @@ fn test_indented_section() { assert_eq!(str::from_utf8(r).unwrap(), "\nnot ind"); let (txt, rest) = get_indented_section(IND2.as_bytes()); - assert_eq!(str::from_utf8(txt).unwrap(), "test end of stream\n 1\n 2"); - assert_eq!(str::from_utf8(rest).unwrap(), "\n"); + assert_eq!(str::from_utf8(txt).unwrap(), "test end of stream\n 1\n 2\n"); + assert_eq!(str::from_utf8(rest).unwrap(), ""); let (txt, rest) = get_indented_section(IND3.as_bytes()); - assert_eq!(str::from_utf8(txt).unwrap(), "test empty lines\n 1\n 2"); - assert_eq!(str::from_utf8(rest).unwrap(), "\n\nnot ind"); + assert_eq!(str::from_utf8(txt).unwrap(), "test empty lines\n 1\n 2\n"); + assert_eq!(str::from_utf8(rest).unwrap(), "\nnot ind"); } const HBT: &str = r"# Heading @@ -310,3 +310,56 @@ fn test_code_at_start() { let res = entrypoint(CODE_STARTLINE); assert_eq!(res, expected); } + +#[test] +fn test_code_in_parens() { + let expected = + vec![MdTree::PlainText("("), MdTree::CodeInline("Foo"), MdTree::PlainText(")")].into(); + let res = entrypoint("(`Foo`)"); + assert_eq!(res, expected); +} + +const LIST_WITH_SPACE: &str = " +para + * l1 + * l2 +"; + +#[test] +fn test_list_with_space() { + let expected = vec![ + MdTree::PlainText("para"), + MdTree::ParagraphBreak, + MdTree::UnorderedListItem(vec![MdTree::PlainText("l1")].into()), + MdTree::LineBreak, + MdTree::UnorderedListItem(vec![MdTree::PlainText("l2")].into()), + ] + .into(); + let res = entrypoint(LIST_WITH_SPACE); + assert_eq!(res, expected); +} + +const SNAKE_CASE: &str = " +foo*bar* +foo**bar** +foo_bar_ +foo__bar__ +"; + +#[test] +fn test_snake_case() { + let expected = vec![ + MdTree::PlainText("foo"), + MdTree::Emphasis("bar"), + MdTree::PlainText(" "), + MdTree::PlainText("foo"), + MdTree::Strong("bar"), + MdTree::PlainText(" "), + MdTree::PlainText("foo_bar_"), + MdTree::PlainText(" "), + MdTree::PlainText("foo__bar__"), + ] + .into(); + let res = entrypoint(SNAKE_CASE); + assert_eq!(res, expected); +} diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 56ef609612a..4b730d307fd 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -352,10 +352,10 @@ fn check_occurrences( check_ops_is_prefix(psess, node_id, macros, binders, ops, span, name); } TokenTree::MetaVarExpr(dl, ref mve) => { - let Some(name) = mve.ident().map(MacroRulesNormalizedIdent::new) else { - return; - }; - check_ops_is_prefix(psess, node_id, macros, binders, ops, dl.entire(), name); + mve.for_each_metavar((), |_, ident| { + let name = MacroRulesNormalizedIdent::new(*ident); + check_ops_is_prefix(psess, node_id, macros, binders, ops, dl.entire(), name); + }); } TokenTree::Delimited(.., ref del) => { check_nested_occurrences(psess, node_id, &del.tts, macros, binders, ops, guar); diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs index 2964ac8cc58..c4ba98f581e 100644 --- a/compiler/rustc_expand/src/mbe/metavar_expr.rs +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -111,10 +111,18 @@ impl MetaVarExpr { Ok(rslt) } - pub(crate) fn ident(&self) -> Option<Ident> { - match *self { - MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => Some(ident), - MetaVarExpr::Concat { .. } | MetaVarExpr::Index(..) | MetaVarExpr::Len(..) => None, + pub(crate) fn for_each_metavar<A>(&self, mut aux: A, mut cb: impl FnMut(A, &Ident) -> A) -> A { + match self { + MetaVarExpr::Concat(elems) => { + for elem in elems { + if let MetaVarExprConcatElem::Var(ident) = elem { + aux = cb(aux, ident) + } + } + aux + } + MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => cb(aux, ident), + MetaVarExpr::Index(..) | MetaVarExpr::Len(..) => aux, } } } diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 7e2ea8de5fc..62337756cd8 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -557,17 +557,13 @@ fn lockstep_iter_size( } } TokenTree::MetaVarExpr(_, expr) => { - let default_rslt = LockstepIterSize::Unconstrained; - let Some(ident) = expr.ident() else { - return default_rslt; - }; - let name = MacroRulesNormalizedIdent::new(ident); - match lookup_cur_matched(name, interpolations, repeats) { - Some(MatchedSeq(ads)) => { - default_rslt.with(LockstepIterSize::Constraint(ads.len(), name)) - } - _ => default_rslt, - } + expr.for_each_metavar(LockstepIterSize::Unconstrained, |lis, ident| { + lis.with(lockstep_iter_size( + &TokenTree::MetaVar(ident.span, *ident), + interpolations, + repeats, + )) + }) } TokenTree::Token(..) => LockstepIterSize::Unconstrained, } @@ -695,7 +691,23 @@ fn transcribe_metavar_expr<'a>( let symbol = match element { MetaVarExprConcatElem::Ident(elem) => elem.name, MetaVarExprConcatElem::Literal(elem) => *elem, - MetaVarExprConcatElem::Var(elem) => extract_var_symbol(dcx, *elem, interp)?, + MetaVarExprConcatElem::Var(ident) => { + match matched_from_ident(dcx, *ident, interp)? { + NamedMatch::MatchedSeq(named_matches) => { + let curr_idx = repeats.last().unwrap().0; + match &named_matches[curr_idx] { + // FIXME(c410-f3r) Nested repetitions are unimplemented + MatchedSeq(_) => unimplemented!(), + MatchedSingle(pnr) => { + extract_symbol_from_pnr(dcx, pnr, ident.span)? + } + } + } + NamedMatch::MatchedSingle(pnr) => { + extract_symbol_from_pnr(dcx, pnr, ident.span)? + } + } + } }; concatenated.push_str(symbol.as_str()); } @@ -752,41 +764,48 @@ fn transcribe_metavar_expr<'a>( } /// Extracts an metavariable symbol that can be an identifier, a token tree or a literal. -fn extract_var_symbol<'a>( +fn extract_symbol_from_pnr<'a>( dcx: DiagCtxtHandle<'a>, - ident: Ident, - interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>, + pnr: &ParseNtResult, + span_err: Span, ) -> PResult<'a, Symbol> { - if let NamedMatch::MatchedSingle(pnr) = matched_from_ident(dcx, ident, interp)? { - if let ParseNtResult::Ident(nt_ident, is_raw) = pnr { + match pnr { + ParseNtResult::Ident(nt_ident, is_raw) => { if let IdentIsRaw::Yes = is_raw { - return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR)); + return Err(dcx.struct_span_err(span_err, RAW_IDENT_ERR)); } return Ok(nt_ident.name); } - - if let ParseNtResult::Tt(TokenTree::Token(Token { kind, .. }, _)) = pnr { - if let TokenKind::Ident(symbol, is_raw) = kind { - if let IdentIsRaw::Yes = is_raw { - return Err(dcx.struct_span_err(ident.span, RAW_IDENT_ERR)); - } - return Ok(*symbol); - } - - if let TokenKind::Literal(Lit { kind: LitKind::Str, symbol, suffix: None }) = kind { - return Ok(*symbol); + ParseNtResult::Tt(TokenTree::Token( + Token { kind: TokenKind::Ident(symbol, is_raw), .. }, + _, + )) => { + if let IdentIsRaw::Yes = is_raw { + return Err(dcx.struct_span_err(span_err, RAW_IDENT_ERR)); } + return Ok(*symbol); } - - if let ParseNtResult::Nt(nt) = pnr - && let Nonterminal::NtLiteral(expr) = &**nt - && let ExprKind::Lit(Lit { kind: LitKind::Str, symbol, suffix: None }) = &expr.kind + ParseNtResult::Tt(TokenTree::Token( + Token { + kind: TokenKind::Literal(Lit { kind: LitKind::Str, symbol, suffix: None }), + .. + }, + _, + )) => { + return Ok(*symbol); + } + ParseNtResult::Nt(nt) + if let Nonterminal::NtLiteral(expr) = &**nt + && let ExprKind::Lit(Lit { kind: LitKind::Str, symbol, suffix: None }) = + &expr.kind => { return Ok(*symbol); } + _ => Err(dcx + .struct_err( + "metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`", + ) + .with_note("currently only string literals are supported") + .with_span(span_err)), } - Err(dcx - .struct_err("metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`") - .with_note("currently only string literals are supported") - .with_span(ident.span)) } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 1db3774222a..9b5ed3b0876 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -312,6 +312,7 @@ declare_features! ( (unstable, prfchw_target_feature, "1.78.0", Some(44839)), (unstable, riscv_target_feature, "1.45.0", Some(44839)), (unstable, rtm_target_feature, "1.35.0", Some(44839)), + (unstable, s390x_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)), (unstable, sse4a_target_feature, "1.27.0", Some(44839)), (unstable, tbm_target_feature, "1.27.0", Some(44839)), (unstable, wasm_target_feature, "1.30.0", Some(44839)), @@ -339,8 +340,8 @@ declare_features! ( (unstable, abi_riscv_interrupt, "1.73.0", Some(111889)), /// Allows `extern "x86-interrupt" fn()`. (unstable, abi_x86_interrupt, "1.17.0", Some(40180)), - /// Allows additional const parameter types, such as `&'static str` or user defined types - (incomplete, adt_const_params, "1.56.0", Some(95174)), + /// Allows additional const parameter types, such as `[u8; 10]` or user defined types + (unstable, adt_const_params, "1.56.0", Some(95174)), /// Allows defining an `#[alloc_error_handler]`. (unstable, alloc_error_handler, "1.29.0", Some(51540)), /// Allows trait methods with arbitrary self types. @@ -630,6 +631,9 @@ declare_features! ( (unstable, unsafe_attributes, "1.80.0", Some(123757)), /// Allows unsafe on extern declarations and safety qualifiers over internal items. (unstable, unsafe_extern_blocks, "1.80.0", Some(123743)), + /// Allows const generic parameters to be defined with types that + /// are not `Sized`, e.g. `fn foo<const N: [u8]>() {`. + (incomplete, unsized_const_params, "CURRENT_RUSTC_VERSION", Some(95174)), /// Allows unsized fn parameters. (internal, unsized_fn_params, "1.49.0", Some(48055)), /// Allows unsized rvalues at arguments and parameters. diff --git a/compiler/rustc_fluent_macro/src/fluent.rs b/compiler/rustc_fluent_macro/src/fluent.rs index 214b6587af3..68fdabd3529 100644 --- a/compiler/rustc_fluent_macro/src/fluent.rs +++ b/compiler/rustc_fluent_macro/src/fluent.rs @@ -253,7 +253,7 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok for Attribute { id: Identifier { name: attr_name }, .. } in attributes { let snake_name = Ident::new( - &format!("{}{}", &crate_prefix, &attr_name.replace('-', "_")), + &format!("{crate_prefix}{}", attr_name.replace('-', "_")), resource_str.span(), ); if !previous_attrs.insert(snake_name.clone()) { diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 30c0e40206a..58cc0f62111 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -358,6 +358,7 @@ language_item_table! { PointerLike, sym::pointer_like, pointer_like, Target::Trait, GenericRequirement::Exact(0); ConstParamTy, sym::const_param_ty, const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); + UnsizedConstParamTy, sym::unsized_const_param_ty, unsized_const_param_ty_trait, Target::Trait, GenericRequirement::Exact(0); Poll, sym::Poll, poll, Target::Enum, GenericRequirement::None; PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None; diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index f08a0f8c8fc..cc404daa51f 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -99,6 +99,10 @@ hir_analysis_const_param_ty_impl_on_non_adt = the trait `ConstParamTy` may not be implemented for this type .label = type is not a structure or enumeration +hir_analysis_const_param_ty_impl_on_unsized = + the trait `ConstParamTy` may not be implemented for this type + .label = type is not `Sized` + hir_analysis_const_specialize = cannot specialize on const impl with non-const impl hir_analysis_copy_impl_on_non_adt = diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index dbc265ad3ff..27db5418165 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -26,7 +26,7 @@ use rustc_middle::ty::{ use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS}; use rustc_target::abi::FieldIdx; use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_type_ir::fold::TypeFoldable; 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 6c53625b590..c99f13468e2 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -21,7 +21,7 @@ use rustc_middle::ty::{ use rustc_middle::ty::{GenericParamDefKind, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::Span; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::regions::InferCtxtRegionExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs index ce921f64481..e4d4b7df24e 100644 --- a/compiler/rustc_hir_analysis/src/check/entry.rs +++ b/compiler/rustc_hir_analysis/src/check/entry.rs @@ -7,7 +7,7 @@ use rustc_session::config::EntryFnType; use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID}; use rustc_span::{symbol::sym, Span}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; use std::ops::Not; diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 24aeb024461..4c230ad84de 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -82,7 +82,6 @@ use rustc_errors::{pluralize, struct_span_code_err, Diag}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_index::bit_set::BitSet; -use rustc_infer::error_reporting::infer::ObligationCauseExt as _; use rustc_infer::infer::outlives::env::OutlivesEnvironment; use rustc_infer::infer::{self, TyCtxtInferExt as _}; use rustc_infer::traits::ObligationCause; @@ -96,10 +95,9 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{def_id::CRATE_DEF_ID, BytePos, Span, Symbol, DUMMY_SP}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::error_reporting::traits::suggestions::{ - ReturnsVisitor, TypeErrCtxtExt as _, -}; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::infer::ObligationCauseExt as _; +use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::ObligationCtxt; use crate::errors; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 71a7b0b1638..0316ef69bf8 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -29,7 +29,7 @@ use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::regions::InferCtxtRegionExt; use rustc_trait_selection::traits::misc::{ type_allowed_to_implement_const_param_ty, ConstParamTyImplementationError, @@ -922,10 +922,8 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), } => { let ty = tcx.type_of(param.def_id).instantiate_identity(); - if tcx.features().adt_const_params { + if tcx.features().unsized_const_params { enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| { - let trait_def_id = - tcx.require_lang_item(LangItem::ConstParamTy, Some(hir_ty.span)); wfcx.register_bound( ObligationCause::new( hir_ty.span, @@ -934,7 +932,21 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), ), wfcx.param_env, ty, - trait_def_id, + tcx.require_lang_item(LangItem::UnsizedConstParamTy, Some(hir_ty.span)), + ); + Ok(()) + }) + } else if tcx.features().adt_const_params { + enter_wf_checking_ctxt(tcx, hir_ty.span, param.def_id, |wfcx| { + wfcx.register_bound( + ObligationCause::new( + hir_ty.span, + param.def_id, + ObligationCauseCode::ConstParam(ty), + ), + wfcx.param_env, + ty, + tcx.require_lang_item(LangItem::ConstParamTy, Some(hir_ty.span)), ); Ok(()) }) @@ -958,14 +970,29 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), diag.note("the only supported types are integers, `bool` and `char`"); let cause = ObligationCause::misc(hir_ty.span, param.def_id); + let adt_const_params_feature_string = + " more complex and user defined types".to_string(); let may_suggest_feature = match type_allowed_to_implement_const_param_ty( tcx, tcx.param_env(param.def_id), ty, + LangItem::ConstParamTy, cause, ) { // Can never implement `ConstParamTy`, don't suggest anything. - Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => false, + Err( + ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed + | ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(..), + ) => None, + Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired) => { + Some(vec![ + (adt_const_params_feature_string, sym::adt_const_params), + ( + " references to implement the `ConstParamTy` trait".into(), + sym::unsized_const_params, + ), + ]) + } // May be able to implement `ConstParamTy`. Only emit the feature help // if the type is local, since the user may be able to fix the local type. Err(ConstParamTyImplementationError::InfrigingFields(..)) => { @@ -985,20 +1012,16 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(), } } - ty_is_local(ty) + ty_is_local(ty).then_some(vec![( + adt_const_params_feature_string, + sym::adt_const_params, + )]) } // Implments `ConstParamTy`, suggest adding the feature to enable. - Ok(..) => true, + Ok(..) => Some(vec![(adt_const_params_feature_string, sym::adt_const_params)]), }; - if may_suggest_feature { - tcx.disabled_nightly_features( - &mut diag, - Some(param.hir_id), - [( - " more complex and user defined types".to_string(), - sym::adt_const_params, - )], - ); + if let Some(features) = may_suggest_feature { + tcx.disabled_nightly_features(&mut diag, Some(param.hir_id), features); } Err(diag.emit()) diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 2ecb170ec89..b35ee270fef 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -17,7 +17,7 @@ use rustc_middle::ty::adjustment::CoerceUnsizedInfo; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::{Span, DUMMY_SP}; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::misc::{ type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy, ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason, @@ -36,9 +36,13 @@ pub(super) fn check_trait<'tcx>( let checker = Checker { tcx, trait_def_id, impl_def_id, impl_header }; let mut res = checker.check(lang_items.drop_trait(), visit_implementation_of_drop); res = res.and(checker.check(lang_items.copy_trait(), visit_implementation_of_copy)); - res = res.and( - checker.check(lang_items.const_param_ty_trait(), visit_implementation_of_const_param_ty), - ); + res = res.and(checker.check(lang_items.const_param_ty_trait(), |checker| { + visit_implementation_of_const_param_ty(checker, LangItem::ConstParamTy) + })); + res = res.and(checker.check(lang_items.unsized_const_param_ty_trait(), |checker| { + visit_implementation_of_const_param_ty(checker, LangItem::UnsizedConstParamTy) + })); + res = res.and( checker.check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized), ); @@ -103,7 +107,13 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran Ok(()) => Ok(()), Err(CopyImplementationError::InfringingFields(fields)) => { let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; - Err(infringing_fields_error(tcx, fields, LangItem::Copy, impl_did, span)) + Err(infringing_fields_error( + tcx, + fields.into_iter().map(|(field, ty, reason)| (tcx.def_span(field.did), ty, reason)), + LangItem::Copy, + impl_did, + span, + )) } Err(CopyImplementationError::NotAnAdt) => { let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; @@ -116,7 +126,12 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran } } -fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { +fn visit_implementation_of_const_param_ty( + checker: &Checker<'_>, + kind: LangItem, +) -> Result<(), ErrorGuaranteed> { + assert!(matches!(kind, LangItem::ConstParamTy | LangItem::UnsizedConstParamTy)); + let tcx = checker.tcx; let header = checker.impl_header; let impl_did = checker.impl_def_id; @@ -125,21 +140,41 @@ fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), E let param_env = tcx.param_env(impl_did); - if let ty::ImplPolarity::Negative = header.polarity { + if let ty::ImplPolarity::Negative | ty::ImplPolarity::Reservation = header.polarity { return Ok(()); } let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did); - match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) { + match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, kind, cause) { Ok(()) => Ok(()), Err(ConstParamTyImplementationError::InfrigingFields(fields)) => { let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; - Err(infringing_fields_error(tcx, fields, LangItem::ConstParamTy, impl_did, span)) + Err(infringing_fields_error( + tcx, + fields.into_iter().map(|(field, ty, reason)| (tcx.def_span(field.did), ty, reason)), + LangItem::ConstParamTy, + impl_did, + span, + )) } Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => { let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span })) } + Err(ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy(infringing_tys)) => { + let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; + Err(infringing_fields_error( + tcx, + infringing_tys.into_iter().map(|(ty, reason)| (span, ty, reason)), + LangItem::ConstParamTy, + impl_did, + span, + )) + } + Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired) => { + let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span; + Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnUnsized { span })) + } } } @@ -501,9 +536,9 @@ pub fn coerce_unsized_info<'tcx>( Ok(CoerceUnsizedInfo { custom_kind: kind }) } -fn infringing_fields_error( - tcx: TyCtxt<'_>, - fields: Vec<(&ty::FieldDef, Ty<'_>, InfringingFieldsReason<'_>)>, +fn infringing_fields_error<'tcx>( + tcx: TyCtxt<'tcx>, + infringing_tys: impl Iterator<Item = (Span, Ty<'tcx>, InfringingFieldsReason<'tcx>)>, lang_item: LangItem, impl_did: LocalDefId, impl_span: Span, @@ -521,13 +556,13 @@ fn infringing_fields_error( let mut label_spans = Vec::new(); - for (field, ty, reason) in fields { + for (span, ty, reason) in infringing_tys { // Only report an error once per type. if !seen_tys.insert(ty) { continue; } - label_spans.push(tcx.def_span(field.did)); + label_spans.push(span); match reason { InfringingFieldsReason::Fulfill(fulfillment_errors) => { diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 16f72f38d60..f2804ce31fa 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -517,9 +517,10 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for UncoveredTyParamCollector<'_, 'tcx> { if !ty.has_type_flags(ty::TypeFlags::HAS_TY_INFER) { return; } - let Some(origin) = self.infcx.type_var_origin(ty) else { + let ty::Infer(ty::TyVar(vid)) = *ty.kind() else { return ty.super_visit_with(self); }; + let origin = self.infcx.type_var_origin(vid); if let Some(def_id) = origin.param_def_id { self.uncovered_params.insert(def_id); } @@ -546,9 +547,10 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for TyVarReplacer<'cx, 'tcx> { if !ty.has_type_flags(ty::TypeFlags::HAS_TY_INFER) { return ty; } - let Some(origin) = self.infcx.type_var_origin(ty) else { + let ty::Infer(ty::TyVar(vid)) = *ty.kind() else { return ty.super_fold_with(self); }; + let origin = self.infcx.type_var_origin(vid); if let Some(def_id) = origin.param_def_id { // The generics of an `impl` don't have a parent, we can index directly. let index = self.generics.param_def_id_to_index[&def_id]; diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 5e23d473274..565351268c9 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -84,6 +84,7 @@ pub fn provide(providers: &mut Providers) { coroutine_kind, coroutine_for_closure, is_type_alias_impl_trait, + rendered_precise_capturing_args, ..*providers }; } @@ -1882,3 +1883,23 @@ fn is_type_alias_impl_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool _ => bug!("tried getting opaque_ty_origin for non-opaque: {:?}", def_id), } } + +fn rendered_precise_capturing_args<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: LocalDefId, +) -> Option<&'tcx [Symbol]> { + if let Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) = + tcx.opt_rpitit_info(def_id.to_def_id()) + { + return tcx.rendered_precise_capturing_args(opaque_def_id); + } + + tcx.hir_node_by_def_id(def_id).expect_item().expect_opaque_ty().bounds.iter().find_map( + |bound| match bound { + hir::GenericBound::Use(args, ..) => { + Some(&*tcx.arena.alloc_from_iter(args.iter().map(|arg| arg.name()))) + } + _ => None, + }, + ) +} 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 7930f54038d..349dc9ad00e 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -2094,11 +2094,7 @@ pub fn deny_non_region_late_bound( format!("late-bound {what} parameter not allowed on {where_}"), ); - let guar = if tcx.features().non_lifetime_binders && first { - diag.emit() - } else { - diag.delay_as_bug() - }; + let guar = diag.emit_unless(!tcx.features().non_lifetime_binders || !first); first = false; *arg = ResolvedArg::Error(guar); diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 2eca64c27d0..c83788928a9 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -279,6 +279,14 @@ pub struct CopyImplOnNonAdt { } #[derive(Diagnostic)] +#[diag(hir_analysis_const_param_ty_impl_on_unsized)] +pub struct ConstParamTyImplOnUnsized { + #[primary_span] + #[label] + pub span: Span, +} + +#[derive(Diagnostic)] #[diag(hir_analysis_const_param_ty_impl_on_non_adt)] pub struct ConstParamTyImplOnNonAdt { #[primary_span] diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 6426ad9dc18..10be69a9fbb 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -651,7 +651,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { self.path_segment.hir_id, num_params_to_take, ); - debug!("suggested_args: {:?}", &suggested_args); + debug!("suggested_args: {suggested_args:?}"); match self.angle_brackets { AngleBrackets::Missing => { diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 5b8b6e98125..2e5f99bb78b 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -78,7 +78,7 @@ use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{GenericArg, GenericArgs, GenericArgsRef}; use rustc_span::{ErrorGuaranteed, Span}; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, translate_args_with_cause, wf, ObligationCtxt}; diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index af08f50f655..d953736c28c 100644 --- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -249,7 +249,7 @@ fn check_explicit_predicates<'tcx>( let explicit_predicates = explicit_map.explicit_predicates_of(tcx, def_id); for (outlives_predicate, &span) in explicit_predicates.as_ref().skip_binder() { - debug!("outlives_predicate = {:?}", &outlives_predicate); + debug!("outlives_predicate = {outlives_predicate:?}"); // Careful: If we are inferring the effects of a `dyn Trait<..>` // type, then when we look up the predicates for `Trait`, @@ -289,12 +289,12 @@ fn check_explicit_predicates<'tcx>( && let GenericArgKind::Type(ty) = outlives_predicate.0.unpack() && ty.walk().any(|arg| arg == self_ty.into()) { - debug!("skipping self ty = {:?}", &ty); + debug!("skipping self ty = {ty:?}"); continue; } let predicate = explicit_predicates.rebind(*outlives_predicate).instantiate(tcx, args); - debug!("predicate = {:?}", &predicate); + debug!("predicate = {predicate:?}"); insert_outlives_predicate(tcx, predicate.0, predicate.1, span, required_predicates); } } diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 341d533492d..65229722771 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -495,6 +495,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { err.span_label(self.span, "invalid cast"); } + fcx.suggest_no_capture_closure(&mut err, self.cast_ty, self.expr_ty); self.try_suggest_collection_to_bool(fcx, &mut err); err.emit(); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 08de871f6fa..79854976bdd 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -18,7 +18,6 @@ use rustc_span::def_id::LocalDefId; use rustc_span::Span; use rustc_target::spec::abi::Abi; use rustc_trait_selection::error_reporting::traits::ArgKind; -use rustc_trait_selection::error_reporting::traits::InferCtxtExt as _; use rustc_trait_selection::traits; use rustc_type_ir::ClosureKind; use std::iter; @@ -734,13 +733,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|ty| ArgKind::from_expected_ty(*ty, None)) .collect(); let (closure_span, closure_arg_span, found_args) = - match self.get_fn_like_arguments(expr_map_node) { + match self.err_ctxt().get_fn_like_arguments(expr_map_node) { Some((sp, arg_sp, args)) => (Some(sp), arg_sp, args), None => (None, None, Vec::new()), }; let expected_span = expected_sig.cause_span.unwrap_or_else(|| self.tcx.def_span(expr_def_id)); let guar = self + .err_ctxt() .report_arg_count_mismatch( expected_span, closure_span, diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 5e2a68e1f02..1bfe9734217 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -58,8 +58,6 @@ use rustc_session::parse::feature_err; use rustc_span::symbol::sym; use rustc_span::{BytePos, DesugaringKind, Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtSelectionErrExt as _; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index d708269f1f5..0d002c52fbb 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -53,8 +53,6 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use rustc_target::abi::{FieldIdx, FIRST_VARIANT}; -use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt as _; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{self, ObligationCauseCode}; @@ -2574,7 +2572,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base: &'tcx hir::Expr<'tcx>, ty: Ty<'tcx>, ) { - let Some(output_ty) = self.get_impl_future_output_ty(ty) else { + let Some(output_ty) = self.err_ctxt().get_impl_future_output_ty(ty) else { err.span_label(field_ident.span, "unknown field"); return; }; diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 3cecbfd4275..9f3aeacd2c5 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -175,7 +175,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { }; debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback); - let span = self.infcx.type_var_origin(ty).map(|origin| origin.span).unwrap_or(DUMMY_SP); + let span = ty.ty_vid().map_or(DUMMY_SP, |vid| self.infcx.type_var_origin(vid).span); self.demand_eqtype(span, ty, fallback); self.fallback_has_occurred.set(true); true diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index cc2c1a302f5..87e8afe6dd1 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -19,7 +19,6 @@ use rustc_hir_analysis::hir_ty_lowering::{ GenericPathSegment, HirTyLowerer, IsMethodCall, RegionInferReason, }; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; -use rustc_infer::infer::need_type_info::TypeAnnotationNeeded; use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; @@ -37,7 +36,7 @@ use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym}; use rustc_span::Span; use rustc_target::abi::FieldIdx; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::traits::{ self, NormalizeExt, ObligationCauseCode, ObligationCtxt, StructurallyNormalizeExt, }; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs index f7abba35706..8e35efa53ae 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/adjust_fulfillment_errors.rs @@ -338,8 +338,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for FindAmbiguousParameter<'_, 'tcx> { type Result = ControlFlow<ty::GenericArg<'tcx>>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result { - if let Some(origin) = self.0.type_var_origin(ty) - && let Some(def_id) = origin.param_def_id + if let ty::Infer(ty::TyVar(vid)) = *ty.kind() + && let Some(def_id) = self.0.type_var_origin(vid).param_def_id && let generics = self.0.tcx.generics_of(self.1) && let Some(index) = generics.param_def_id_to_index(self.0.tcx, def_id) && let Some(arg) = diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 2b4025ca808..7c96a991bed 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -29,7 +29,6 @@ use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_index::IndexVec; -use rustc_infer::error_reporting::infer::{FailureCode, ObligationCauseExt}; use rustc_infer::infer::TypeTrace; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::ty::adjustment::AllowTwoPhase; @@ -39,6 +38,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::Session; use rustc_span::symbol::{kw, Ident}; use rustc_span::{sym, BytePos, Span, DUMMY_SP}; +use rustc_trait_selection::error_reporting::infer::{FailureCode, ObligationCauseExt}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 3fe87c03e74..39d73dae015 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -15,13 +15,13 @@ use hir::def_id::CRATE_DEF_ID; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, RegionInferReason}; -use rustc_infer::error_reporting::infer::sub_relations::SubRelations; -use rustc_infer::error_reporting::infer::TypeErrCtxt; use rustc_infer::infer; use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::{self, sym, Span, DUMMY_SP}; +use rustc_trait_selection::error_reporting::infer::sub_relations::SubRelations; +use rustc_trait_selection::error_reporting::TypeErrCtxt; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt}; use std::cell::{Cell, RefCell}; @@ -162,9 +162,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Creates an `TypeErrCtxt` with a reference to the in-progress /// `TypeckResults` which is used for diagnostics. - /// Use [`InferCtxt::err_ctxt`] to start one without a `TypeckResults`. + /// Use [`InferCtxtErrorExt::err_ctxt`] to start one without a `TypeckResults`. /// - /// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt + /// [`InferCtxtErrorExt::err_ctxt`]: rustc_trait_selection::error_reporting::InferCtxtErrorExt::err_ctxt pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> { let mut sub_relations = SubRelations::default(); sub_relations.add_constraints( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index b3b4c5a56fb..fe7495deb2b 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -32,8 +32,8 @@ use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; use rustc_span::{Span, Symbol}; -use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt; use rustc_trait_selection::error_reporting::traits::DefIdOrName; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -1107,12 +1107,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .tcx .instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars)); let ty = match self.tcx.asyncness(fn_id) { - ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| { - span_bug!( - fn_decl.output.span(), - "failed to get output type of async function" - ) - }), + ty::Asyncness::Yes => { + self.err_ctxt().get_impl_future_output_ty(ty).unwrap_or_else(|| { + span_bug!( + fn_decl.output.span(), + "failed to get output type of async function" + ) + }) + } ty::Asyncness::No => ty, }; let ty = self.normalize(expr.span, ty); diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index e817685e41c..9cb6124ab21 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -12,7 +12,6 @@ use rustc_hir::HirId; use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; -use rustc_infer::infer::need_type_info::TypeAnnotationNeeded; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_infer::traits::ObligationCauseCode; @@ -34,6 +33,7 @@ use rustc_span::edit_distance::{ }; use rustc_span::symbol::sym; use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP}; +use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 1cc7cf67ee3..da3ac2fea98 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -36,7 +36,6 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{edit_distance, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span}; use rustc_span::{Symbol, DUMMY_SP}; use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote; -use rustc_trait_selection::error_reporting::traits::on_unimplemented::TypeErrCtxtExt as _; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ @@ -1265,9 +1264,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ( match parent_pred { - None => format!("`{}`", &p), + None => format!("`{p}`"), Some(parent_pred) => match format_pred(*parent_pred) { - None => format!("`{}`", &p), + None => format!("`{p}`"), Some((parent_p, _)) => { if !suggested && !suggested_bounds.contains(pred) @@ -3276,7 +3275,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, return_type: Option<Ty<'tcx>>, ) { - let output_ty = match self.get_impl_future_output_ty(ty) { + let output_ty = match self.err_ctxt().get_impl_future_output_ty(ty) { Some(output_ty) => self.resolve_vars_if_possible(output_ty), _ => return, }; diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index d59b8276d3a..7b5845388d4 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -18,7 +18,6 @@ use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; -use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt as _; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{FulfillmentError, ObligationCtxt}; use rustc_type_ir::TyKind::*; diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 611854ce2af..4ef7f37b309 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -8,7 +8,6 @@ use rustc_errors::{ErrorGuaranteed, StashKey}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirId; -use rustc_infer::infer::need_type_info::TypeAnnotationNeeded; use rustc_middle::span_bug; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; @@ -18,7 +17,7 @@ use rustc_middle::ty::TypeSuperFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::solve; use std::mem; diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index c279195a7e9..e51734ff7a7 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -1,397 +1,5 @@ -infer_actual_impl_expl_but_actually_implemented_for_ty = ...but `{$trait_path}` is actually implemented for the type `{$ty}`{$has_lifetime -> - [true] , for some specific lifetime `'{$lifetime}` - *[false] {""} -} -infer_actual_impl_expl_but_actually_implements_trait = ...but it actually implements `{$trait_path}`{$has_lifetime -> - [true] , for some specific lifetime `'{$lifetime}` - *[false] {""} -} -infer_actual_impl_expl_but_actually_ty_implements = ...but `{$ty}` actually implements `{$trait_path}`{$has_lifetime -> - [true] , for some specific lifetime `'{$lifetime}` - *[false] {""} -} - -infer_actual_impl_expl_expected_other_any = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`... -infer_actual_impl_expl_expected_other_nothing = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$ty_or_sig}` must implement `{$trait_path}` - -infer_actual_impl_expl_expected_other_some = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`... -infer_actual_impl_expl_expected_other_two = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... -infer_actual_impl_expl_expected_passive_any = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any lifetime `'{$lifetime_1}`... -infer_actual_impl_expl_expected_passive_nothing = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}` -infer_actual_impl_expl_expected_passive_some = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for some specific lifetime `'{$lifetime_1}`... -infer_actual_impl_expl_expected_passive_two = {$leading_ellipsis -> - [true] ... - *[false] {""} -}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... -infer_actual_impl_expl_expected_signature_any = {$leading_ellipsis -> - [true] ... - *[false] {""} -}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`... -infer_actual_impl_expl_expected_signature_nothing = {$leading_ellipsis -> - [true] ... - *[false] {""} -}closure with signature `{$ty_or_sig}` must implement `{$trait_path}` -infer_actual_impl_expl_expected_signature_some = {$leading_ellipsis -> - [true] ... - *[false] {""} -}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`... -infer_actual_impl_expl_expected_signature_two = {$leading_ellipsis -> - [true] ... - *[false] {""} -}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... -infer_ascribe_user_type_prove_predicate = ...so that the where clause holds - -infer_await_both_futures = consider `await`ing on both `Future`s -infer_await_future = consider `await`ing on the `Future` -infer_await_note = calling an async function returns a future - -infer_but_calling_introduces = {$has_param_name -> - [true] `{$param_name}` - *[false] `fn` parameter -} has {$lifetime_kind -> - [true] lifetime `{$lifetime}` - *[false] an anonymous lifetime `'_` -} but calling `{$assoc_item}` introduces an implicit `'static` lifetime requirement - .label1 = {$has_lifetime -> - [true] lifetime `{$lifetime}` - *[false] an anonymous lifetime `'_` - } - .label2 = ...is used and required to live as long as `'static` here because of an implicit lifetime bound on the {$has_impl_path -> - [true] `impl` of `{$impl_path}` - *[false] inherent `impl` - } - -infer_but_needs_to_satisfy = {$has_param_name -> - [true] `{$param_name}` - *[false] `fn` parameter -} has {$has_lifetime -> - [true] lifetime `{$lifetime}` - *[false] an anonymous lifetime `'_` -} but it needs to satisfy a `'static` lifetime requirement - .influencer = this data with {$has_lifetime -> - [true] lifetime `{$lifetime}` - *[false] an anonymous lifetime `'_` - }... - .require = {$spans_empty -> - *[true] ...is used and required to live as long as `'static` here - [false] ...and is required to live as long as `'static` here - } - .used_here = ...is used here... - .introduced_by_bound = `'static` lifetime requirement introduced by this bound - -infer_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait -infer_consider_specifying_length = consider specifying the actual array length -infer_data_flows = ...but data{$label_var1_exists -> - [true] {" "}from `{$label_var1}` - *[false] {""} -} flows{$label_var2_exists -> - [true] {" "}into `{$label_var2}` - *[false] {""} -} here - -infer_data_lifetime_flow = ...but data with one lifetime flows into the other here -infer_data_returned = ...but data{$label_var1_exists -> - [true] {" "}from `{$label_var1}` - *[false] {""} -} is returned here - -infer_declared_different = this parameter and the return type are declared with different lifetimes... -infer_declared_multiple = this type is declared with multiple lifetimes... -infer_does_not_outlive_static_from_impl = ...does not necessarily outlive the static lifetime introduced by the compatible `impl` -infer_dtcs_has_lifetime_req_label = this has an implicit `'static` lifetime requirement -infer_dtcs_has_req_note = the used `impl` has a `'static` requirement -infer_dtcs_introduces_requirement = calling this method introduces the `impl`'s `'static` requirement -infer_dtcs_suggestion = consider relaxing the implicit `'static` requirement - -infer_explicit_lifetime_required_sugg_with_ident = add explicit lifetime `{$named}` to the type of `{$simple_ident}` - -infer_explicit_lifetime_required_sugg_with_param_type = add explicit lifetime `{$named}` to type - -infer_explicit_lifetime_required_with_ident = explicit lifetime required in the type of `{$simple_ident}` - .label = lifetime `{$named}` required - -infer_explicit_lifetime_required_with_param_type = explicit lifetime required in parameter type - .label = lifetime `{$named}` required - -infer_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}` - -infer_fn_uniq_types = different fn items have unique types, even if their signatures are the same -infer_fps_cast = consider casting to a fn pointer -infer_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}` - -infer_fps_items_are_distinct = fn items are distinct from fn pointers -infer_fps_remove_ref = consider removing the reference -infer_fps_use_ref = consider using a reference -infer_fulfill_req_lifetime = the type `{$ty}` does not fulfill the required lifetime - -infer_full_type_written = the full type name has been written to '{$path}' - -infer_implicit_static_lifetime_note = this has an implicit `'static` lifetime requirement -infer_implicit_static_lifetime_suggestion = consider relaxing the implicit `'static` requirement -infer_label_bad = {$bad_kind -> - *[other] cannot infer type - [more_info] cannot infer {$prefix_kind -> - *[type] type for {$prefix} - [const_with_param] the value of const parameter - [const] the value of the constant - } `{$name}`{$has_parent -> - [true] {" "}declared on the {$parent_prefix} `{$parent_name}` - *[false] {""} - } -} - -infer_lf_bound_not_satisfied = lifetime bound not satisfied -infer_lifetime_mismatch = lifetime mismatch - -infer_lifetime_param_suggestion = consider {$is_reuse -> - [true] reusing - *[false] introducing -} a named lifetime parameter{$is_impl -> - [true] {" "}and update trait if needed - *[false] {""} -} -infer_lifetime_param_suggestion_elided = each elided lifetime in input position becomes a distinct lifetime - -infer_meant_byte_literal = if you meant to write a byte literal, prefix with `b` -infer_meant_char_literal = if you meant to write a `char` literal, use single quotes -infer_meant_str_literal = if you meant to write a string literal, use double quotes -infer_mismatched_static_lifetime = incompatible lifetime on type -infer_more_targeted = {$has_param_name -> - [true] `{$param_name}` - *[false] `fn` parameter -} has {$has_lifetime -> - [true] lifetime `{$lifetime}` - *[false] an anonymous lifetime `'_` -} but calling `{$ident}` introduces an implicit `'static` lifetime requirement - -infer_msl_introduces_static = introduces a `'static` lifetime requirement -infer_msl_unmet_req = because this has an unmet lifetime requirement - -infer_nothing = {""} - -infer_oc_cant_coerce = cannot coerce intrinsics to function pointers -infer_oc_closure_selfref = closure/coroutine type that references itself -infer_oc_const_compat = const not compatible with trait -infer_oc_fn_lang_correct_type = {$lang_item_name -> - [panic_impl] `#[panic_handler]` - *[lang_item_name] lang item `{$lang_item_name}` - } function has wrong type -infer_oc_fn_main_correct_type = `main` function has wrong type -infer_oc_fn_start_correct_type = `#[start]` function has wrong type -infer_oc_generic = mismatched types - -infer_oc_if_else_different = `if` and `else` have incompatible types -infer_oc_intrinsic_correct_type = intrinsic has wrong type -infer_oc_match_compat = `match` arms have incompatible types -infer_oc_method_compat = method not compatible with trait -infer_oc_method_correct_type = mismatched `self` parameter type -infer_oc_no_diverge = `else` clause of `let...else` does not diverge -infer_oc_no_else = `if` may be missing an `else` clause -infer_oc_try_compat = `?` operator has incompatible types -infer_oc_type_compat = type not compatible with trait -infer_opaque_captures_lifetime = hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds - .label = opaque type defined here - infer_opaque_hidden_type = opaque type's hidden type cannot be another opaque type from the same scope .label = one of the two opaque types used here has to be outside its defining scope .opaque_type = opaque type whose hidden type is being assigned .hidden_type = opaque type being used as hidden type - -infer_outlives_bound = lifetime of the source pointer does not outlive lifetime bound of the object type -infer_outlives_content = lifetime of reference outlives lifetime of borrowed content... - -infer_precise_capturing_existing = add `{$new_lifetime}` to the `use<...>` bound to explicitly capture it -infer_precise_capturing_new = add a `use<...>` bound to explicitly capture `{$new_lifetime}` - -infer_precise_capturing_new_but_apit = add a `use<...>` bound to explicitly capture `{$new_lifetime}` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate - -infer_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here... -infer_prlf_defined_without_sub = the lifetime defined here... -infer_prlf_known_limitation = this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information) - -infer_prlf_must_outlive_with_sup = ...must outlive the lifetime `{$sup_symbol}` defined here -infer_prlf_must_outlive_without_sup = ...must outlive the lifetime defined here -infer_reborrow = ...so that reference does not outlive borrowed content -infer_ref_longer_than_data = in type `{$ty}`, reference has a longer lifetime than the data it references - -infer_reference_outlives_referent = ...so that the reference type `{$name}` does not outlive the data it points at -infer_region_explanation = {$pref_kind -> - *[should_not_happen] [{$pref_kind}] - [ref_valid_for] ...the reference is valid for - [content_valid_for] ...but the borrowed content is only valid for - [type_obj_valid_for] object type is valid for - [source_pointer_valid_for] source pointer is only valid for - [type_satisfy] type must satisfy - [type_outlive] type must outlive - [lf_param_instantiated_with] lifetime parameter instantiated with - [lf_param_must_outlive] but lifetime parameter must outlive - [lf_instantiated_with] lifetime instantiated with - [lf_must_outlive] but lifetime must outlive - [pointer_valid_for] the pointer is valid for - [data_valid_for] but the referenced data is only valid for - [empty] {""} -}{$pref_kind -> - [empty] {""} - *[other] {" "} -}{$desc_kind -> - *[should_not_happen] [{$desc_kind}] - [restatic] the static lifetime - [revar] lifetime {$desc_arg} - [as_defined] the lifetime `{$desc_arg}` as defined here - [as_defined_anon] the anonymous lifetime as defined here - [defined_here] the anonymous lifetime defined here - [defined_here_reg] the lifetime `{$desc_arg}` as defined here -}{$suff_kind -> - *[should_not_happen] [{$suff_kind}] - [empty]{""} - [continues] ... - [req_by_binding] {" "}as required by this binding -} - -infer_relate_object_bound = ...so that it can be closed over into an object -infer_relate_param_bound = ...so that the type `{$name}` will meet its required lifetime bounds{$continues -> - [true] ... - *[false] {""} -} -infer_relate_param_bound_2 = ...that is required by this bound -infer_relate_region_param_bound = ...so that the declared lifetime parameter bounds are satisfied -infer_ril_because_of = because of this returned expression -infer_ril_introduced_by = requirement introduced by this return type -infer_ril_introduced_here = `'static` requirement introduced here -infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type - -infer_source_kind_closure_return = - try giving this closure an explicit return type - -# coroutine_kind may need to be translated -infer_source_kind_fully_qualified = - try using a fully qualified path to specify the expected types - -infer_source_kind_subdiag_generic_label = - cannot infer {$is_type -> - [true] type - *[false] the value - } of the {$is_type -> - [true] type - *[false] const - } {$parent_exists -> - [true] parameter `{$param_name}` declared on the {$parent_prefix} `{$parent_name}` - *[false] parameter {$param_name} - } - -infer_source_kind_subdiag_generic_suggestion = - consider specifying the generic {$arg_count -> - [one] argument - *[other] arguments - } - -infer_source_kind_subdiag_let = {$kind -> - [with_pattern] consider giving `{$name}` an explicit type - [closure] consider giving this closure parameter an explicit type - *[other] consider giving this pattern a type -}{$x_kind -> - [has_name] , where the {$prefix_kind -> - *[type] type for {$prefix} - [const_with_param] value of const parameter - [const] value of the constant - } `{$arg_name}` is specified - [underscore] , where the placeholders `_` are specified - *[empty] {""} -} - -infer_srs_add = consider returning the local binding `{$ident}` -infer_srs_add_one = consider returning one of these bindings - -infer_srs_remove = consider removing this semicolon -infer_srs_remove_and_box = consider removing this semicolon and boxing the expressions -infer_stp_wrap_many = try wrapping the pattern in a variant of `{$path}` - -infer_stp_wrap_one = try wrapping the pattern in `{$variant}` -infer_subtype = ...so that the {$requirement -> - [method_compat] method type is compatible with trait - [type_compat] associated type is compatible with trait - [const_compat] const is compatible with trait - [expr_assignable] expression is assignable - [if_else_different] `if` and `else` have incompatible types - [no_else] `if` missing an `else` returns `()` - [fn_main_correct_type] `main` function has the correct type - [fn_start_correct_type] `#[start]` function has the correct type - [fn_lang_correct_type] lang item function has the correct type - [intrinsic_correct_type] intrinsic has the correct type - [method_correct_type] method receiver has the correct type - *[other] types are compatible -} -infer_subtype_2 = ...so that {$requirement -> - [method_compat] method type is compatible with trait - [type_compat] associated type is compatible with trait - [const_compat] const is compatible with trait - [expr_assignable] expression is assignable - [if_else_different] `if` and `else` have incompatible types - [no_else] `if` missing an `else` returns `()` - [fn_main_correct_type] `main` function has the correct type - [fn_start_correct_type] `#[start]` function has the correct type - [fn_lang_correct_type] lang item function has the correct type - [intrinsic_correct_type] intrinsic has the correct type - [method_correct_type] method receiver has the correct type - *[other] types are compatible -} - -infer_suggest_accessing_field = you might have meant to use field `{$name}` whose type is `{$ty}` - -infer_suggest_add_let_for_letchains = consider adding `let` - -infer_tid_consider_borrowing = consider borrowing this type parameter in the trait -infer_tid_param_help = the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` - -infer_tid_rel_help = verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output -infer_trait_impl_diff = `impl` item signature doesn't match `trait` item signature - .found = found `{$found}` - .expected = expected `{$expected}` - .expected_found = expected signature `{$expected}` - {" "}found signature `{$found}` - -infer_trait_placeholder_mismatch = implementation of `{$trait_def_id}` is not general enough - .label_satisfy = doesn't satisfy where-clause - .label_where = due to a where-clause on `{$def_id}`... - .label_dup = implementation of `{$trait_def_id}` is not general enough - -infer_try_cannot_convert = `?` operator cannot convert from `{$found}` to `{$expected}` - -infer_tuple_trailing_comma = use a trailing comma to create a tuple with one element - -infer_type_annotations_needed = {$source_kind -> - [closure] type annotations needed for the closure `{$source_name}` - [normal] type annotations needed for `{$source_name}` - *[other] type annotations needed -} - .label = type must be known at this point - -infer_types_declared_different = these two types are declared with different lifetimes... - -infer_warn_removing_apit_params = you could use a `use<...>` bound to explicitly capture `{$new_lifetime}`, but argument-position `impl Trait`s are not nameable - -infer_where_copy_predicates = copy the `where` clause predicates from the trait - -infer_where_remove = remove the `where` clause diff --git a/compiler/rustc_infer/src/error_reporting/mod.rs b/compiler/rustc_infer/src/error_reporting/mod.rs deleted file mode 100644 index 132485ec661..00000000000 --- a/compiler/rustc_infer/src/error_reporting/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod infer; diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 2ce712e0bff..1a5c0137219 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1,28 +1,5 @@ -use hir::GenericParamKind; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{ - codes::*, Applicability, Diag, DiagMessage, DiagStyledString, EmissionGuarantee, IntoDiagArg, - MultiSpan, SubdiagMessageOp, Subdiagnostic, -}; -use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{walk_ty, Visitor}; -use rustc_hir::FnRetTy; -use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_middle::ty::print::TraitRefPrintOnlyTraitPath; -use rustc_middle::ty::{Binder, FnSig, Region, Ty, TyCtxt}; -use rustc_span::symbol::kw; -use rustc_span::Symbol; -use rustc_span::{symbol::Ident, BytePos, Span}; - -use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlighted; -use crate::error_reporting::infer::ObligationCauseAsDiagArg; -use crate::fluent_generated as fluent; -use crate::infer::need_type_info::UnderspecifiedArgKind; - -use std::path::PathBuf; - -pub mod note_and_explain; +use rustc_macros::Diagnostic; +use rustc_span::Span; #[derive(Diagnostic)] #[diag(infer_opaque_hidden_type)] @@ -35,1599 +12,3 @@ pub struct OpaqueHiddenTypeDiag { #[note(infer_hidden_type)] pub hidden_type: Span, } - -#[derive(Diagnostic)] -#[diag(infer_type_annotations_needed, code = E0282)] -pub struct AnnotationRequired<'a> { - #[primary_span] - pub span: Span, - pub source_kind: &'static str, - pub source_name: &'a str, - #[label] - pub failure_span: Option<Span>, - #[subdiagnostic] - pub bad_label: Option<InferenceBadError<'a>>, - #[subdiagnostic] - pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, - #[subdiagnostic] - pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, - #[note(infer_full_type_written)] - pub was_written: Option<()>, - pub path: PathBuf, -} - -// Copy of `AnnotationRequired` for E0283 -#[derive(Diagnostic)] -#[diag(infer_type_annotations_needed, code = E0283)] -pub struct AmbiguousImpl<'a> { - #[primary_span] - pub span: Span, - pub source_kind: &'static str, - pub source_name: &'a str, - #[label] - pub failure_span: Option<Span>, - #[subdiagnostic] - pub bad_label: Option<InferenceBadError<'a>>, - #[subdiagnostic] - pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, - #[subdiagnostic] - pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, - #[note(infer_full_type_written)] - pub was_written: Option<()>, - pub path: PathBuf, -} - -// Copy of `AnnotationRequired` for E0284 -#[derive(Diagnostic)] -#[diag(infer_type_annotations_needed, code = E0284)] -pub struct AmbiguousReturn<'a> { - #[primary_span] - pub span: Span, - pub source_kind: &'static str, - pub source_name: &'a str, - #[label] - pub failure_span: Option<Span>, - #[subdiagnostic] - pub bad_label: Option<InferenceBadError<'a>>, - #[subdiagnostic] - pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, - #[subdiagnostic] - pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, - #[note(infer_full_type_written)] - pub was_written: Option<()>, - pub path: PathBuf, -} - -// Used when a better one isn't available -#[derive(Subdiagnostic)] -#[label(infer_label_bad)] -pub struct InferenceBadError<'a> { - #[primary_span] - pub span: Span, - pub bad_kind: &'static str, - pub prefix_kind: UnderspecifiedArgKind, - pub has_parent: bool, - pub prefix: &'a str, - pub parent_prefix: &'a str, - pub parent_name: String, - pub name: String, -} - -#[derive(Subdiagnostic)] -pub enum SourceKindSubdiag<'a> { - #[suggestion( - infer_source_kind_subdiag_let, - style = "verbose", - code = ": {type_name}", - applicability = "has-placeholders" - )] - LetLike { - #[primary_span] - span: Span, - name: String, - type_name: String, - kind: &'static str, - x_kind: &'static str, - prefix_kind: UnderspecifiedArgKind, - prefix: &'a str, - arg_name: String, - }, - #[label(infer_source_kind_subdiag_generic_label)] - GenericLabel { - #[primary_span] - span: Span, - is_type: bool, - param_name: String, - parent_exists: bool, - parent_prefix: String, - parent_name: String, - }, - #[suggestion( - infer_source_kind_subdiag_generic_suggestion, - style = "verbose", - code = "::<{args}>", - applicability = "has-placeholders" - )] - GenericSuggestion { - #[primary_span] - span: Span, - arg_count: usize, - args: String, - }, -} - -#[derive(Subdiagnostic)] -pub enum SourceKindMultiSuggestion<'a> { - #[multipart_suggestion( - infer_source_kind_fully_qualified, - style = "verbose", - applicability = "has-placeholders" - )] - FullyQualified { - #[suggestion_part(code = "{def_path}({adjustment}")] - span_lo: Span, - #[suggestion_part(code = "{successor_pos}")] - span_hi: Span, - def_path: String, - adjustment: &'a str, - successor_pos: &'a str, - }, - #[multipart_suggestion( - infer_source_kind_closure_return, - style = "verbose", - applicability = "has-placeholders" - )] - ClosureReturn { - #[suggestion_part(code = "{start_span_code}")] - start_span: Span, - start_span_code: String, - #[suggestion_part(code = " }}")] - end_span: Option<Span>, - }, -} - -impl<'a> SourceKindMultiSuggestion<'a> { - pub fn new_fully_qualified( - span: Span, - def_path: String, - adjustment: &'a str, - successor: (&'a str, BytePos), - ) -> Self { - Self::FullyQualified { - span_lo: span.shrink_to_lo(), - span_hi: span.shrink_to_hi().with_hi(successor.1), - def_path, - adjustment, - successor_pos: successor.0, - } - } - - pub fn new_closure_return( - ty_info: String, - data: &'a FnRetTy<'a>, - should_wrap_expr: Option<Span>, - ) -> Self { - let arrow = match data { - FnRetTy::DefaultReturn(_) => " -> ", - _ => "", - }; - let (start_span, start_span_code, end_span) = match should_wrap_expr { - Some(end_span) => (data.span(), format!("{arrow}{ty_info} {{"), Some(end_span)), - None => (data.span(), format!("{arrow}{ty_info}"), None), - }; - Self::ClosureReturn { start_span, start_span_code, end_span } - } -} - -pub enum RegionOriginNote<'a> { - Plain { - span: Span, - msg: DiagMessage, - }, - WithName { - span: Span, - msg: DiagMessage, - name: &'a str, - continues: bool, - }, - WithRequirement { - span: Span, - requirement: ObligationCauseAsDiagArg<'a>, - expected_found: Option<(DiagStyledString, DiagStyledString)>, - }, -} - -impl Subdiagnostic for RegionOriginNote<'_> { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - let mut label_or_note = |span, msg: DiagMessage| { - let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); - let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count(); - let span_is_primary = diag.span.primary_spans().iter().all(|&sp| sp == span); - if span_is_primary && sub_count == 0 && expanded_sub_count == 0 { - diag.span_label(span, msg); - } else if span_is_primary && expanded_sub_count == 0 { - diag.note(msg); - } else { - diag.span_note(span, msg); - } - }; - match self { - RegionOriginNote::Plain { span, msg } => { - label_or_note(span, msg); - } - RegionOriginNote::WithName { span, msg, name, continues } => { - label_or_note(span, msg); - diag.arg("name", name); - diag.arg("continues", continues); - } - RegionOriginNote::WithRequirement { - span, - requirement, - expected_found: Some((expected, found)), - } => { - label_or_note(span, fluent::infer_subtype); - diag.arg("requirement", requirement); - - diag.note_expected_found(&"", expected, &"", found); - } - RegionOriginNote::WithRequirement { span, requirement, expected_found: None } => { - // FIXME: this really should be handled at some earlier stage. Our - // handling of region checking when type errors are present is - // *terrible*. - label_or_note(span, fluent::infer_subtype_2); - diag.arg("requirement", requirement); - } - }; - } -} - -pub enum LifetimeMismatchLabels { - InRet { - param_span: Span, - ret_span: Span, - span: Span, - label_var1: Option<Ident>, - }, - Normal { - hir_equal: bool, - ty_sup: Span, - ty_sub: Span, - span: Span, - sup: Option<Ident>, - sub: Option<Ident>, - }, -} - -impl Subdiagnostic for LifetimeMismatchLabels { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - match self { - LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { - diag.span_label(param_span, fluent::infer_declared_different); - diag.span_label(ret_span, fluent::infer_nothing); - diag.span_label(span, fluent::infer_data_returned); - diag.arg("label_var1_exists", label_var1.is_some()); - diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default()); - } - LifetimeMismatchLabels::Normal { - hir_equal, - ty_sup, - ty_sub, - span, - sup: label_var1, - sub: label_var2, - } => { - if hir_equal { - diag.span_label(ty_sup, fluent::infer_declared_multiple); - diag.span_label(ty_sub, fluent::infer_nothing); - diag.span_label(span, fluent::infer_data_lifetime_flow); - } else { - diag.span_label(ty_sup, fluent::infer_types_declared_different); - diag.span_label(ty_sub, fluent::infer_nothing); - diag.span_label(span, fluent::infer_data_flows); - diag.arg("label_var1_exists", label_var1.is_some()); - diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default()); - diag.arg("label_var2_exists", label_var2.is_some()); - diag.arg("label_var2", label_var2.map(|x| x.to_string()).unwrap_or_default()); - } - } - } - } -} - -pub struct AddLifetimeParamsSuggestion<'a> { - pub tcx: TyCtxt<'a>, - pub generic_param_scope: LocalDefId, - pub sub: Region<'a>, - pub ty_sup: &'a hir::Ty<'a>, - pub ty_sub: &'a hir::Ty<'a>, - pub add_note: bool, -} - -impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - let mut mk_suggestion = || { - let Some(anon_reg) = self.tcx.is_suitable_region(self.generic_param_scope, self.sub) - else { - return false; - }; - - let node = self.tcx.hir_node_by_def_id(anon_reg.def_id); - let is_impl = matches!(&node, hir::Node::ImplItem(_)); - let (generics, parent_generics) = match node { - hir::Node::Item(&hir::Item { - kind: hir::ItemKind::Fn(_, ref generics, ..), - .. - }) - | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) - | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => ( - generics, - match self.tcx.parent_hir_node(self.tcx.local_def_id_to_hir_id(anon_reg.def_id)) - { - hir::Node::Item(hir::Item { - kind: hir::ItemKind::Trait(_, _, ref generics, ..), - .. - }) - | hir::Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { ref generics, .. }), - .. - }) => Some(generics), - _ => None, - }, - ), - _ => return false, - }; - - let suggestion_param_name = generics - .params - .iter() - .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) - .map(|p| p.name.ident().name) - .find(|i| *i != kw::UnderscoreLifetime); - let introduce_new = suggestion_param_name.is_none(); - - let mut default = "'a".to_string(); - if let Some(parent_generics) = parent_generics { - let used: FxHashSet<_> = parent_generics - .params - .iter() - .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) - .map(|p| p.name.ident().name) - .filter(|i| *i != kw::UnderscoreLifetime) - .map(|l| l.to_string()) - .collect(); - if let Some(lt) = - ('a'..='z').map(|it| format!("'{it}")).find(|it| !used.contains(it)) - { - // We want a lifetime that *isn't* present in the `trait` or `impl` that assoc - // `fn` belongs to. We could suggest reusing one of their lifetimes, but it is - // likely to be an over-constraining lifetime requirement, so we always add a - // lifetime to the `fn`. - default = lt; - } - } - let suggestion_param_name = - suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| default); - - struct ImplicitLifetimeFinder { - suggestions: Vec<(Span, String)>, - suggestion_param_name: String, - } - - impl<'v> Visitor<'v> for ImplicitLifetimeFinder { - fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { - let make_suggestion = |ident: Ident| { - if ident.name == kw::Empty && ident.span.is_empty() { - format!("{}, ", self.suggestion_param_name) - } else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() { - format!("{} ", self.suggestion_param_name) - } else { - self.suggestion_param_name.clone() - } - }; - match ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { - for segment in path.segments { - if let Some(args) = segment.args { - if args.args.iter().all(|arg| { - matches!( - arg, - hir::GenericArg::Lifetime(lifetime) - if lifetime.ident.name == kw::Empty - ) - }) { - self.suggestions.push(( - segment.ident.span.shrink_to_hi(), - format!( - "<{}>", - args.args - .iter() - .map(|_| self.suggestion_param_name.clone()) - .collect::<Vec<_>>() - .join(", ") - ), - )); - } else { - for arg in args.args { - if let hir::GenericArg::Lifetime(lifetime) = arg - && lifetime.is_anonymous() - { - self.suggestions.push(( - lifetime.ident.span, - make_suggestion(lifetime.ident), - )); - } - } - } - } - } - } - hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => { - self.suggestions - .push((lifetime.ident.span, make_suggestion(lifetime.ident))); - } - _ => {} - } - walk_ty(self, ty); - } - } - let mut visitor = ImplicitLifetimeFinder { - suggestions: vec![], - suggestion_param_name: suggestion_param_name.clone(), - }; - if let Some(fn_decl) = node.fn_decl() - && let hir::FnRetTy::Return(ty) = fn_decl.output - { - visitor.visit_ty(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(self.ty_sub); - if visitor.suggestions.is_empty() { - return false; - } - if introduce_new { - let new_param_suggestion = if let Some(first) = - generics.params.iter().find(|p| !p.name.ident().span.is_empty()) - { - (first.span.shrink_to_lo(), format!("{suggestion_param_name}, ")) - } else { - (generics.span, format!("<{suggestion_param_name}>")) - }; - - visitor.suggestions.push(new_param_suggestion); - } - diag.multipart_suggestion_verbose( - fluent::infer_lifetime_param_suggestion, - visitor.suggestions, - Applicability::MaybeIncorrect, - ); - diag.arg("is_impl", is_impl); - diag.arg("is_reuse", !introduce_new); - - true - }; - if mk_suggestion() && self.add_note { - diag.note(fluent::infer_lifetime_param_suggestion_elided); - } - } -} - -#[derive(Diagnostic)] -#[diag(infer_lifetime_mismatch, code = E0623)] -pub struct LifetimeMismatch<'a> { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub labels: LifetimeMismatchLabels, - #[subdiagnostic] - pub suggestion: AddLifetimeParamsSuggestion<'a>, -} - -pub struct IntroducesStaticBecauseUnmetLifetimeReq { - pub unmet_requirements: MultiSpan, - pub binding_span: Span, -} - -impl Subdiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - mut self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - self.unmet_requirements - .push_span_label(self.binding_span, fluent::infer_msl_introduces_static); - diag.span_note(self.unmet_requirements, fluent::infer_msl_unmet_req); - } -} - -// FIXME(#100717): replace with a `Option<Span>` when subdiagnostic supports that -#[derive(Subdiagnostic)] -pub enum DoesNotOutliveStaticFromImpl { - #[note(infer_does_not_outlive_static_from_impl)] - Spanned { - #[primary_span] - span: Span, - }, - #[note(infer_does_not_outlive_static_from_impl)] - Unspanned, -} - -#[derive(Subdiagnostic)] -pub enum ImplicitStaticLifetimeSubdiag { - #[note(infer_implicit_static_lifetime_note)] - Note { - #[primary_span] - span: Span, - }, - #[suggestion( - infer_implicit_static_lifetime_suggestion, - style = "verbose", - code = " + '_", - applicability = "maybe-incorrect" - )] - Sugg { - #[primary_span] - span: Span, - }, -} - -#[derive(Diagnostic)] -#[diag(infer_mismatched_static_lifetime)] -pub struct MismatchedStaticLifetime<'a> { - #[primary_span] - pub cause_span: Span, - #[subdiagnostic] - pub unmet_lifetime_reqs: IntroducesStaticBecauseUnmetLifetimeReq, - #[subdiagnostic] - pub expl: Option<note_and_explain::RegionExplanation<'a>>, - #[subdiagnostic] - pub does_not_outlive_static_from_impl: DoesNotOutliveStaticFromImpl, - #[subdiagnostic] - pub implicit_static_lifetimes: Vec<ImplicitStaticLifetimeSubdiag>, -} - -#[derive(Diagnostic)] -pub enum ExplicitLifetimeRequired<'a> { - #[diag(infer_explicit_lifetime_required_with_ident, code = E0621)] - WithIdent { - #[primary_span] - #[label] - span: Span, - simple_ident: Ident, - named: String, - #[suggestion( - infer_explicit_lifetime_required_sugg_with_ident, - code = "{new_ty}", - applicability = "unspecified" - )] - new_ty_span: Span, - #[skip_arg] - new_ty: Ty<'a>, - }, - #[diag(infer_explicit_lifetime_required_with_param_type, code = E0621)] - WithParamType { - #[primary_span] - #[label] - span: Span, - named: String, - #[suggestion( - infer_explicit_lifetime_required_sugg_with_param_type, - code = "{new_ty}", - applicability = "unspecified" - )] - new_ty_span: Span, - #[skip_arg] - new_ty: Ty<'a>, - }, -} - -pub enum TyOrSig<'tcx> { - Ty(Highlighted<'tcx, Ty<'tcx>>), - ClosureSig(Highlighted<'tcx, Binder<'tcx, FnSig<'tcx>>>), -} - -impl IntoDiagArg for TyOrSig<'_> { - fn into_diag_arg(self) -> rustc_errors::DiagArgValue { - match self { - TyOrSig::Ty(ty) => ty.into_diag_arg(), - TyOrSig::ClosureSig(sig) => sig.into_diag_arg(), - } - } -} - -#[derive(Subdiagnostic)] -pub enum ActualImplExplNotes<'tcx> { - #[note(infer_actual_impl_expl_expected_signature_two)] - ExpectedSignatureTwo { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - lifetime_2: usize, - }, - #[note(infer_actual_impl_expl_expected_signature_any)] - ExpectedSignatureAny { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - }, - #[note(infer_actual_impl_expl_expected_signature_some)] - ExpectedSignatureSome { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - }, - #[note(infer_actual_impl_expl_expected_signature_nothing)] - ExpectedSignatureNothing { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - }, - #[note(infer_actual_impl_expl_expected_passive_two)] - ExpectedPassiveTwo { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - lifetime_2: usize, - }, - #[note(infer_actual_impl_expl_expected_passive_any)] - ExpectedPassiveAny { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - }, - #[note(infer_actual_impl_expl_expected_passive_some)] - ExpectedPassiveSome { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - }, - #[note(infer_actual_impl_expl_expected_passive_nothing)] - ExpectedPassiveNothing { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - }, - #[note(infer_actual_impl_expl_expected_other_two)] - ExpectedOtherTwo { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - lifetime_2: usize, - }, - #[note(infer_actual_impl_expl_expected_other_any)] - ExpectedOtherAny { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - }, - #[note(infer_actual_impl_expl_expected_other_some)] - ExpectedOtherSome { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - }, - #[note(infer_actual_impl_expl_expected_other_nothing)] - ExpectedOtherNothing { - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - }, - #[note(infer_actual_impl_expl_but_actually_implements_trait)] - ButActuallyImplementsTrait { - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - has_lifetime: bool, - lifetime: usize, - }, - #[note(infer_actual_impl_expl_but_actually_implemented_for_ty)] - ButActuallyImplementedForTy { - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - has_lifetime: bool, - lifetime: usize, - ty: String, - }, - #[note(infer_actual_impl_expl_but_actually_ty_implements)] - ButActuallyTyImplements { - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - has_lifetime: bool, - lifetime: usize, - ty: String, - }, -} - -pub enum ActualImplExpectedKind { - Signature, - Passive, - Other, -} - -pub enum ActualImplExpectedLifetimeKind { - Two, - Any, - Some, - Nothing, -} - -impl<'tcx> ActualImplExplNotes<'tcx> { - pub fn new_expected( - kind: ActualImplExpectedKind, - lt_kind: ActualImplExpectedLifetimeKind, - leading_ellipsis: bool, - ty_or_sig: TyOrSig<'tcx>, - trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, - lifetime_1: usize, - lifetime_2: usize, - ) -> Self { - match (kind, lt_kind) { - (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Two) => { - Self::ExpectedSignatureTwo { - leading_ellipsis, - ty_or_sig, - trait_path, - lifetime_1, - lifetime_2, - } - } - (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Any) => { - Self::ExpectedSignatureAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } - } - (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Some) => { - Self::ExpectedSignatureSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } - } - (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Nothing) => { - Self::ExpectedSignatureNothing { leading_ellipsis, ty_or_sig, trait_path } - } - (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Two) => { - Self::ExpectedPassiveTwo { - leading_ellipsis, - ty_or_sig, - trait_path, - lifetime_1, - lifetime_2, - } - } - (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Any) => { - Self::ExpectedPassiveAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } - } - (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Some) => { - Self::ExpectedPassiveSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } - } - (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Nothing) => { - Self::ExpectedPassiveNothing { leading_ellipsis, ty_or_sig, trait_path } - } - (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Two) => { - Self::ExpectedOtherTwo { - leading_ellipsis, - ty_or_sig, - trait_path, - lifetime_1, - lifetime_2, - } - } - (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Any) => { - Self::ExpectedOtherAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } - } - (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Some) => { - Self::ExpectedOtherSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } - } - (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Nothing) => { - Self::ExpectedOtherNothing { leading_ellipsis, ty_or_sig, trait_path } - } - } - } -} - -#[derive(Diagnostic)] -#[diag(infer_trait_placeholder_mismatch)] -pub struct TraitPlaceholderMismatch<'tcx> { - #[primary_span] - pub span: Span, - #[label(infer_label_satisfy)] - pub satisfy_span: Option<Span>, - #[label(infer_label_where)] - pub where_span: Option<Span>, - #[label(infer_label_dup)] - pub dup_span: Option<Span>, - pub def_id: String, - pub trait_def_id: String, - - #[subdiagnostic] - pub actual_impl_expl_notes: Vec<ActualImplExplNotes<'tcx>>, -} - -pub struct ConsiderBorrowingParamHelp { - pub spans: Vec<Span>, -} - -impl Subdiagnostic for ConsiderBorrowingParamHelp { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { - let mut type_param_span: MultiSpan = self.spans.clone().into(); - for &span in &self.spans { - // Seems like we can't call f() here as Into<DiagMessage> is required - type_param_span.push_span_label(span, fluent::infer_tid_consider_borrowing); - } - let msg = f(diag, fluent::infer_tid_param_help.into()); - diag.span_help(type_param_span, msg); - } -} - -#[derive(Subdiagnostic)] -#[help(infer_tid_rel_help)] -pub struct RelationshipHelp; - -#[derive(Diagnostic)] -#[diag(infer_trait_impl_diff)] -pub struct TraitImplDiff { - #[primary_span] - #[label(infer_found)] - pub sp: Span, - #[label(infer_expected)] - pub trait_sp: Span, - #[note(infer_expected_found)] - pub note: (), - #[subdiagnostic] - pub param_help: ConsiderBorrowingParamHelp, - #[subdiagnostic] - // Seems like subdiagnostics are always pushed to the end, so this one - // also has to be a subdiagnostic to maintain order. - pub rel_help: Option<RelationshipHelp>, - pub expected: String, - pub found: String, -} - -pub struct DynTraitConstraintSuggestion { - pub span: Span, - pub ident: Ident, -} - -impl Subdiagnostic for DynTraitConstraintSuggestion { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { - let mut multi_span: MultiSpan = vec![self.span].into(); - multi_span.push_span_label(self.span, fluent::infer_dtcs_has_lifetime_req_label); - multi_span.push_span_label(self.ident.span, fluent::infer_dtcs_introduces_requirement); - let msg = f(diag, fluent::infer_dtcs_has_req_note.into()); - diag.span_note(multi_span, msg); - let msg = f(diag, fluent::infer_dtcs_suggestion.into()); - diag.span_suggestion_verbose( - self.span.shrink_to_hi(), - msg, - " + '_", - Applicability::MaybeIncorrect, - ); - } -} - -#[derive(Diagnostic)] -#[diag(infer_but_calling_introduces, code = E0772)] -pub struct ButCallingIntroduces { - #[label(infer_label1)] - pub param_ty_span: Span, - #[primary_span] - #[label(infer_label2)] - pub cause_span: Span, - - pub has_param_name: bool, - pub param_name: String, - pub has_lifetime: bool, - pub lifetime: String, - pub assoc_item: Symbol, - pub has_impl_path: bool, - pub impl_path: String, -} - -pub struct ReqIntroducedLocations { - pub span: MultiSpan, - pub spans: Vec<Span>, - pub fn_decl_span: Span, - pub cause_span: Span, - pub add_label: bool, -} - -impl Subdiagnostic for ReqIntroducedLocations { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - mut self, - diag: &mut Diag<'_, G>, - f: &F, - ) { - for sp in self.spans { - self.span.push_span_label(sp, fluent::infer_ril_introduced_here); - } - - if self.add_label { - self.span.push_span_label(self.fn_decl_span, fluent::infer_ril_introduced_by); - } - self.span.push_span_label(self.cause_span, fluent::infer_ril_because_of); - let msg = f(diag, fluent::infer_ril_static_introduced_by.into()); - diag.span_note(self.span, msg); - } -} - -pub struct MoreTargeted { - pub ident: Symbol, -} - -impl Subdiagnostic for MoreTargeted { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - diag.code(E0772); - diag.primary_message(fluent::infer_more_targeted); - diag.arg("ident", self.ident); - } -} - -#[derive(Diagnostic)] -#[diag(infer_but_needs_to_satisfy, code = E0759)] -pub struct ButNeedsToSatisfy { - #[primary_span] - pub sp: Span, - #[label(infer_influencer)] - pub influencer_point: Span, - #[label(infer_used_here)] - pub spans: Vec<Span>, - #[label(infer_require)] - pub require_span_as_label: Option<Span>, - #[note(infer_require)] - pub require_span_as_note: Option<Span>, - #[note(infer_introduced_by_bound)] - pub bound: Option<Span>, - - #[subdiagnostic] - pub req_introduces_loc: Option<ReqIntroducedLocations>, - - pub has_param_name: bool, - pub param_name: String, - pub spans_empty: bool, - pub has_lifetime: bool, - pub lifetime: String, -} - -#[derive(Diagnostic)] -#[diag(infer_outlives_content, code = E0312)] -pub struct OutlivesContent<'a> { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub notes: Vec<note_and_explain::RegionExplanation<'a>>, -} - -#[derive(Diagnostic)] -#[diag(infer_outlives_bound, code = E0476)] -pub struct OutlivesBound<'a> { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub notes: Vec<note_and_explain::RegionExplanation<'a>>, -} - -#[derive(Diagnostic)] -#[diag(infer_fulfill_req_lifetime, code = E0477)] -pub struct FulfillReqLifetime<'a> { - #[primary_span] - pub span: Span, - pub ty: Ty<'a>, - #[subdiagnostic] - pub note: Option<note_and_explain::RegionExplanation<'a>>, -} - -#[derive(Diagnostic)] -#[diag(infer_lf_bound_not_satisfied, code = E0478)] -pub struct LfBoundNotSatisfied<'a> { - #[primary_span] - pub span: Span, - #[subdiagnostic] - pub notes: Vec<note_and_explain::RegionExplanation<'a>>, -} - -#[derive(Diagnostic)] -#[diag(infer_ref_longer_than_data, code = E0491)] -pub struct RefLongerThanData<'a> { - #[primary_span] - pub span: Span, - pub ty: Ty<'a>, - #[subdiagnostic] - pub notes: Vec<note_and_explain::RegionExplanation<'a>>, -} - -#[derive(Subdiagnostic)] -pub enum WhereClauseSuggestions { - #[suggestion( - infer_where_remove, - code = "", - applicability = "machine-applicable", - style = "verbose" - )] - Remove { - #[primary_span] - span: Span, - }, - #[suggestion( - infer_where_copy_predicates, - code = "{space}where {trait_predicates}", - applicability = "machine-applicable", - style = "verbose" - )] - CopyPredicates { - #[primary_span] - span: Span, - space: &'static str, - trait_predicates: String, - }, -} - -#[derive(Subdiagnostic)] -pub enum SuggestRemoveSemiOrReturnBinding { - #[multipart_suggestion(infer_srs_remove_and_box, applicability = "machine-applicable")] - RemoveAndBox { - #[suggestion_part(code = "Box::new(")] - first_lo: Span, - #[suggestion_part(code = ")")] - first_hi: Span, - #[suggestion_part(code = "Box::new(")] - second_lo: Span, - #[suggestion_part(code = ")")] - second_hi: Span, - #[suggestion_part(code = "")] - sp: Span, - }, - #[suggestion( - infer_srs_remove, - style = "short", - code = "", - applicability = "machine-applicable" - )] - Remove { - #[primary_span] - sp: Span, - }, - #[suggestion( - infer_srs_add, - style = "verbose", - code = "{code}", - applicability = "maybe-incorrect" - )] - Add { - #[primary_span] - sp: Span, - code: String, - ident: Ident, - }, - #[note(infer_srs_add_one)] - AddOne { - #[primary_span] - spans: MultiSpan, - }, -} - -#[derive(Subdiagnostic)] -pub enum ConsiderAddingAwait { - #[help(infer_await_both_futures)] - BothFuturesHelp, - #[multipart_suggestion(infer_await_both_futures, applicability = "maybe-incorrect")] - BothFuturesSugg { - #[suggestion_part(code = ".await")] - first: Span, - #[suggestion_part(code = ".await")] - second: Span, - }, - #[suggestion( - infer_await_future, - code = ".await", - style = "verbose", - applicability = "maybe-incorrect" - )] - FutureSugg { - #[primary_span] - span: Span, - }, - #[note(infer_await_note)] - FutureSuggNote { - #[primary_span] - span: Span, - }, - #[multipart_suggestion( - infer_await_future, - style = "verbose", - applicability = "maybe-incorrect" - )] - FutureSuggMultiple { - #[suggestion_part(code = ".await")] - spans: Vec<Span>, - }, -} - -#[derive(Diagnostic)] -pub enum PlaceholderRelationLfNotSatisfied { - #[diag(infer_lf_bound_not_satisfied)] - HasBoth { - #[primary_span] - span: Span, - #[note(infer_prlf_defined_with_sub)] - sub_span: Span, - #[note(infer_prlf_must_outlive_with_sup)] - sup_span: Span, - sub_symbol: Symbol, - sup_symbol: Symbol, - #[note(infer_prlf_known_limitation)] - note: (), - }, - #[diag(infer_lf_bound_not_satisfied)] - HasSub { - #[primary_span] - span: Span, - #[note(infer_prlf_defined_with_sub)] - sub_span: Span, - #[note(infer_prlf_must_outlive_without_sup)] - sup_span: Span, - sub_symbol: Symbol, - #[note(infer_prlf_known_limitation)] - note: (), - }, - #[diag(infer_lf_bound_not_satisfied)] - HasSup { - #[primary_span] - span: Span, - #[note(infer_prlf_defined_without_sub)] - sub_span: Span, - #[note(infer_prlf_must_outlive_with_sup)] - sup_span: Span, - sup_symbol: Symbol, - #[note(infer_prlf_known_limitation)] - note: (), - }, - #[diag(infer_lf_bound_not_satisfied)] - HasNone { - #[primary_span] - span: Span, - #[note(infer_prlf_defined_without_sub)] - sub_span: Span, - #[note(infer_prlf_must_outlive_without_sup)] - sup_span: Span, - #[note(infer_prlf_known_limitation)] - note: (), - }, - #[diag(infer_lf_bound_not_satisfied)] - OnlyPrimarySpan { - #[primary_span] - span: Span, - #[note(infer_prlf_known_limitation)] - note: (), - }, -} - -#[derive(Diagnostic)] -#[diag(infer_opaque_captures_lifetime, code = E0700)] -pub struct OpaqueCapturesLifetime<'tcx> { - #[primary_span] - pub span: Span, - #[label] - pub opaque_ty_span: Span, - pub opaque_ty: Ty<'tcx>, -} - -#[derive(Subdiagnostic)] -pub enum FunctionPointerSuggestion<'a> { - #[suggestion( - infer_fps_use_ref, - code = "&{fn_name}", - style = "verbose", - applicability = "maybe-incorrect" - )] - UseRef { - #[primary_span] - span: Span, - #[skip_arg] - fn_name: String, - }, - #[suggestion( - infer_fps_remove_ref, - code = "{fn_name}", - style = "verbose", - applicability = "maybe-incorrect" - )] - RemoveRef { - #[primary_span] - span: Span, - #[skip_arg] - fn_name: String, - }, - #[suggestion( - infer_fps_cast, - code = "&({fn_name} as {sig})", - style = "verbose", - applicability = "maybe-incorrect" - )] - CastRef { - #[primary_span] - span: Span, - #[skip_arg] - fn_name: String, - #[skip_arg] - sig: Binder<'a, FnSig<'a>>, - }, - #[suggestion( - infer_fps_cast, - code = "{fn_name} as {sig}", - style = "verbose", - applicability = "maybe-incorrect" - )] - Cast { - #[primary_span] - span: Span, - #[skip_arg] - fn_name: String, - #[skip_arg] - sig: Binder<'a, FnSig<'a>>, - }, - #[suggestion( - infer_fps_cast_both, - code = "{fn_name} as {found_sig}", - style = "hidden", - applicability = "maybe-incorrect" - )] - CastBoth { - #[primary_span] - span: Span, - #[skip_arg] - fn_name: String, - #[skip_arg] - found_sig: Binder<'a, FnSig<'a>>, - expected_sig: Binder<'a, FnSig<'a>>, - }, - #[suggestion( - infer_fps_cast_both, - code = "&({fn_name} as {found_sig})", - style = "hidden", - applicability = "maybe-incorrect" - )] - CastBothRef { - #[primary_span] - span: Span, - #[skip_arg] - fn_name: String, - #[skip_arg] - found_sig: Binder<'a, FnSig<'a>>, - expected_sig: Binder<'a, FnSig<'a>>, - }, -} - -#[derive(Subdiagnostic)] -#[note(infer_fps_items_are_distinct)] -pub struct FnItemsAreDistinct; - -#[derive(Subdiagnostic)] -#[note(infer_fn_uniq_types)] -pub struct FnUniqTypes; - -#[derive(Subdiagnostic)] -#[help(infer_fn_consider_casting)] -pub struct FnConsiderCasting { - pub casting: String, -} - -#[derive(Subdiagnostic)] -pub enum SuggestAccessingField<'a> { - #[suggestion( - infer_suggest_accessing_field, - code = "{snippet}.{name}", - applicability = "maybe-incorrect" - )] - Safe { - #[primary_span] - span: Span, - snippet: String, - name: Symbol, - ty: Ty<'a>, - }, - #[suggestion( - infer_suggest_accessing_field, - code = "unsafe {{ {snippet}.{name} }}", - applicability = "maybe-incorrect" - )] - Unsafe { - #[primary_span] - span: Span, - snippet: String, - name: Symbol, - ty: Ty<'a>, - }, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion(infer_stp_wrap_one, applicability = "maybe-incorrect")] -pub struct SuggestTuplePatternOne { - pub variant: String, - #[suggestion_part(code = "{variant}(")] - pub span_low: Span, - #[suggestion_part(code = ")")] - pub span_high: Span, -} - -pub struct SuggestTuplePatternMany { - pub path: String, - pub cause_span: Span, - pub compatible_variants: Vec<String>, -} - -impl Subdiagnostic for SuggestTuplePatternMany { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - f: &F, - ) { - diag.arg("path", self.path); - let message = f(diag, crate::fluent_generated::infer_stp_wrap_many.into()); - diag.multipart_suggestions( - message, - self.compatible_variants.into_iter().map(|variant| { - vec![ - (self.cause_span.shrink_to_lo(), format!("{variant}(")), - (self.cause_span.shrink_to_hi(), ")".to_string()), - ] - }), - rustc_errors::Applicability::MaybeIncorrect, - ); - } -} - -#[derive(Subdiagnostic)] -pub enum TypeErrorAdditionalDiags { - #[suggestion( - infer_meant_byte_literal, - code = "b'{code}'", - applicability = "machine-applicable" - )] - MeantByteLiteral { - #[primary_span] - span: Span, - code: String, - }, - #[suggestion( - infer_meant_char_literal, - code = "'{code}'", - applicability = "machine-applicable" - )] - MeantCharLiteral { - #[primary_span] - span: Span, - code: String, - }, - #[multipart_suggestion(infer_meant_str_literal, applicability = "machine-applicable")] - MeantStrLiteral { - #[suggestion_part(code = "\"")] - start: Span, - #[suggestion_part(code = "\"")] - end: Span, - }, - #[suggestion( - infer_consider_specifying_length, - code = "{length}", - applicability = "maybe-incorrect" - )] - ConsiderSpecifyingLength { - #[primary_span] - span: Span, - length: u64, - }, - #[note(infer_try_cannot_convert)] - TryCannotConvert { found: String, expected: String }, - #[suggestion(infer_tuple_trailing_comma, code = ",", applicability = "machine-applicable")] - TupleOnlyComma { - #[primary_span] - span: Span, - }, - #[multipart_suggestion(infer_tuple_trailing_comma, applicability = "machine-applicable")] - TupleAlsoParentheses { - #[suggestion_part(code = "(")] - span_low: Span, - #[suggestion_part(code = ",)")] - span_high: Span, - }, - #[suggestion( - infer_suggest_add_let_for_letchains, - style = "verbose", - applicability = "machine-applicable", - code = "let " - )] - AddLetForLetChains { - #[primary_span] - span: Span, - }, -} - -#[derive(Diagnostic)] -pub enum ObligationCauseFailureCode { - #[diag(infer_oc_method_compat, code = E0308)] - MethodCompat { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_type_compat, code = E0308)] - TypeCompat { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_const_compat, code = E0308)] - ConstCompat { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_try_compat, code = E0308)] - TryCompat { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_match_compat, code = E0308)] - MatchCompat { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_if_else_different, code = E0308)] - IfElseDifferent { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_no_else, code = E0317)] - NoElse { - #[primary_span] - span: Span, - }, - #[diag(infer_oc_no_diverge, code = E0308)] - NoDiverge { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_fn_main_correct_type, code = E0580)] - FnMainCorrectType { - #[primary_span] - span: Span, - }, - #[diag(infer_oc_fn_start_correct_type, code = E0308)] - FnStartCorrectType { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_fn_lang_correct_type, code = E0308)] - FnLangCorrectType { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - lang_item_name: Symbol, - }, - #[diag(infer_oc_intrinsic_correct_type, code = E0308)] - IntrinsicCorrectType { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_method_correct_type, code = E0308)] - MethodCorrectType { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_closure_selfref, code = E0644)] - ClosureSelfref { - #[primary_span] - span: Span, - }, - #[diag(infer_oc_cant_coerce, code = E0308)] - CantCoerce { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, - #[diag(infer_oc_generic, code = E0308)] - Generic { - #[primary_span] - span: Span, - #[subdiagnostic] - subdiags: Vec<TypeErrorAdditionalDiags>, - }, -} - -#[derive(Subdiagnostic)] -pub enum AddPreciseCapturing { - #[suggestion( - infer_precise_capturing_new, - style = "verbose", - code = " + use<{concatenated_bounds}>", - applicability = "machine-applicable" - )] - New { - #[primary_span] - span: Span, - new_lifetime: Symbol, - concatenated_bounds: String, - }, - #[suggestion( - infer_precise_capturing_existing, - style = "verbose", - code = "{pre}{new_lifetime}{post}", - applicability = "machine-applicable" - )] - Existing { - #[primary_span] - span: Span, - new_lifetime: Symbol, - pre: &'static str, - post: &'static str, - }, -} - -pub struct AddPreciseCapturingAndParams { - pub suggs: Vec<(Span, String)>, - pub new_lifetime: Symbol, - pub apit_spans: Vec<Span>, -} - -impl Subdiagnostic for AddPreciseCapturingAndParams { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - diag.arg("new_lifetime", self.new_lifetime); - diag.multipart_suggestion_verbose( - fluent::infer_precise_capturing_new_but_apit, - self.suggs, - Applicability::MaybeIncorrect, - ); - diag.span_note(self.apit_spans, fluent::infer_warn_removing_apit_params); - } -} diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index c9073d8c23e..3cee0a622f1 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -11,7 +11,6 @@ pub use BoundRegionConversionTime::*; pub use RegionVariableOrigin::*; pub use SubregionOrigin::*; -use crate::error_reporting::infer::TypeErrCtxt; use crate::infer::relate::RelateResult; use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligation, TraitEngine}; use free_regions::RegionRelations; @@ -24,7 +23,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::Rollback; use rustc_data_structures::unify as ut; -use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_errors::ErrorGuaranteed; +use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::extension; use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; @@ -65,8 +65,6 @@ pub mod relate; pub mod resolve; pub(crate) mod snapshot; pub mod type_variable; -// FIXME(error_reporting): Where should we put this? -pub mod need_type_info; #[must_use] #[derive(Debug)] @@ -698,36 +696,24 @@ impl<'tcx> InferCtxt<'tcx> { self.next_trait_solver } - /// Creates a `TypeErrCtxt` for emitting various inference errors. - /// During typeck, use `FnCtxt::err_ctxt` instead. - pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> { - TypeErrCtxt { - infcx: self, - sub_relations: Default::default(), - typeck_results: None, - fallback_has_occurred: false, - normalize_fn_sig: Box::new(|fn_sig| fn_sig), - autoderef_steps: Box::new(|ty| { - debug_assert!(false, "shouldn't be using autoderef_steps outside of typeck"); - vec![(ty, vec![])] - }), - } - } - pub fn freshen<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T { t.fold_with(&mut self.freshener()) } - /// Returns the origin of the type variable identified by `vid`, or `None` - /// if this is not a type variable. + /// Returns the origin of the type variable identified by `vid`. /// - /// No attempt is made to resolve `ty`. - pub fn type_var_origin(&self, ty: Ty<'tcx>) -> Option<TypeVariableOrigin> { - match *ty.kind() { - ty::Infer(ty::TyVar(vid)) => { - Some(self.inner.borrow_mut().type_variables().var_origin(vid)) - } - _ => None, + /// No attempt is made to resolve `vid` to its root variable. + pub fn type_var_origin(&self, vid: TyVid) -> TypeVariableOrigin { + self.inner.borrow_mut().type_variables().var_origin(vid) + } + + /// Returns the origin of the const variable identified by `vid` + // FIXME: We should store origins separately from the unification table + // so this doesn't need to be optional. + pub fn const_var_origin(&self, vid: ConstVid) -> Option<ConstVariableOrigin> { + match self.inner.borrow_mut().const_unification_table().probe_value(vid) { + ConstVariableValue::Known { .. } => None, + ConstVariableValue::Unknown { origin, .. } => Some(origin), } } @@ -769,18 +755,6 @@ impl<'tcx> InferCtxt<'tcx> { .collect() } - // FIXME(-Znext-solver): Get rid of this method, it's never correct. Either that, - // or we need to process the obligations. - pub fn can_eq_shallow<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool - where - T: at::ToTrace<'tcx>, - { - let origin = &ObligationCause::dummy(); - // We're only answering whether the types could be the same, and with - // opaque types, "they can be the same", via registering a hidden type. - self.probe(|_| self.at(origin, param_env).eq(DefineOpaqueTypes::Yes, a, b).is_ok()) - } - #[instrument(skip(self), level = "debug")] pub fn sub_regions( &self, @@ -1589,60 +1563,6 @@ impl<'tcx> InferCtxt<'tcx> { } } -impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - // [Note-Type-error-reporting] - // An invariant is that anytime the expected or actual type is Error (the special - // error type, meaning that an error occurred when typechecking this expression), - // this is a derived error. The error cascaded from another error (that was already - // reported), so it's not useful to display it to the user. - // The following methods implement this logic. - // They check if either the actual or expected type is Error, and don't print the error - // in this case. The typechecker should only ever report type errors involving mismatched - // types using one of these methods, and should not call span_err directly for such - // errors. - pub fn type_error_struct_with_diag<M>( - &self, - sp: Span, - mk_diag: M, - actual_ty: Ty<'tcx>, - ) -> Diag<'a> - where - M: FnOnce(String) -> Diag<'a>, - { - let actual_ty = self.resolve_vars_if_possible(actual_ty); - debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty); - - let mut err = mk_diag(self.ty_to_string(actual_ty)); - - // Don't report an error if actual type is `Error`. - if actual_ty.references_error() { - err.downgrade_to_delayed_bug(); - } - - err - } - - pub fn report_mismatched_types( - &self, - cause: &ObligationCause<'tcx>, - expected: Ty<'tcx>, - actual: Ty<'tcx>, - err: TypeError<'tcx>, - ) -> Diag<'a> { - self.report_and_explain_type_error(TypeTrace::types(cause, true, expected, actual), err) - } - - pub fn report_mismatched_consts( - &self, - cause: &ObligationCause<'tcx>, - expected: ty::Const<'tcx>, - actual: ty::Const<'tcx>, - err: TypeError<'tcx>, - ) -> Diag<'a> { - self.report_and_explain_type_error(TypeTrace::consts(cause, true, expected, actual), err) - } -} - /// Helper for [InferCtxt::ty_or_const_infer_var_changed] (see comment on that), currently /// used only for `traits::fulfill`'s list of `stalled_on` inference variables. #[derive(Copy, Clone, Debug)] @@ -1888,3 +1808,32 @@ fn replace_param_and_infer_args_with_placeholder<'tcx>( args.fold_with(&mut ReplaceParamAndInferWithPlaceholder { tcx, idx: 0 }) } + +impl<'tcx> InferCtxt<'tcx> { + /// Given a [`hir::Block`], get the span of its last expression or + /// statement, peeling off any inner blocks. + pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span { + let block = block.innermost_block(); + if let Some(expr) = &block.expr { + expr.span + } else if let Some(stmt) = block.stmts.last() { + // possibly incorrect trailing `;` in the else arm + stmt.span + } else { + // empty block; point at its entirety + block.span + } + } + + /// Given a [`hir::HirId`] for a block, get the span of its last expression + /// or statement, peeling off any inner blocks. + pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span { + match self.tcx.hir_node(hir_id) { + hir::Node::Block(blk) => self.find_block_span(blk), + // The parser was in a weird state if either of these happen, but + // it's better not to panic. + hir::Node::Expr(e) => e.span, + _ => rustc_span::DUMMY_SP, + } + } +} diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 02ebf933f53..b65ac859667 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -34,7 +34,6 @@ #[macro_use] extern crate tracing; -pub mod error_reporting; mod errors; pub mod infer; pub mod traits; diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs deleted file mode 100644 index 7730fe29e09..00000000000 --- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs +++ /dev/null @@ -1,204 +0,0 @@ -use super::ObjectSafetyViolation; - -use crate::infer::InferCtxt; -use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diag, MultiSpan}; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::Span; -use std::fmt; -use std::iter; - -impl<'tcx> InferCtxt<'tcx> { - pub fn report_extra_impl_obligation<'a>( - &'a self, - error_span: Span, - impl_item_def_id: LocalDefId, - trait_item_def_id: DefId, - requirement: &dyn fmt::Display, - ) -> Diag<'a> { - let mut err = struct_span_code_err!( - self.dcx(), - error_span, - E0276, - "impl has stricter requirements than trait" - ); - - if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) { - if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) { - let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); - err.span_label(span, format!("definition of `{item_name}` from trait")); - } - } - - err.span_label(error_span, format!("impl has extra requirement {requirement}")); - - err - } -} - -pub fn report_object_safety_error<'tcx>( - tcx: TyCtxt<'tcx>, - span: Span, - hir_id: Option<hir::HirId>, - trait_def_id: DefId, - violations: &[ObjectSafetyViolation], -) -> Diag<'tcx> { - let trait_str = tcx.def_path_str(trait_def_id); - let trait_span = tcx.hir().get_if_local(trait_def_id).and_then(|node| match node { - hir::Node::Item(item) => Some(item.ident.span), - _ => None, - }); - let mut err = struct_span_code_err!( - tcx.dcx(), - span, - E0038, - "the trait `{}` cannot be made into an object", - trait_str - ); - err.span_label(span, format!("`{trait_str}` cannot be made into an object")); - - if let Some(hir_id) = hir_id - && let hir::Node::Ty(ty) = tcx.hir_node(hir_id) - && let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind - { - let mut hir_id = hir_id; - while let hir::Node::Ty(ty) = tcx.parent_hir_node(hir_id) { - hir_id = ty.hir_id; - } - if tcx.parent_hir_node(hir_id).fn_sig().is_some() { - // Do not suggest `impl Trait` when dealing with things like super-traits. - err.span_suggestion_verbose( - ty.span.until(trait_ref.span), - "consider using an opaque type instead", - "impl ", - Applicability::MaybeIncorrect, - ); - } - } - let mut reported_violations = FxIndexSet::default(); - let mut multi_span = vec![]; - let mut messages = vec![]; - for violation in violations { - if let ObjectSafetyViolation::SizedSelf(sp) = &violation - && !sp.is_empty() - { - // Do not report `SizedSelf` without spans pointing at `SizedSelf` obligations - // with a `Span`. - reported_violations.insert(ObjectSafetyViolation::SizedSelf(vec![].into())); - } - if reported_violations.insert(violation.clone()) { - let spans = violation.spans(); - let msg = if trait_span.is_none() || spans.is_empty() { - format!("the trait cannot be made into an object because {}", violation.error_msg()) - } else { - format!("...because {}", violation.error_msg()) - }; - if spans.is_empty() { - err.note(msg); - } else { - for span in spans { - multi_span.push(span); - messages.push(msg.clone()); - } - } - } - } - let has_multi_span = !multi_span.is_empty(); - let mut note_span = MultiSpan::from_spans(multi_span.clone()); - if let (Some(trait_span), true) = (trait_span, has_multi_span) { - note_span.push_span_label(trait_span, "this trait cannot be made into an object..."); - } - for (span, msg) in iter::zip(multi_span, messages) { - note_span.push_span_label(span, msg); - } - err.span_note( - note_span, - "for a trait to be \"object safe\" it needs to allow building a vtable to allow the call \ - to be resolvable dynamically; for more information visit \ - <https://doc.rust-lang.org/reference/items/traits.html#object-safety>", - ); - - // Only provide the help if its a local trait, otherwise it's not actionable. - if trait_span.is_some() { - let mut reported_violations: Vec<_> = reported_violations.into_iter().collect(); - reported_violations.sort(); - - let mut potential_solutions: Vec<_> = - reported_violations.into_iter().map(|violation| violation.solution()).collect(); - potential_solutions.sort(); - // Allows us to skip suggesting that the same item should be moved to another trait multiple times. - potential_solutions.dedup(); - for solution in potential_solutions { - solution.add_to(&mut err); - } - } - - let impls_of = tcx.trait_impls_of(trait_def_id); - let impls = if impls_of.blanket_impls().is_empty() { - impls_of - .non_blanket_impls() - .values() - .flatten() - .filter(|def_id| { - !matches!(tcx.type_of(*def_id).instantiate_identity().kind(), ty::Dynamic(..)) - }) - .collect::<Vec<_>>() - } else { - vec![] - }; - let externally_visible = if !impls.is_empty() - && let Some(def_id) = trait_def_id.as_local() - // We may be executing this during typeck, which would result in cycle - // if we used effective_visibilities query, which looks into opaque types - // (and therefore calls typeck). - && tcx.resolutions(()).effective_visibilities.is_exported(def_id) - { - true - } else { - false - }; - match &impls[..] { - [] => {} - _ if impls.len() > 9 => {} - [only] if externally_visible => { - err.help(with_no_trimmed_paths!(format!( - "only type `{}` is seen to implement the trait in this crate, consider using it \ - directly instead", - tcx.type_of(*only).instantiate_identity(), - ))); - } - [only] => { - err.help(with_no_trimmed_paths!(format!( - "only type `{}` implements the trait, consider using it directly instead", - tcx.type_of(*only).instantiate_identity(), - ))); - } - impls => { - let mut types = impls - .iter() - .map(|t| { - with_no_trimmed_paths!(format!(" {}", tcx.type_of(*t).instantiate_identity(),)) - }) - .collect::<Vec<_>>(); - types.sort(); - err.help(format!( - "the following types implement the trait, consider defining an enum where each \ - variant holds one of these types, implementing `{}` for this new enum and using \ - it instead:\n{}", - trait_str, - types.join("\n"), - )); - } - } - if externally_visible { - err.note(format!( - "`{trait_str}` can be implemented in other crates; if you want to support your users \ - passing their own types here, you can't refer to a specific type", - )); - } - - err -} diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 556b3bd063d..7bc3af374fc 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -3,7 +3,6 @@ //! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html mod engine; -pub mod error_reporting; mod project; mod structural_impls; pub mod util; diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 290f91045c4..fc073233d97 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -322,6 +322,8 @@ fn register_builtins(store: &mut LintStore) { REFINING_IMPL_TRAIT_INTERNAL ); + add_lint_group!("deprecated_safe", DEPRECATED_SAFE_2024); + // Register renamed and removed lints. store.register_renamed("single_use_lifetime", "single_use_lifetimes"); store.register_renamed("elided_lifetime_in_path", "elided_lifetimes_in_paths"); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 04764b71b10..2f4e6a32308 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -37,7 +37,7 @@ declare_lint_pass! { DEPRECATED, DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, DEPRECATED_IN_FUTURE, - DEPRECATED_SAFE, + DEPRECATED_SAFE_2024, DEPRECATED_WHERE_CLAUSE_LOCATION, DUPLICATE_MACRO_ATTRIBUTES, ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT, @@ -4812,8 +4812,8 @@ declare_lint! { } declare_lint! { - /// The `deprecated_safe` lint detects unsafe functions being used as safe - /// functions. + /// The `deprecated_safe_2024` lint detects unsafe functions being used as + /// safe functions. /// /// ### Example /// @@ -4832,8 +4832,8 @@ declare_lint! { /// /// Rust [editions] allow the language to evolve without breaking backward /// compatibility. This lint catches code that uses `unsafe` functions that - /// were declared as safe (non-`unsafe`) in earlier editions. If you switch - /// the compiler to a new edition without updating the code, then it + /// were declared as safe (non-`unsafe`) in editions prior to Rust 2024. If + /// you switch the compiler to Rust 2024 without updating the code, then it /// will fail to compile if you are using a function previously marked as /// safe. /// @@ -4850,7 +4850,7 @@ declare_lint! { /// future. /// /// [editions]: https://doc.rust-lang.org/edition-guide/ - pub DEPRECATED_SAFE, + pub DEPRECATED_SAFE_2024, Allow, "detects unsafe functions being used as safe functions", @future_incompatible = FutureIncompatibleInfo { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 0ba9b940eed..e5e430bc90d 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -7,6 +7,7 @@ use crate::rmeta::*; use rustc_ast as ast; use rustc_data_structures::captures::Captures; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::owned_slice::OwnedSlice; use rustc_data_structures::sync::{Lock, Lrc, OnceLock}; use rustc_data_structures::unhash::UnhashMap; @@ -83,12 +84,12 @@ pub(crate) struct CrateMetadata { /// Trait impl data. /// FIXME: Used only from queries and can use query cache, /// so pre-decoding can probably be avoided. - trait_impls: FxHashMap<(u32, DefIndex), LazyArray<(DefIndex, Option<SimplifiedType>)>>, + trait_impls: FxIndexMap<(u32, DefIndex), LazyArray<(DefIndex, Option<SimplifiedType>)>>, /// Inherent impls which do not follow the normal coherence rules. /// /// These can be introduced using either `#![rustc_coherence_is_core]` /// or `#[rustc_allow_incoherent_impl]`. - incoherent_impls: FxHashMap<SimplifiedType, LazyArray<DefIndex>>, + incoherent_impls: FxIndexMap<SimplifiedType, LazyArray<DefIndex>>, /// Proc macro descriptions for this crate, if it's a proc macro crate. raw_proc_macros: Option<&'static [ProcMacro]>, /// Source maps for code from the crate. diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 6b240f0f0b3..bbd9ab5704f 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -72,6 +72,15 @@ impl<'a, 'tcx, T: Copy + Decodable<DecodeContext<'a, 'tcx>>> ProcessQueryValue<' } } +impl<'a, 'tcx, T: Copy + Decodable<DecodeContext<'a, 'tcx>>> + ProcessQueryValue<'tcx, Option<&'tcx [T]>> for Option<DecodeIterator<'a, 'tcx, T>> +{ + #[inline(always)] + fn process_decoded(self, tcx: TyCtxt<'tcx>, _err: impl Fn() -> !) -> Option<&'tcx [T]> { + if let Some(iter) = self { Some(&*tcx.arena.alloc_from_iter(iter)) } else { None } + } +} + impl ProcessQueryValue<'_, Option<DeprecationEntry>> for Option<Deprecation> { #[inline(always)] fn process_decoded(self, _tcx: TyCtxt<'_>, _err: impl Fn() -> !) -> Option<DeprecationEntry> { @@ -249,6 +258,7 @@ provide! { tcx, def_id, other, cdata, .process_decoded(tcx, || panic!("{def_id:?} does not have coerce_unsized_info"))) } mir_const_qualif => { table } rendered_const => { table } + rendered_precise_capturing_args => { table } asyncness => { table_direct } fn_arg_names => { table } coroutine_kind => { table_direct } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 209316ca20f..6f31c0fa520 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2,7 +2,7 @@ use crate::errors::{FailCreateFileEncoder, FailWriteFile}; use crate::rmeta::*; use rustc_ast::Attribute; -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::{Mmap, MmapMut}; use rustc_data_structures::sync::{join, par_for_each_in, Lrc}; use rustc_data_structures::temp_dir::MaybeTempDir; @@ -13,7 +13,6 @@ use rustc_hir_pretty::id_to_string; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::metadata_symbol_name; use rustc_middle::mir::interpret; -use rustc_middle::query::LocalCrate; use rustc_middle::query::Providers; use rustc_middle::traits::specialization_graph; use rustc_middle::ty::codec::TyEncoder; @@ -1496,6 +1495,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.tables .is_type_alias_impl_trait .set(def_id.index, self.tcx.is_type_alias_impl_trait(def_id)); + self.encode_precise_capturing_args(def_id); } if tcx.impl_method_has_trait_impl_trait_tys(def_id) && let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id) @@ -1508,10 +1508,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } - let inherent_impls = tcx.with_stable_hashing_context(|hcx| { - tcx.crate_inherent_impls(()).unwrap().inherent_impls.to_sorted(&hcx, true) - }); - for (def_id, impls) in inherent_impls { + for (def_id, impls) in &tcx.crate_inherent_impls(()).unwrap().inherent_impls { record_defaulted_array!(self.tables.inherent_impls[def_id.to_def_id()] <- impls.iter().map(|def_id| { assert!(def_id.is_local()); def_id.index @@ -1635,6 +1632,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.tables.assumed_wf_types_for_rpitit[def_id] <- self.tcx.assumed_wf_types_for_rpitit(def_id) ); + self.encode_precise_capturing_args(def_id); } } if item.is_effects_desugaring { @@ -1642,6 +1640,14 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } + fn encode_precise_capturing_args(&mut self, def_id: DefId) { + let Some(precise_capturing_args) = self.tcx.rendered_precise_capturing_args(def_id) else { + return; + }; + + record_array!(self.tables.rendered_precise_capturing_args[def_id] <- precise_capturing_args); + } + fn encode_mir(&mut self) { if self.is_proc_macro { return; @@ -1992,8 +1998,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_impls(&mut self) -> LazyArray<TraitImpls> { empty_proc_macro!(self); let tcx = self.tcx; - let mut fx_hash_map: FxHashMap<DefId, Vec<(DefIndex, Option<SimplifiedType>)>> = - FxHashMap::default(); + let mut trait_impls: FxIndexMap<DefId, Vec<(DefIndex, Option<SimplifiedType>)>> = + FxIndexMap::default(); for id in tcx.hir().items() { let DefKind::Impl { of_trait } = tcx.def_kind(id.owner_id) else { @@ -2012,7 +2018,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { trait_ref.self_ty(), TreatParams::AsCandidateKey, ); - fx_hash_map + trait_impls .entry(trait_ref.def_id) .or_default() .push((id.owner_id.def_id.local_def_index, simplified_self_ty)); @@ -2033,47 +2039,30 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } - let mut all_impls: Vec<_> = fx_hash_map.into_iter().collect(); - - // Bring everything into deterministic order for hashing - all_impls.sort_by_cached_key(|&(trait_def_id, _)| tcx.def_path_hash(trait_def_id)); - - let all_impls: Vec<_> = all_impls + let trait_impls: Vec<_> = trait_impls .into_iter() - .map(|(trait_def_id, mut impls)| { - // Bring everything into deterministic order for hashing - impls.sort_by_cached_key(|&(index, _)| { - tcx.hir().def_path_hash(LocalDefId { local_def_index: index }) - }); - - TraitImpls { - trait_id: (trait_def_id.krate.as_u32(), trait_def_id.index), - impls: self.lazy_array(&impls), - } + .map(|(trait_def_id, impls)| TraitImpls { + trait_id: (trait_def_id.krate.as_u32(), trait_def_id.index), + impls: self.lazy_array(&impls), }) .collect(); - self.lazy_array(&all_impls) + self.lazy_array(&trait_impls) } #[instrument(level = "debug", skip(self))] fn encode_incoherent_impls(&mut self) -> LazyArray<IncoherentImpls> { empty_proc_macro!(self); let tcx = self.tcx; - let all_impls = tcx.with_stable_hashing_context(|hcx| { - tcx.crate_inherent_impls(()).unwrap().incoherent_impls.to_sorted(&hcx, true) - }); - let all_impls: Vec<_> = all_impls - .into_iter() - .map(|(&simp, impls)| { - let mut impls: Vec<_> = - impls.into_iter().map(|def_id| def_id.local_def_index).collect(); - impls.sort_by_cached_key(|&local_def_index| { - tcx.hir().def_path_hash(LocalDefId { local_def_index }) - }); - - IncoherentImpls { self_ty: simp, impls: self.lazy_array(impls) } + let all_impls: Vec<_> = tcx + .crate_inherent_impls(()) + .unwrap() + .incoherent_impls + .iter() + .map(|(&simp, impls)| IncoherentImpls { + self_ty: simp, + impls: self.lazy_array(impls.iter().map(|def_id| def_id.local_def_index)), }) .collect(); @@ -2317,32 +2306,6 @@ pub fn provide(providers: &mut Providers) { span_bug!(tcx.def_span(def_id), "no traits in scope for a doc link") }) }, - traits: |tcx, LocalCrate| { - let mut traits = Vec::new(); - for id in tcx.hir().items() { - if matches!(tcx.def_kind(id.owner_id), DefKind::Trait | DefKind::TraitAlias) { - traits.push(id.owner_id.to_def_id()) - } - } - - // Bring everything into deterministic order. - traits.sort_by_cached_key(|&def_id| tcx.def_path_hash(def_id)); - tcx.arena.alloc_slice(&traits) - }, - trait_impls_in_crate: |tcx, LocalCrate| { - let mut trait_impls = Vec::new(); - for id in tcx.hir().items() { - if matches!(tcx.def_kind(id.owner_id), DefKind::Impl { .. }) - && tcx.impl_trait_ref(id.owner_id).is_some() - { - trait_impls.push(id.owner_id.to_def_id()) - } - } - - // Bring everything into deterministic order. - trait_impls.sort_by_cached_key(|&def_id| tcx.def_path_hash(def_id)); - tcx.arena.alloc_slice(&trait_impls) - }, ..*providers } diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 2a44b3423ae..e565c8c1ea1 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -442,6 +442,7 @@ define_tables! { coerce_unsized_info: Table<DefIndex, LazyValue<ty::adjustment::CoerceUnsizedInfo>>, mir_const_qualif: Table<DefIndex, LazyValue<mir::ConstQualifs>>, rendered_const: Table<DefIndex, LazyValue<String>>, + rendered_precise_capturing_args: Table<DefIndex, LazyArray<Symbol>>, asyncness: Table<DefIndex, ty::Asyncness>, fn_arg_names: Table<DefIndex, LazyArray<Ident>>, coroutine_kind: Table<DefIndex, hir::CoroutineKind>, diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index bf10a71dbae..75dc685a16a 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -103,6 +103,10 @@ declare_hooks! { /// Create a list-like THIR representation for debugging. hook thir_flat(key: LocalDefId) -> String; + + /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we + /// can just link to the upstream crate and therefore don't need a mono item. + hook should_codegen_locally(instance: crate::ty::Instance<'tcx>) -> bool; } #[cold] diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index d1ccd158cf9..b113e81bd2d 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -112,7 +112,7 @@ pub fn report_unstable( ) { let msg = match reason { Some(r) => format!("use of unstable library feature '{feature}': {r}"), - None => format!("use of unstable library feature '{}'", &feature), + None => format!("use of unstable library feature '{feature}'"), }; if is_soft { diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index bdd1eb11a38..15febfa7d9c 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -12,15 +12,13 @@ use std::fmt; use std::io; use std::io::{Read, Write}; use std::num::NonZero; -use std::sync::atomic::{AtomicU32, Ordering}; -use smallvec::{smallvec, SmallVec}; use tracing::{debug, trace}; use rustc_ast::LitKind; use rustc_attr::InlineAttr; use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::sync::{HashMapExt, Lock}; +use rustc_data_structures::sync::Lock; use rustc_errors::ErrorGuaranteed; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable}; @@ -159,14 +157,9 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>( } } -// Used to avoid infinite recursion when decoding cyclic allocations. -type DecodingSessionId = NonZero<u32>; - #[derive(Clone)] enum State { Empty, - InProgressNonAlloc(SmallVec<[DecodingSessionId; 1]>), - InProgress(SmallVec<[DecodingSessionId; 1]>, AllocId), Done(AllocId), } @@ -180,13 +173,7 @@ pub struct AllocDecodingState { impl AllocDecodingState { #[inline] pub fn new_decoding_session(&self) -> AllocDecodingSession<'_> { - static DECODER_SESSION_ID: AtomicU32 = AtomicU32::new(0); - let counter = DECODER_SESSION_ID.fetch_add(1, Ordering::SeqCst); - - // Make sure this is never zero. - let session_id = DecodingSessionId::new((counter & 0x7FFFFFFF) + 1).unwrap(); - - AllocDecodingSession { state: self, session_id } + AllocDecodingSession { state: self } } pub fn new(data_offsets: Vec<u64>) -> Self { @@ -200,7 +187,6 @@ impl AllocDecodingState { #[derive(Copy, Clone)] pub struct AllocDecodingSession<'s> { state: &'s AllocDecodingState, - session_id: DecodingSessionId, } impl<'s> AllocDecodingSession<'s> { @@ -220,70 +206,35 @@ impl<'s> AllocDecodingSession<'s> { (alloc_kind, decoder.position()) }); + // We are going to hold this lock during the entire decoding of this allocation, which may + // require that we decode other allocations. This cannot deadlock for two reasons: + // + // At the time of writing, it is only possible to create an allocation that contains a pointer + // to itself using the const_allocate intrinsic (which is for testing only), and even attempting + // to evaluate such consts blows the stack. If we ever grow a mechanism for producing + // cyclic allocations, we will need a new strategy for decoding that doesn't bring back + // https://github.com/rust-lang/rust/issues/126741. + // + // It is also impossible to create two allocations (call them A and B) where A is a pointer to B, and B + // is a pointer to A, because attempting to evaluate either of those consts will produce a + // query cycle, failing compilation. + let mut entry = self.state.decoding_state[idx].lock(); // Check the decoding state to see if it's already decoded or if we should // decode it here. - let alloc_id = { - let mut entry = self.state.decoding_state[idx].lock(); - - match *entry { - State::Done(alloc_id) => { - return alloc_id; - } - ref mut entry @ State::Empty => { - // We are allowed to decode. - match alloc_kind { - AllocDiscriminant::Alloc => { - // If this is an allocation, we need to reserve an - // `AllocId` so we can decode cyclic graphs. - let alloc_id = decoder.interner().reserve_alloc_id(); - *entry = State::InProgress(smallvec![self.session_id], alloc_id); - Some(alloc_id) - } - AllocDiscriminant::Fn - | AllocDiscriminant::Static - | AllocDiscriminant::VTable => { - // Fns and statics cannot be cyclic, and their `AllocId` - // is determined later by interning. - *entry = State::InProgressNonAlloc(smallvec![self.session_id]); - None - } - } - } - State::InProgressNonAlloc(ref mut sessions) => { - if sessions.contains(&self.session_id) { - bug!("this should be unreachable"); - } else { - // Start decoding concurrently. - sessions.push(self.session_id); - None - } - } - State::InProgress(ref mut sessions, alloc_id) => { - if sessions.contains(&self.session_id) { - // Don't recurse. - return alloc_id; - } else { - // Start decoding concurrently. - sessions.push(self.session_id); - Some(alloc_id) - } - } - } - }; + if let State::Done(alloc_id) = *entry { + return alloc_id; + } // Now decode the actual data. let alloc_id = decoder.with_position(pos, |decoder| { match alloc_kind { AllocDiscriminant::Alloc => { + trace!("creating memory alloc ID"); let alloc = <ConstAllocation<'tcx> as Decodable<_>>::decode(decoder); - // We already have a reserved `AllocId`. - let alloc_id = alloc_id.unwrap(); - trace!("decoded alloc {:?}: {:#?}", alloc_id, alloc); - decoder.interner().set_alloc_id_same_memory(alloc_id, alloc); - alloc_id + trace!("decoded alloc {:?}", alloc); + decoder.interner().reserve_and_set_memory_alloc(alloc) } AllocDiscriminant::Fn => { - assert!(alloc_id.is_none()); trace!("creating fn alloc ID"); let instance = ty::Instance::decode(decoder); trace!("decoded fn alloc instance: {:?}", instance); @@ -291,35 +242,26 @@ impl<'s> AllocDecodingSession<'s> { // Here we cannot call `reserve_and_set_fn_alloc` as that would use a query, which // is not possible in this context. That's why the allocation stores // whether it is unique or not. - let alloc_id = - decoder.interner().reserve_and_set_fn_alloc_internal(instance, unique); - alloc_id + decoder.interner().reserve_and_set_fn_alloc_internal(instance, unique) } AllocDiscriminant::VTable => { - assert!(alloc_id.is_none()); trace!("creating vtable alloc ID"); let ty = <Ty<'_> as Decodable<D>>::decode(decoder); let poly_trait_ref = <Option<ty::PolyExistentialTraitRef<'_>> as Decodable<D>>::decode(decoder); trace!("decoded vtable alloc instance: {ty:?}, {poly_trait_ref:?}"); - let alloc_id = - decoder.interner().reserve_and_set_vtable_alloc(ty, poly_trait_ref); - alloc_id + decoder.interner().reserve_and_set_vtable_alloc(ty, poly_trait_ref) } AllocDiscriminant::Static => { - assert!(alloc_id.is_none()); trace!("creating extern static alloc ID"); let did = <DefId as Decodable<D>>::decode(decoder); trace!("decoded static def-ID: {:?}", did); - let alloc_id = decoder.interner().reserve_and_set_static_alloc(did); - alloc_id + decoder.interner().reserve_and_set_static_alloc(did) } } }); - self.state.decoding_state[idx].with_lock(|entry| { - *entry = State::Done(alloc_id); - }); + *entry = State::Done(alloc_id); alloc_id } @@ -563,12 +505,6 @@ impl<'tcx> TyCtxt<'tcx> { bug!("tried to set allocation ID {id:?}, but it was already existing as {old:#?}"); } } - - /// Freezes an `AllocId` created with `reserve` by pointing it at an `Allocation`. May be called - /// twice for the same `(AllocId, Allocation)` pair. - fn set_alloc_id_same_memory(self, id: AllocId, mem: ConstAllocation<'tcx>) { - self.alloc_map.lock().alloc_map.insert_same(id, GlobalAlloc::Memory(mem)); - } } //////////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 817c7157b68..c7ea1d43383 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1261,6 +1261,7 @@ rustc_queries! { desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } separate_provide_extern } + /// Gets the rendered value of the specified constant or associated constant. /// Used by rustdoc. query rendered_const(def_id: DefId) -> &'tcx String { @@ -1268,6 +1269,13 @@ rustc_queries! { desc { |tcx| "rendering constant initializer of `{}`", tcx.def_path_str(def_id) } separate_provide_extern } + + /// Gets the rendered precise capturing args for an opaque for use in rustdoc. + query rendered_precise_capturing_args(def_id: DefId) -> Option<&'tcx [Symbol]> { + desc { |tcx| "rendering precise capturing args for `{}`", tcx.def_path_str(def_id) } + separate_provide_extern + } + query impl_parent(def_id: DefId) -> Option<DefId> { desc { |tcx| "computing specialization parent impl of `{}`", tcx.def_path_str(def_id) } separate_provide_extern diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index bd073cd891f..558590af7ec 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -40,7 +40,6 @@ use rustc_data_structures::intern::Interned; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::tagged_ptr::CopyTaggedPtr; -use rustc_data_structures::unord::UnordMap; use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; @@ -2083,6 +2082,8 @@ pub fn provide(providers: &mut Providers) { *providers = Providers { trait_impls_of: trait_def::trait_impls_of_provider, incoherent_impls: trait_def::incoherent_impls_provider, + trait_impls_in_crate: trait_def::trait_impls_in_crate_provider, + traits: trait_def::traits_provider, const_param_default: consts::const_param_default, vtable_allocation: vtable::vtable_allocation_provider, ..*providers @@ -2096,8 +2097,8 @@ pub fn provide(providers: &mut Providers) { /// (constructing this map requires touching the entire crate). #[derive(Clone, Debug, Default, HashStable)] pub struct CrateInherentImpls { - pub inherent_impls: LocalDefIdMap<Vec<DefId>>, - pub incoherent_impls: UnordMap<SimplifiedType, Vec<LocalDefId>>, + pub inherent_impls: FxIndexMap<LocalDefId, Vec<DefId>>, + pub incoherent_impls: FxIndexMap<SimplifiedType, Vec<LocalDefId>>, } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, HashStable)] diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 57cd2dc73c4..0e241663e18 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2627,7 +2627,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> { self.prepare_region_info(value); } - debug!("self.used_region_names: {:?}", &self.used_region_names); + debug!("self.used_region_names: {:?}", self.used_region_names); let mut empty = true; let mut start_or_continue = |cx: &mut Self, start: &str, cont: &str| { diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index da5860043c9..3bd9f6ad11b 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -1,16 +1,19 @@ -use crate::traits::specialization_graph; -use crate::ty::fast_reject::{self, SimplifiedType, TreatParams}; -use crate::ty::{Ident, Ty, TyCtxt}; -use hir::def_id::LOCAL_CRATE; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; use std::iter; use tracing::debug; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; +use rustc_hir as hir; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::DefId; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_macros::{Decodable, Encodable, HashStable}; +use crate::query::LocalCrate; +use crate::traits::specialization_graph; +use crate::ty::fast_reject::{self, SimplifiedType, TreatParams}; +use crate::ty::{Ident, Ty, TyCtxt}; + /// A trait's definition with type information. #[derive(HashStable, Encodable, Decodable)] pub struct TraitDef { @@ -274,3 +277,27 @@ pub(super) fn incoherent_impls_provider( Ok(tcx.arena.alloc_slice(&impls)) } + +pub(super) fn traits_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> &[DefId] { + let mut traits = Vec::new(); + for id in tcx.hir().items() { + if matches!(tcx.def_kind(id.owner_id), DefKind::Trait | DefKind::TraitAlias) { + traits.push(id.owner_id.to_def_id()) + } + } + + tcx.arena.alloc_slice(&traits) +} + +pub(super) fn trait_impls_in_crate_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> &[DefId] { + let mut trait_impls = Vec::new(); + for id in tcx.hir().items() { + if matches!(tcx.def_kind(id.owner_id), DefKind::Impl { .. }) + && tcx.impl_trait_ref(id.owner_id).is_some() + { + trait_impls.push(id.owner_id.to_def_id()) + } + } + + tcx.arena.alloc_slice(&trait_impls) +} diff --git a/compiler/rustc_middle/src/util/find_self_call.rs b/compiler/rustc_middle/src/util/find_self_call.rs index 027e2703e98..831853b0b48 100644 --- a/compiler/rustc_middle/src/util/find_self_call.rs +++ b/compiler/rustc_middle/src/util/find_self_call.rs @@ -14,7 +14,7 @@ pub fn find_self_call<'tcx>( local: Local, block: BasicBlock, ) -> Option<(DefId, GenericArgsRef<'tcx>)> { - debug!("find_self_call(local={:?}): terminator={:?}", local, &body[block].terminator); + debug!("find_self_call(local={:?}): terminator={:?}", local, body[block].terminator); if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) = &body[block].terminator { diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 281f3ef6ef3..dda4debecec 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -325,9 +325,16 @@ mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = mir_build_union_pattern = cannot use unions in constant patterns +mir_build_unreachable_making_this_unreachable = collectively making this unreachable + +mir_build_unreachable_matches_same_values = matches some of the same values + mir_build_unreachable_pattern = unreachable pattern .label = unreachable pattern - .catchall_label = matches any value + .unreachable_matches_no_values = this pattern matches no values because `{$ty}` is uninhabited + .unreachable_covered_by_catchall = matches any value + .unreachable_covered_by_one = matches all the values already + .unreachable_covered_by_many = these patterns collectively make the last one unreachable mir_build_unsafe_fn_safe_body = an unsafe function restricts its caller, but its body is safe by default mir_build_unsafe_not_inherited = items do not inherit unsafety from separate enclosing items diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index b531a392efa..95bc8b3d0cb 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -1598,6 +1598,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for subcandidate in candidate.subcandidates.iter_mut() { expanded_candidates.push(subcandidate); } + // Note that the subcandidates have been added to `expanded_candidates`, + // but `candidate` itself has not. If the last candidate has more match pairs, + // they are handled separately by `test_remaining_match_pairs_after_or`. } else { // A candidate that doesn't start with an or-pattern has nothing to // expand, so it is included in the post-expansion list as-is. @@ -1613,19 +1616,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expanded_candidates.as_mut_slice(), ); - // Simplify subcandidates and process any leftover match pairs. - for candidate in candidates_to_expand { + // Postprocess subcandidates, and process any leftover match pairs. + // (Only the last candidate can possibly have more match pairs.) + debug_assert!({ + let mut all_except_last = candidates_to_expand.iter().rev().skip(1); + all_except_last.all(|candidate| candidate.match_pairs.is_empty()) + }); + for candidate in candidates_to_expand.iter_mut() { if !candidate.subcandidates.is_empty() { - self.finalize_or_candidate(span, scrutinee_span, candidate); + self.merge_trivial_subcandidates(candidate); + self.remove_never_subcandidates(candidate); } } + if let Some(last_candidate) = candidates_to_expand.last_mut() { + self.test_remaining_match_pairs_after_or(span, scrutinee_span, last_candidate); + } remainder_start.and(remaining_candidates) } /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new - /// subcandidate. Any candidate that has been expanded that way should be passed to - /// `finalize_or_candidate` after its subcandidates have been processed. + /// subcandidate. Any candidate that has been expanded this way should also be postprocessed + /// at the end of [`Self::expand_and_match_or_candidates`]. fn create_or_subcandidates<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, @@ -1642,7 +1654,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate.subcandidates[0].false_edge_start_block = candidate.false_edge_start_block; } - /// Simplify subcandidates and process any leftover match pairs. The candidate should have been + /// Try to merge all of the subcandidates of the given candidate into one. This avoids + /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The candidate should have been /// expanded with `create_or_subcandidates`. /// /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like @@ -1695,105 +1708,130 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// | /// ... /// ``` - fn finalize_or_candidate( - &mut self, - span: Span, - scrutinee_span: Span, - candidate: &mut Candidate<'_, 'tcx>, - ) { - if candidate.subcandidates.is_empty() { + /// + /// Note that this takes place _after_ the subcandidates have participated + /// in match tree lowering. + fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { + assert!(!candidate.subcandidates.is_empty()); + if candidate.has_guard { + // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. return; } - self.merge_trivial_subcandidates(candidate); + // FIXME(or_patterns; matthewjasper) Try to be more aggressive here. + let can_merge = candidate.subcandidates.iter().all(|subcandidate| { + subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty() + }); + if !can_merge { + return; + } - if !candidate.match_pairs.is_empty() { - let or_span = candidate.or_span.unwrap_or(candidate.extra_data.span); - let source_info = self.source_info(or_span); - // If more match pairs remain, test them after each subcandidate. - // We could add them to the or-candidates before the call to `test_or_pattern` but this - // would make it impossible to detect simplifiable or-patterns. That would guarantee - // exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`. - let mut last_otherwise = None; - candidate.visit_leaves(|leaf_candidate| { - last_otherwise = leaf_candidate.otherwise_block; - }); - let remaining_match_pairs = mem::take(&mut candidate.match_pairs); - candidate.visit_leaves(|leaf_candidate| { - assert!(leaf_candidate.match_pairs.is_empty()); - leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned()); - let or_start = leaf_candidate.pre_binding_block.unwrap(); - let otherwise = - self.match_candidates(span, scrutinee_span, or_start, &mut [leaf_candidate]); - // In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q, - // R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching - // directly to `last_otherwise`. If there is a guard, - // `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we - // can't skip `Q`. - let or_otherwise = if leaf_candidate.has_guard { - leaf_candidate.otherwise_block.unwrap() - } else { - last_otherwise.unwrap() - }; - self.cfg.goto(otherwise, source_info, or_otherwise); - }); + let mut last_otherwise = None; + let shared_pre_binding_block = self.cfg.start_new_block(); + // This candidate is about to become a leaf, so unset `or_span`. + let or_span = candidate.or_span.take().unwrap(); + let source_info = self.source_info(or_span); + + if candidate.false_edge_start_block.is_none() { + candidate.false_edge_start_block = candidate.subcandidates[0].false_edge_start_block; + } + + // Remove the (known-trivial) subcandidates from the candidate tree, + // so that they aren't visible after match tree lowering, and wire them + // all to join up at a single shared pre-binding block. + // (Note that the subcandidates have already had their part of the match + // tree lowered by this point, which is why we can add a goto to them.) + for subcandidate in mem::take(&mut candidate.subcandidates) { + let subcandidate_block = subcandidate.pre_binding_block.unwrap(); + self.cfg.goto(subcandidate_block, source_info, shared_pre_binding_block); + last_otherwise = subcandidate.otherwise_block; } + candidate.pre_binding_block = Some(shared_pre_binding_block); + assert!(last_otherwise.is_some()); + candidate.otherwise_block = last_otherwise; } - /// Try to merge all of the subcandidates of the given candidate into one. This avoids - /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The candidate should have been - /// expanded with `create_or_subcandidates`. - fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { - if candidate.subcandidates.is_empty() || candidate.has_guard { - // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. + /// Never subcandidates may have a set of bindings inconsistent with their siblings, + /// which would break later code. So we filter them out. Note that we can't filter out + /// top-level candidates this way. + fn remove_never_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) { + if candidate.subcandidates.is_empty() { return; } - // FIXME(or_patterns; matthewjasper) Try to be more aggressive here. - let can_merge = candidate.subcandidates.iter().all(|subcandidate| { - subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty() - }); - if can_merge { - let mut last_otherwise = None; - let any_matches = self.cfg.start_new_block(); - let or_span = candidate.or_span.take().unwrap(); - let source_info = self.source_info(or_span); - if candidate.false_edge_start_block.is_none() { - candidate.false_edge_start_block = - candidate.subcandidates[0].false_edge_start_block; - } - for subcandidate in mem::take(&mut candidate.subcandidates) { - let or_block = subcandidate.pre_binding_block.unwrap(); - self.cfg.goto(or_block, source_info, any_matches); - last_otherwise = subcandidate.otherwise_block; - } - candidate.pre_binding_block = Some(any_matches); - assert!(last_otherwise.is_some()); - candidate.otherwise_block = last_otherwise; - } else { - // Never subcandidates may have a set of bindings inconsistent with their siblings, - // which would break later code. So we filter them out. Note that we can't filter out - // top-level candidates this way. - candidate.subcandidates.retain_mut(|candidate| { - if candidate.extra_data.is_never { - candidate.visit_leaves(|subcandidate| { - let block = subcandidate.pre_binding_block.unwrap(); - // That block is already unreachable but needs a terminator to make the MIR well-formed. - let source_info = self.source_info(subcandidate.extra_data.span); - self.cfg.terminate(block, source_info, TerminatorKind::Unreachable); - }); - false - } else { - true - } - }); - if candidate.subcandidates.is_empty() { - // If `candidate` has become a leaf candidate, ensure it has a `pre_binding_block`. - candidate.pre_binding_block = Some(self.cfg.start_new_block()); + candidate.subcandidates.retain_mut(|candidate| { + if candidate.extra_data.is_never { + candidate.visit_leaves(|subcandidate| { + let block = subcandidate.pre_binding_block.unwrap(); + // That block is already unreachable but needs a terminator to make the MIR well-formed. + let source_info = self.source_info(subcandidate.extra_data.span); + self.cfg.terminate(block, source_info, TerminatorKind::Unreachable); + }); + false + } else { + true } + }); + if candidate.subcandidates.is_empty() { + // If `candidate` has become a leaf candidate, ensure it has a `pre_binding_block`. + candidate.pre_binding_block = Some(self.cfg.start_new_block()); } } + /// If more match pairs remain, test them after each subcandidate. + /// We could have added them to the or-candidates during or-pattern expansion, but that + /// would make it impossible to detect simplifiable or-patterns. That would guarantee + /// exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`. + fn test_remaining_match_pairs_after_or( + &mut self, + span: Span, + scrutinee_span: Span, + candidate: &mut Candidate<'_, 'tcx>, + ) { + if candidate.match_pairs.is_empty() { + return; + } + + let or_span = candidate.or_span.unwrap_or(candidate.extra_data.span); + let source_info = self.source_info(or_span); + let mut last_otherwise = None; + candidate.visit_leaves(|leaf_candidate| { + last_otherwise = leaf_candidate.otherwise_block; + }); + + let remaining_match_pairs = mem::take(&mut candidate.match_pairs); + // We're testing match pairs that remained after an `Or`, so the remaining + // pairs should all be `Or` too, due to the sorting invariant. + debug_assert!( + remaining_match_pairs + .iter() + .all(|match_pair| matches!(match_pair.test_case, TestCase::Or { .. })) + ); + + candidate.visit_leaves(|leaf_candidate| { + // At this point the leaf's own match pairs have all been lowered + // and removed, so `extend` and assignment are equivalent, + // but extending can also recycle any existing vector capacity. + assert!(leaf_candidate.match_pairs.is_empty()); + leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned()); + + let or_start = leaf_candidate.pre_binding_block.unwrap(); + let otherwise = + self.match_candidates(span, scrutinee_span, or_start, &mut [leaf_candidate]); + // In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q, + // R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching + // directly to `last_otherwise`. If there is a guard, + // `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we + // can't skip `Q`. + let or_otherwise = if leaf_candidate.has_guard { + leaf_candidate.otherwise_block.unwrap() + } else { + last_otherwise.unwrap() + }; + self.cfg.goto(otherwise, source_info, or_otherwise); + }); + } + /// Pick a test to run. Which test doesn't matter as long as it is guaranteed to fully match at /// least one match pair. We currently simply pick the test corresponding to the first match /// pair of the first candidate in the list. @@ -2162,92 +2200,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.ascribe_types(block, ascriptions); - // rust-lang/rust#27282: The `autoref` business deserves some - // explanation here. - // - // The intent of the `autoref` flag is that when it is true, - // then any pattern bindings of type T will map to a `&T` - // within the context of the guard expression, but will - // continue to map to a `T` in the context of the arm body. To - // avoid surfacing this distinction in the user source code - // (which would be a severe change to the language and require - // far more revision to the compiler), when `autoref` is true, - // then any occurrence of the identifier in the guard - // expression will automatically get a deref op applied to it. - // - // So an input like: - // - // ``` - // let place = Foo::new(); - // match place { foo if inspect(foo) - // => feed(foo), ... } - // ``` - // - // will be treated as if it were really something like: - // - // ``` - // let place = Foo::new(); - // match place { Foo { .. } if { let tmp1 = &place; inspect(*tmp1) } - // => { let tmp2 = place; feed(tmp2) }, ... } - // ``` - // - // And an input like: - // - // ``` - // let place = Foo::new(); - // match place { ref mut foo if inspect(foo) - // => feed(foo), ... } - // ``` - // - // will be treated as if it were really something like: - // - // ``` - // let place = Foo::new(); - // match place { Foo { .. } if { let tmp1 = & &mut place; inspect(*tmp1) } - // => { let tmp2 = &mut place; feed(tmp2) }, ... } - // ``` - // - // In short, any pattern binding will always look like *some* - // kind of `&T` within the guard at least in terms of how the - // MIR-borrowck views it, and this will ensure that guard - // expressions cannot mutate their the match inputs via such - // bindings. (It also ensures that guard expressions can at - // most *copy* values from such bindings; non-Copy things - // cannot be moved via pattern bindings in guard expressions.) - // - // ---- - // - // Implementation notes (under assumption `autoref` is true). - // - // To encode the distinction above, we must inject the - // temporaries `tmp1` and `tmp2`. - // - // There are two cases of interest: binding by-value, and binding by-ref. - // - // 1. Binding by-value: Things are simple. - // - // * Establishing `tmp1` creates a reference into the - // matched place. This code is emitted by - // bind_matched_candidate_for_guard. - // - // * `tmp2` is only initialized "lazily", after we have - // checked the guard. Thus, the code that can trigger - // moves out of the candidate can only fire after the - // guard evaluated to true. This initialization code is - // emitted by bind_matched_candidate_for_arm. - // - // 2. Binding by-reference: Things are tricky. - // - // * Here, the guard expression wants a `&&` or `&&mut` - // into the original input. This means we need to borrow - // the reference that we create for the arm. - // * So we eagerly create the reference for the arm and then take a - // reference to that. + // Lower an instance of the arm guard (if present) for this candidate, + // and then perform bindings for the arm body. if let Some((arm, match_scope)) = arm_match_scope && let Some(guard) = arm.guard { let tcx = self.tcx; + // Bindings for guards require some extra handling to automatically + // insert implicit references/dereferences. self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone()); let guard_frame = GuardFrame { locals: bindings.clone().map(|b| GuardFrameLocal::new(b.var_id)).collect(), @@ -2387,6 +2348,82 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + /// Binding for guards is a bit different from binding for the arm body, + /// because an extra layer of implicit reference/dereference is added. + /// + /// The idea is that any pattern bindings of type T will map to a `&T` within + /// the context of the guard expression, but will continue to map to a `T` + /// in the context of the arm body. To avoid surfacing this distinction in + /// the user source code (which would be a severe change to the language and + /// require far more revision to the compiler), any occurrence of the + /// identifier in the guard expression will automatically get a deref op + /// applied to it. (See the caller of [`Self::is_bound_var_in_guard`].) + /// + /// So an input like: + /// + /// ```ignore (illustrative) + /// let place = Foo::new(); + /// match place { foo if inspect(foo) + /// => feed(foo), ... } + /// ``` + /// + /// will be treated as if it were really something like: + /// + /// ```ignore (illustrative) + /// let place = Foo::new(); + /// match place { Foo { .. } if { let tmp1 = &place; inspect(*tmp1) } + /// => { let tmp2 = place; feed(tmp2) }, ... } + /// ``` + /// + /// And an input like: + /// + /// ```ignore (illustrative) + /// let place = Foo::new(); + /// match place { ref mut foo if inspect(foo) + /// => feed(foo), ... } + /// ``` + /// + /// will be treated as if it were really something like: + /// + /// ```ignore (illustrative) + /// let place = Foo::new(); + /// match place { Foo { .. } if { let tmp1 = & &mut place; inspect(*tmp1) } + /// => { let tmp2 = &mut place; feed(tmp2) }, ... } + /// ``` + /// --- + /// + /// ## Implementation notes + /// + /// To encode the distinction above, we must inject the + /// temporaries `tmp1` and `tmp2`. + /// + /// There are two cases of interest: binding by-value, and binding by-ref. + /// + /// 1. Binding by-value: Things are simple. + /// + /// * Establishing `tmp1` creates a reference into the + /// matched place. This code is emitted by + /// [`Self::bind_matched_candidate_for_guard`]. + /// + /// * `tmp2` is only initialized "lazily", after we have + /// checked the guard. Thus, the code that can trigger + /// moves out of the candidate can only fire after the + /// guard evaluated to true. This initialization code is + /// emitted by [`Self::bind_matched_candidate_for_arm_body`]. + /// + /// 2. Binding by-reference: Things are tricky. + /// + /// * Here, the guard expression wants a `&&` or `&&mut` + /// into the original input. This means we need to borrow + /// the reference that we create for the arm. + /// * So we eagerly create the reference for the arm and then take a + /// reference to that. + /// + /// --- + /// + /// See these PRs for some historical context: + /// - <https://github.com/rust-lang/rust/pull/49870> (introduction of autoref) + /// - <https://github.com/rust-lang/rust/pull/59114> (always use autoref) fn bind_matched_candidate_for_guard<'b>( &mut self, block: BasicBlock, @@ -2418,10 +2455,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); match binding.binding_mode.0 { ByRef::No => { + // The arm binding will be by value, so for the guard binding + // just take a shared reference to the matched place. let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source); self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); } ByRef::Yes(mutbl) => { + // The arm binding will be by reference, so eagerly create it now. let value_for_arm = self.storage_live_binding( block, binding.var_id, @@ -2433,6 +2473,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let rvalue = Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source); self.cfg.push_assign(block, source_info, value_for_arm, rvalue); + // For the guard binding, take a shared reference to that reference. let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm); self.cfg.push_assign(block, source_info, ref_for_guard, rvalue); } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index a65586ccdb7..6309f2ac98e 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -10,7 +10,7 @@ use rustc_middle::thir::visit::Visitor; use rustc_middle::thir::*; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt}; -use rustc_session::lint::builtin::{DEPRECATED_SAFE, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; +use rustc_session::lint::builtin::{DEPRECATED_SAFE_2024, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; use rustc_session::lint::Level; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::symbol::Symbol; @@ -99,7 +99,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { { let sm = self.tcx.sess.source_map(); self.tcx.emit_node_span_lint( - DEPRECATED_SAFE, + DEPRECATED_SAFE_2024, self.hir_context, span, CallToDeprecatedSafeFnRequiresUnsafe { @@ -466,6 +466,24 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } } } + ExprKind::AddressOf { arg, .. } => { + if let ExprKind::Scope { value: arg, .. } = self.thir[arg].kind + // THIR desugars UNSAFE_STATIC into *UNSAFE_STATIC_REF, where + // UNSAFE_STATIC_REF holds the addr of the UNSAFE_STATIC, so: take two steps + && let ExprKind::Deref { arg } = self.thir[arg].kind + // FIXME(workingjubiee): we lack a clear reason to reject ThreadLocalRef here, + // but we also have no conclusive reason to allow it either! + && let ExprKind::StaticRef { .. } = self.thir[arg].kind + { + // A raw ref to a place expr, even an "unsafe static", is okay! + // We short-circuit to not recursively traverse this expression. + return; + // note: const_mut_refs enables this code, and it currently remains unsafe: + // static mut BYTE: u8 = 0; + // static mut BYTE_PTR: *mut u8 = unsafe { addr_of_mut!(BYTE) }; + // static mut DEREF_BYTE_PTR: *mut u8 = unsafe { addr_of_mut!(*BYTE_PTR) }; + } + } ExprKind::Deref { arg } => { if let ExprKind::StaticRef { def_id, .. } | ExprKind::ThreadLocalRef(def_id) = self.thir[arg].kind diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index f6f443b64a6..bdc4b0ea97d 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -582,11 +582,23 @@ pub(crate) struct NonConstPath { #[derive(LintDiagnostic)] #[diag(mir_build_unreachable_pattern)] -pub(crate) struct UnreachablePattern { +pub(crate) struct UnreachablePattern<'tcx> { #[label] pub(crate) span: Option<Span>, - #[label(mir_build_catchall_label)] - pub(crate) catchall: Option<Span>, + #[subdiagnostic] + pub(crate) matches_no_values: Option<UnreachableMatchesNoValues<'tcx>>, + #[label(mir_build_unreachable_covered_by_catchall)] + pub(crate) covered_by_catchall: Option<Span>, + #[label(mir_build_unreachable_covered_by_one)] + pub(crate) covered_by_one: Option<Span>, + #[note(mir_build_unreachable_covered_by_many)] + pub(crate) covered_by_many: Option<MultiSpan>, +} + +#[derive(Subdiagnostic)] +#[note(mir_build_unreachable_matches_no_values)] +pub(crate) struct UnreachableMatchesNoValues<'tcx> { + pub(crate) ty: Ty<'tcx>, } #[derive(Diagnostic)] diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 28f9300b97a..99afb500e0b 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -939,9 +939,11 @@ impl<'tcx> Cx<'tcx> { } } - // We encode uses of statics as a `*&STATIC` where the `&STATIC` part is - // a constant reference (or constant raw pointer for `static mut`) in MIR + // A source Rust `path::to::STATIC` is a place expr like *&ident is. + // In THIR, we make them exactly equivalent by inserting the implied *& or *&raw, + // but distinguish between &STATIC and &THREAD_LOCAL as they have different semantics Res::Def(DefKind::Static { .. }, id) => { + // this is &raw for extern static or static mut, and & for other statics let ty = self.tcx.static_ptr_ty(id); let temp_lifetime = self .rvalue_scopes diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 70065b5a2c3..5e904057e73 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1,4 +1,5 @@ use crate::errors::*; +use crate::fluent_generated as fluent; use rustc_arena::{DroplessArena, TypedArena}; use rustc_ast::Mutability; @@ -16,8 +17,8 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; use rustc_pattern_analysis::errors::Uncovered; use rustc_pattern_analysis::rustc::{ - Constructor, DeconstructedPat, MatchArm, RustcPatCtxt as PatCtxt, Usefulness, UsefulnessReport, - WitnessPat, + Constructor, DeconstructedPat, MatchArm, RedundancyExplanation, RevealedTy, + RustcPatCtxt as PatCtxt, Usefulness, UsefulnessReport, WitnessPat, }; use rustc_session::lint::builtin::{ BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS, @@ -391,12 +392,16 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { ) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> { let pattern_complexity_limit = get_limit_size(cx.tcx.hir().krate_attrs(), cx.tcx.sess, sym::pattern_complexity); - let report = - rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty, pattern_complexity_limit) - .map_err(|err| { - self.error = Err(err); - err - })?; + let report = rustc_pattern_analysis::rustc::analyze_match( + &cx, + &arms, + scrut_ty, + pattern_complexity_limit, + ) + .map_err(|err| { + self.error = Err(err); + err + })?; // Warn unreachable subpatterns. for (arm, is_useful) in report.arm_usefulness.iter() { @@ -405,9 +410,9 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { { let mut redundant_subpats = redundant_subpats.clone(); // Emit lints in the order in which they occur in the file. - redundant_subpats.sort_unstable_by_key(|pat| pat.data().span); - for pat in redundant_subpats { - report_unreachable_pattern(cx, arm.arm_data, pat.data().span, None) + redundant_subpats.sort_unstable_by_key(|(pat, _)| pat.data().span); + for (pat, explanation) in redundant_subpats { + report_unreachable_pattern(cx, arm.arm_data, pat, &explanation) } } } @@ -906,26 +911,60 @@ fn report_irrefutable_let_patterns( fn report_unreachable_pattern<'p, 'tcx>( cx: &PatCtxt<'p, 'tcx>, hir_id: HirId, - span: Span, - catchall: Option<Span>, + pat: &DeconstructedPat<'p, 'tcx>, + explanation: &RedundancyExplanation<'p, 'tcx>, ) { - cx.tcx.emit_node_span_lint( - UNREACHABLE_PATTERNS, - hir_id, - span, - UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall }, - ); + let pat_span = pat.data().span; + let mut lint = UnreachablePattern { + span: Some(pat_span), + matches_no_values: None, + covered_by_catchall: None, + covered_by_one: None, + covered_by_many: None, + }; + match explanation.covered_by.as_slice() { + [] => { + // Empty pattern; we report the uninhabited type that caused the emptiness. + lint.span = None; // Don't label the pattern itself + pat.walk(&mut |subpat| { + let ty = **subpat.ty(); + if cx.is_uninhabited(ty) { + lint.matches_no_values = Some(UnreachableMatchesNoValues { ty }); + false // No need to dig further. + } else if matches!(subpat.ctor(), Constructor::Ref | Constructor::UnionField) { + false // Don't explore further since they are not by-value. + } else { + true + } + }); + } + [covering_pat] if pat_is_catchall(covering_pat) => { + lint.covered_by_catchall = Some(covering_pat.data().span); + } + [covering_pat] => { + lint.covered_by_one = Some(covering_pat.data().span); + } + covering_pats => { + let mut multispan = MultiSpan::from_span(pat_span); + for p in covering_pats { + multispan.push_span_label( + p.data().span, + fluent::mir_build_unreachable_matches_same_values, + ); + } + multispan + .push_span_label(pat_span, fluent::mir_build_unreachable_making_this_unreachable); + lint.covered_by_many = Some(multispan); + } + } + cx.tcx.emit_node_span_lint(UNREACHABLE_PATTERNS, hir_id, pat_span, lint); } /// Report unreachable arms, if any. fn report_arm_reachability<'p, 'tcx>(cx: &PatCtxt<'p, 'tcx>, report: &UsefulnessReport<'p, 'tcx>) { - let mut catchall = None; for (arm, is_useful) in report.arm_usefulness.iter() { - if matches!(is_useful, Usefulness::Redundant) { - report_unreachable_pattern(cx, arm.arm_data, arm.pat.data().span, catchall) - } - if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) { - catchall = Some(arm.pat.data().span); + if let Usefulness::Redundant(explanation) = is_useful { + report_unreachable_pattern(cx, arm.arm_data, arm.pat, explanation) } } } @@ -998,27 +1037,31 @@ fn report_non_exhaustive_match<'p, 'tcx>( err.note(format!("the matched value is of type `{}`", scrut_ty)); if !is_empty_match { - let mut non_exhaustive_tys = FxIndexSet::default(); + let mut special_tys = FxIndexSet::default(); // Look at the first witness. - collect_non_exhaustive_tys(cx, &witnesses[0], &mut non_exhaustive_tys); + collect_special_tys(cx, &witnesses[0], &mut special_tys); - for ty in non_exhaustive_tys { + for ty in special_tys { if ty.is_ptr_sized_integral() { - if ty == cx.tcx.types.usize { + if ty.inner() == cx.tcx.types.usize { err.note(format!( "`{ty}` does not have a fixed maximum value, so half-open ranges are necessary to match \ exhaustively", )); - } else if ty == cx.tcx.types.isize { + } else if ty.inner() == cx.tcx.types.isize { err.note(format!( "`{ty}` does not have fixed minimum and maximum values, so half-open ranges are necessary to match \ exhaustively", )); } - } else if ty == cx.tcx.types.str_ { + } else if ty.inner() == cx.tcx.types.str_ { err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary"); - } else if cx.is_foreign_non_exhaustive_enum(cx.reveal_opaque_ty(ty)) { + } else if cx.is_foreign_non_exhaustive_enum(ty) { err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively")); + } else if cx.is_uninhabited(ty.inner()) && cx.tcx.features().min_exhaustive_patterns { + // The type is uninhabited yet there is a witness: we must be in the `MaybeInvalid` + // case. + err.note(format!("`{ty}` is uninhabited but is not being matched by value, so a wildcard `_` is required")); } } } @@ -1168,22 +1211,22 @@ fn joined_uncovered_patterns<'p, 'tcx>( } } -fn collect_non_exhaustive_tys<'tcx>( +/// Collect types that require specific explanations when they show up in witnesses. +fn collect_special_tys<'tcx>( cx: &PatCtxt<'_, 'tcx>, pat: &WitnessPat<'_, 'tcx>, - non_exhaustive_tys: &mut FxIndexSet<Ty<'tcx>>, + special_tys: &mut FxIndexSet<RevealedTy<'tcx>>, ) { - if matches!(pat.ctor(), Constructor::NonExhaustive) { - non_exhaustive_tys.insert(pat.ty().inner()); + if matches!(pat.ctor(), Constructor::NonExhaustive | Constructor::Never) { + special_tys.insert(*pat.ty()); } if let Constructor::IntRange(range) = pat.ctor() { if cx.is_range_beyond_boundaries(range, *pat.ty()) { // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`. - non_exhaustive_tys.insert(pat.ty().inner()); + special_tys.insert(*pat.ty()); } } - pat.iter_fields() - .for_each(|field_pat| collect_non_exhaustive_tys(cx, field_pat, non_exhaustive_tys)) + pat.iter_fields().for_each(|field_pat| collect_special_tys(cx, field_pat, special_tys)) } fn report_adt_defined_here<'tcx>( diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index a9bceeccdce..d44da42416d 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -688,7 +688,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, '_, 'tcx> { let init_loc_map = &move_data.init_loc_map; let rev_lookup = &move_data.rev_lookup; - debug!("initializes move_indexes {:?}", &init_loc_map[location]); + debug!("initializes move_indexes {:?}", init_loc_map[location]); trans.gen_all(init_loc_map[location].iter().copied()); if let mir::StatementKind::StorageDead(local) = stmt.kind { diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 261dcd52d71..658cc4c51a9 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -81,7 +81,7 @@ use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::abi::{FieldIdx, VariantIdx}; use rustc_target::spec::PanicStrategy; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::TyCtxtInferExt as _; use rustc_trait_selection::traits::ObligationCtxt; use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; diff --git a/compiler/rustc_mir_transform/src/dead_store_elimination.rs b/compiler/rustc_mir_transform/src/dead_store_elimination.rs index 08dba1de500..60230bea02e 100644 --- a/compiler/rustc_mir_transform/src/dead_store_elimination.rs +++ b/compiler/rustc_mir_transform/src/dead_store_elimination.rs @@ -102,7 +102,7 @@ pub fn eliminate<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { | StatementKind::Nop => (), StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => { - bug!("{:?} not found in this MIR phase!", &statement.kind) + bug!("{:?} not found in this MIR phase!", statement.kind) } } } diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs index 9edb8bcee6e..40c0c723d25 100644 --- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs +++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs @@ -106,11 +106,11 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch { let parent = BasicBlock::from_usize(i); let Some(opt_data) = evaluate_candidate(tcx, body, parent) else { continue }; - if !tcx.consider_optimizing(|| format!("EarlyOtherwiseBranch {:?}", &opt_data)) { + if !tcx.consider_optimizing(|| format!("EarlyOtherwiseBranch {opt_data:?}")) { break; } - trace!("SUCCESS: found optimization possibility to apply: {:?}", &opt_data); + trace!("SUCCESS: found optimization possibility to apply: {opt_data:?}"); should_cleanup = true; diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index bfd505c0672..3655a677ba0 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -228,6 +228,7 @@ use rustc_middle::ty::{ self, AssocKind, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, VtblEntry, }; +use rustc_middle::util::Providers; use rustc_middle::{bug, span_bug}; use rustc_session::config::EntryFnType; use rustc_session::Limit; @@ -399,7 +400,7 @@ fn collect_items_rec<'tcx>( let instance = Instance::mono(tcx, def_id); // Sanity check whether this ended up being collected accidentally - debug_assert!(should_codegen_locally(tcx, instance)); + debug_assert!(tcx.should_codegen_locally(instance)); let DefKind::Static { nested, .. } = tcx.def_kind(def_id) else { bug!() }; // Nested statics have no type. @@ -431,7 +432,7 @@ fn collect_items_rec<'tcx>( } MonoItem::Fn(instance) => { // Sanity check whether this ended up being collected accidentally - debug_assert!(should_codegen_locally(tcx, instance)); + debug_assert!(tcx.should_codegen_locally(instance)); // Keep track of the monomorphization recursion depth recursion_depth_reset = Some(check_recursion_limit( @@ -475,7 +476,7 @@ fn collect_items_rec<'tcx>( } hir::InlineAsmOperand::SymStatic { path: _, def_id } => { let instance = Instance::mono(tcx, *def_id); - if should_codegen_locally(tcx, instance) { + if tcx.should_codegen_locally(instance) { trace!("collecting static {:?}", def_id); used_items.push(dummy_spanned(MonoItem::Static(*def_id))); } @@ -712,7 +713,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { if let ty::Closure(def_id, args) = *source_ty.kind() { let instance = Instance::resolve_closure(self.tcx, def_id, args, ty::ClosureKind::FnOnce); - if should_codegen_locally(self.tcx, instance) { + if self.tcx.should_codegen_locally(instance) { self.used_items.push(create_fn_mono_item(self.tcx, instance, span)); } } else { @@ -722,7 +723,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { mir::Rvalue::ThreadLocalRef(def_id) => { assert!(self.tcx.is_thread_local_static(def_id)); let instance = Instance::mono(self.tcx, def_id); - if should_codegen_locally(self.tcx, instance) { + if self.tcx.should_codegen_locally(instance) { trace!("collecting thread-local static {:?}", def_id); self.used_items.push(respan(span, MonoItem::Static(def_id))); } @@ -749,7 +750,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { let tcx = self.tcx; let push_mono_lang_item = |this: &mut Self, lang_item: LangItem| { let instance = Instance::mono(tcx, tcx.require_lang_item(lang_item, Some(source))); - if should_codegen_locally(tcx, instance) { + if tcx.should_codegen_locally(instance) { this.used_items.push(create_fn_mono_item(tcx, instance, source)); } }; @@ -783,7 +784,7 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> { } mir::InlineAsmOperand::SymStatic { def_id } => { let instance = Instance::mono(self.tcx, def_id); - if should_codegen_locally(self.tcx, instance) { + if self.tcx.should_codegen_locally(instance) { trace!("collecting asm sym static {:?}", def_id); self.used_items.push(respan(source, MonoItem::Static(def_id))); } @@ -873,7 +874,7 @@ fn visit_instance_use<'tcx>( output: &mut MonoItems<'tcx>, ) { debug!("visit_item_use({:?}, is_direct_call={:?})", instance, is_direct_call); - if !should_codegen_locally(tcx, instance) { + if !tcx.should_codegen_locally(instance) { return; } if let ty::InstanceKind::Intrinsic(def_id) = instance.def { @@ -885,13 +886,13 @@ fn visit_instance_use<'tcx>( // codegen a call to that function without generating code for the function itself. let def_id = tcx.require_lang_item(LangItem::PanicNounwind, None); let panic_instance = Instance::mono(tcx, def_id); - if should_codegen_locally(tcx, panic_instance) { + if tcx.should_codegen_locally(panic_instance) { output.push(create_fn_mono_item(tcx, panic_instance, source)); } } else if tcx.has_attr(def_id, sym::rustc_intrinsic) { // Codegen the fallback body of intrinsics with fallback bodies let instance = ty::Instance::new(def_id, instance.args); - if should_codegen_locally(tcx, instance) { + if tcx.should_codegen_locally(instance) { output.push(create_fn_mono_item(tcx, instance, source)); } } @@ -930,7 +931,7 @@ fn visit_instance_use<'tcx>( /// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we /// can just link to the upstream crate and therefore don't need a mono item. -pub(crate) fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool { +fn should_codegen_locally<'tcx>(tcx: TyCtxtAt<'tcx>, instance: Instance<'tcx>) -> bool { let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() else { return true; }; @@ -946,7 +947,7 @@ pub(crate) fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance } if tcx.is_reachable_non_generic(def_id) - || instance.polymorphize(tcx).upstream_monomorphization(tcx).is_some() + || instance.polymorphize(*tcx).upstream_monomorphization(*tcx).is_some() { // We can link to the item in question, no instance needed in this crate. return false; @@ -1127,7 +1128,7 @@ fn create_mono_items_for_vtable_methods<'tcx>( None } VtblEntry::Method(instance) => { - Some(*instance).filter(|instance| should_codegen_locally(tcx, *instance)) + Some(*instance).filter(|instance| tcx.should_codegen_locally(*instance)) } }) .map(|item| create_fn_mono_item(tcx, item, source)); @@ -1144,7 +1145,7 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt GlobalAlloc::Static(def_id) => { assert!(!tcx.is_thread_local_static(def_id)); let instance = Instance::mono(tcx, def_id); - if should_codegen_locally(tcx, instance) { + if tcx.should_codegen_locally(instance) { trace!("collecting static {:?}", def_id); output.push(dummy_spanned(MonoItem::Static(def_id))); } @@ -1162,7 +1163,7 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt } } GlobalAlloc::Function { instance, .. } => { - if should_codegen_locally(tcx, instance) { + if tcx.should_codegen_locally(instance) { trace!("collecting {:?} with {:#?}", alloc_id, instance); output.push(create_fn_mono_item(tcx, instance, DUMMY_SP)); } @@ -1284,7 +1285,7 @@ fn visit_mentioned_item<'tcx>( if let ty::Closure(def_id, args) = *source_ty.kind() { let instance = Instance::resolve_closure(tcx, def_id, args, ty::ClosureKind::FnOnce); - if should_codegen_locally(tcx, instance) { + if tcx.should_codegen_locally(instance) { output.push(create_fn_mono_item(tcx, instance, span)); } } else { @@ -1557,7 +1558,7 @@ fn create_mono_items_for_default_impls<'tcx>( let instance = ty::Instance::expect_resolve(tcx, param_env, method.def_id, args, DUMMY_SP); let mono_item = create_fn_mono_item(tcx, instance, DUMMY_SP); - if mono_item.node.is_instantiable(tcx) && should_codegen_locally(tcx, instance) { + if mono_item.node.is_instantiable(tcx) && tcx.should_codegen_locally(instance) { output.push(mono_item); } } @@ -1613,3 +1614,7 @@ pub(crate) fn collect_crate_mono_items<'tcx>( (mono_items, state.usage_map.into_inner()) } + +pub fn provide(providers: &mut Providers) { + providers.hooks.should_codegen_locally = should_codegen_locally; +} diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index aa3b4cd5b67..fc6e8e0d14f 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -5,14 +5,11 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::bug; -use rustc_middle::query::{Providers, TyCtxtAt}; +use rustc_middle::query::TyCtxtAt; use rustc_middle::traits; use rustc_middle::ty::adjustment::CustomCoerceUnsized; -use rustc_middle::ty::Instance; -use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{self, Ty}; -use rustc_span::def_id::DefId; -use rustc_span::def_id::LOCAL_CRATE; +use rustc_middle::util::Providers; use rustc_span::ErrorGuaranteed; mod collector; @@ -21,8 +18,6 @@ mod partitioning; mod polymorphize; mod util; -use collector::should_codegen_locally; - rustc_fluent_macro::fluent_messages! { "../messages.ftl" } fn custom_coerce_unsize_info<'tcx>( @@ -47,34 +42,6 @@ fn custom_coerce_unsize_info<'tcx>( } } -/// Returns whether a call from the current crate to the [`Instance`] would produce a call -/// from `compiler_builtins` to a symbol the linker must resolve. -/// -/// Such calls from `compiler_bultins` are effectively impossible for the linker to handle. Some -/// linkers will optimize such that dead calls to unresolved symbols are not an error, but this is -/// not guaranteed. So we used this function in codegen backends to ensure we do not generate any -/// unlinkable calls. -/// -/// Note that calls to LLVM intrinsics are uniquely okay because they won't make it to the linker. -pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( - tcx: TyCtxt<'tcx>, - instance: Instance<'tcx>, -) -> bool { - fn is_llvm_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - if let Some(name) = tcx.codegen_fn_attrs(def_id).link_name { - name.as_str().starts_with("llvm.") - } else { - false - } - } - - let def_id = instance.def_id(); - !def_id.is_local() - && tcx.is_compiler_builtins(LOCAL_CRATE) - && !is_llvm_intrinsic(tcx, def_id) - && !should_codegen_locally(tcx, instance) -} - pub fn provide(providers: &mut Providers) { partitioning::provide(providers); polymorphize::provide(providers); diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 9a7c488833a..8c7c5e0074a 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -112,9 +112,9 @@ use rustc_middle::mir::mono::{ CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, MonoItem, MonoItemData, Visibility, }; -use rustc_middle::query::Providers; use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths}; use rustc_middle::ty::{self, visit::TypeVisitableExt, InstanceKind, TyCtxt}; +use rustc_middle::util::Providers; use rustc_session::config::{DumpMonoStatsFormat, SwitchWithOptPath}; use rustc_session::CodegenUnits; use rustc_span::symbol::Symbol; @@ -1314,4 +1314,6 @@ pub fn provide(providers: &mut Providers) { .find(|cgu| cgu.name() == name) .unwrap_or_else(|| panic!("failed to find cgu with name {name:?}")) }; + + collector::provide(providers); } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index ce2fa83810f..9cbd989cc0e 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -34,7 +34,7 @@ use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_span::{BytePos, Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs}; use rustc_trait_selection::traits::ObligationCtxt; use std::cell::Cell; diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs index 603e98cfa92..6f59c782e06 100644 --- a/compiler/rustc_passes/src/layout_test.rs +++ b/compiler/rustc_passes/src/layout_test.rs @@ -8,7 +8,7 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_target::abi::{HasDataLayout, TargetDataLayout}; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::{infer::TyCtxtInferExt, traits}; use crate::errors::{ diff --git a/compiler/rustc_pattern_analysis/src/constructor.rs b/compiler/rustc_pattern_analysis/src/constructor.rs index fb103705475..f3e8e547066 100644 --- a/compiler/rustc_pattern_analysis/src/constructor.rs +++ b/compiler/rustc_pattern_analysis/src/constructor.rs @@ -904,7 +904,7 @@ impl<Cx: PatCx> Constructor<Cx> { // be careful to detect strings here. However a string literal pattern will never // be reported as a non-exhaustiveness witness, so we can ignore this issue. Ref => { - write!(f, "&{:?}", &fields.next().unwrap())?; + write!(f, "&{:?}", fields.next().unwrap())?; } Slice(slice) => { write!(f, "[")?; diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs index 1b4bcb789d2..a5c0b13c90b 100644 --- a/compiler/rustc_pattern_analysis/src/lib.rs +++ b/compiler/rustc_pattern_analysis/src/lib.rs @@ -1,4 +1,6 @@ -//! Analysis of patterns, notably match exhaustiveness checking. +//! Analysis of patterns, notably match exhaustiveness checking. The main entrypoint for this crate +//! is [`usefulness::compute_match_usefulness`]. For rustc-specific types and entrypoints, see the +//! [`rustc`] module. // tidy-alphabetical-start #![allow(rustc::diagnostic_outside_of_impl)] @@ -23,14 +25,8 @@ use std::fmt; pub use rustc_index::{Idx, IndexVec}; // re-exported to avoid rustc_index version issues -#[cfg(feature = "rustc")] -use rustc_middle::ty::Ty; -#[cfg(feature = "rustc")] -use rustc_span::ErrorGuaranteed; - use crate::constructor::{Constructor, ConstructorSet, IntRange}; use crate::pat::DeconstructedPat; -use crate::pat_column::PatternColumn; pub trait Captures<'a> {} impl<'a, T: ?Sized> Captures<'a> for T {} @@ -128,30 +124,3 @@ impl<'p, Cx: PatCx> Clone for MatchArm<'p, Cx> { } impl<'p, Cx: PatCx> Copy for MatchArm<'p, Cx> {} - -/// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are -/// useful, and runs some lints. -#[cfg(feature = "rustc")] -pub fn analyze_match<'p, 'tcx>( - tycx: &rustc::RustcPatCtxt<'p, 'tcx>, - arms: &[rustc::MatchArm<'p, 'tcx>], - scrut_ty: Ty<'tcx>, - pattern_complexity_limit: Option<usize>, -) -> Result<rustc::UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> { - use lints::lint_nonexhaustive_missing_variants; - use usefulness::{compute_match_usefulness, PlaceValidity}; - - let scrut_ty = tycx.reveal_opaque_ty(scrut_ty); - let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee); - let report = - compute_match_usefulness(tycx, arms, scrut_ty, scrut_validity, pattern_complexity_limit)?; - - // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting - // `if let`s. Only run if the match is exhaustive otherwise the error is redundant. - if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() { - let pat_column = PatternColumn::new(arms); - lint_nonexhaustive_missing_variants(tycx, arms, &pat_column, scrut_ty)?; - } - - Ok(report) -} diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 5e75976621e..a591c3c554b 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -11,7 +11,7 @@ use crate::{PatCx, PrivateUninhabitedField}; use self::Constructor::*; /// A globally unique id to distinguish patterns. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(crate) struct PatId(u32); impl PatId { fn new() -> Self { @@ -147,6 +147,21 @@ impl<Cx: PatCx> fmt::Debug for DeconstructedPat<Cx> { } } +/// Delegate to `uid`. +impl<Cx: PatCx> PartialEq for DeconstructedPat<Cx> { + fn eq(&self, other: &Self) -> bool { + self.uid == other.uid + } +} +/// Delegate to `uid`. +impl<Cx: PatCx> Eq for DeconstructedPat<Cx> {} +/// Delegate to `uid`. +impl<Cx: PatCx> std::hash::Hash for DeconstructedPat<Cx> { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.uid.hash(state); + } +} + /// Represents either a pattern obtained from user input or a wildcard constructed during the /// algorithm. Do not use `Wild` to represent a wildcard pattern comping from user input. /// @@ -190,7 +205,18 @@ impl<'p, Cx: PatCx> PatOrWild<'p, Cx> { } } - /// Expand this (possibly-nested) or-pattern into its alternatives. + /// Expand this or-pattern into its alternatives. This only expands one or-pattern; use + /// `flatten_or_pat` to recursively expand nested or-patterns. + pub(crate) fn expand_or_pat(self) -> SmallVec<[Self; 1]> { + match self { + PatOrWild::Pat(pat) if pat.is_or_pat() => { + pat.iter_fields().map(|ipat| PatOrWild::Pat(&ipat.pat)).collect() + } + _ => smallvec![self], + } + } + + /// Recursively expand this (possibly-nested) or-pattern into its alternatives. pub(crate) fn flatten_or_pat(self) -> SmallVec<[Self; 1]> { match self { PatOrWild::Pat(pat) if pat.is_or_pat() => pat diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index d17ee8bff50..910dd4c6c1a 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -20,6 +20,9 @@ use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT}; use crate::constructor::{ IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility, }; +use crate::lints::lint_nonexhaustive_missing_variants; +use crate::pat_column::PatternColumn; +use crate::usefulness::{compute_match_usefulness, PlaceValidity}; use crate::{errors, Captures, PatCx, PrivateUninhabitedField}; use crate::constructor::Constructor::*; @@ -29,6 +32,8 @@ pub type Constructor<'p, 'tcx> = crate::constructor::Constructor<RustcPatCtxt<'p pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet<RustcPatCtxt<'p, 'tcx>>; pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<RustcPatCtxt<'p, 'tcx>>; pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcPatCtxt<'p, 'tcx>>; +pub type RedundancyExplanation<'p, 'tcx> = + crate::usefulness::RedundancyExplanation<'p, RustcPatCtxt<'p, 'tcx>>; pub type Usefulness<'p, 'tcx> = crate::usefulness::Usefulness<'p, RustcPatCtxt<'p, 'tcx>>; pub type UsefulnessReport<'p, 'tcx> = crate::usefulness::UsefulnessReport<'p, RustcPatCtxt<'p, 'tcx>>; @@ -40,9 +45,15 @@ pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcPatCtxt<'p, 'tcx>>; /// /// Use `.inner()` or deref to get to the `Ty<'tcx>`. #[repr(transparent)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct RevealedTy<'tcx>(Ty<'tcx>); +impl<'tcx> fmt::Display for RevealedTy<'tcx> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(fmt) + } +} + impl<'tcx> fmt::Debug for RevealedTy<'tcx> { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(fmt) @@ -1052,3 +1063,26 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { expand(pat, &mut pats); pats } + +/// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are +/// useful, and runs some lints. +pub fn analyze_match<'p, 'tcx>( + tycx: &RustcPatCtxt<'p, 'tcx>, + arms: &[MatchArm<'p, 'tcx>], + scrut_ty: Ty<'tcx>, + pattern_complexity_limit: Option<usize>, +) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> { + let scrut_ty = tycx.reveal_opaque_ty(scrut_ty); + let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee); + let report = + compute_match_usefulness(tycx, arms, scrut_ty, scrut_validity, pattern_complexity_limit)?; + + // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting + // `if let`s. Only run if the match is exhaustive otherwise the error is redundant. + if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() { + let pat_column = PatternColumn::new(arms); + lint_nonexhaustive_missing_variants(tycx, arms, &pat_column, scrut_ty)?; + } + + Ok(report) +} diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 6e96a93473f..76dc338e71c 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -462,8 +462,9 @@ //! # Or-patterns //! //! What we have described so far works well if there are no or-patterns. To handle them, if the -//! first pattern of a row in the matrix is an or-pattern, we expand it by duplicating the rest of -//! the row as necessary. This is handled automatically in [`Matrix`]. +//! first pattern of any row in the matrix is an or-pattern, we expand it by duplicating the rest of +//! the row as necessary. For code reuse, this is implemented as "specializing with the `Or` +//! constructor". //! //! This makes usefulness tracking subtle, because we also want to compute whether an alternative of //! an or-pattern is redundant, e.g. in `Some(_) | Some(0)`. We therefore track usefulness of each @@ -712,7 +713,7 @@ use self::PlaceValidity::*; use crate::constructor::{Constructor, ConstructorSet, IntRange}; use crate::pat::{DeconstructedPat, PatId, PatOrWild, WitnessPat}; use crate::{Captures, MatchArm, PatCx, PrivateUninhabitedField}; -use rustc_hash::FxHashSet; +use rustc_hash::{FxHashMap, FxHashSet}; use rustc_index::bit_set::BitSet; use smallvec::{smallvec, SmallVec}; use std::fmt; @@ -725,18 +726,81 @@ pub fn ensure_sufficient_stack<R>(f: impl FnOnce() -> R) -> R { f() } +/// A pattern is a "branch" if it is the immediate child of an or-pattern, or if it is the whole +/// pattern of a match arm. These are the patterns that can be meaningfully considered "redundant", +/// since e.g. `0` in `(0, 1)` cannot be redundant on its own. +/// +/// We track for each branch pattern whether it is useful, and if not why. +struct BranchPatUsefulness<'p, Cx: PatCx> { + /// Whether this pattern is useful. + useful: bool, + /// A set of patterns that: + /// - come before this one in the match; + /// - intersect this one; + /// - at the end of the algorithm, if `!self.useful`, their union covers this pattern. + covered_by: FxHashSet<&'p DeconstructedPat<Cx>>, +} + +impl<'p, Cx: PatCx> BranchPatUsefulness<'p, Cx> { + /// Update `self` with the usefulness information found in `row`. + fn update(&mut self, row: &MatrixRow<'p, Cx>, matrix: &Matrix<'p, Cx>) { + self.useful |= row.useful; + // This deserves an explanation: `intersects_at_least` does not contain all intersections + // because we skip irrelevant values (see the docs for `intersects_at_least` for an + // example). Yet we claim this suffices to build a covering set. + // + // Let `p` be our pattern. Assume it is found not useful. For a value `v`, if the value was + // relevant then we explored that value and found that there was another pattern `q` before + // `p` that matches it too. We therefore recorded an intersection with `q`. If `v` was + // irrelevant, we know there's another value `v2` that matches strictly fewer rows (while + // still matching our row) and is relevant. Since `p` is not useful, there must have been a + // `q` before `p` that matches `v2`, and we recorded that intersection. Since `v2` matches + // strictly fewer rows than `v`, `q` also matches `v`. In either case, we recorded in + // `intersects_at_least` a pattern that matches `v`. Hence using `intersects_at_least` is + // sufficient to build a covering set. + for row_id in row.intersects_at_least.iter() { + let row = &matrix.rows[row_id]; + if row.useful && !row.is_under_guard { + if let PatOrWild::Pat(intersecting) = row.head() { + self.covered_by.insert(intersecting); + } + } + } + } + + /// Check whether this pattern is redundant, and if so explain why. + fn is_redundant(&self) -> Option<RedundancyExplanation<'p, Cx>> { + if self.useful { + None + } else { + // We avoid instability by sorting by `uid`. The order of `uid`s only depends on the + // pattern structure. + #[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))] + let mut covered_by: Vec<_> = self.covered_by.iter().copied().collect(); + covered_by.sort_by_key(|pat| pat.uid); // sort to avoid instability + Some(RedundancyExplanation { covered_by }) + } + } +} + +impl<'p, Cx: PatCx> Default for BranchPatUsefulness<'p, Cx> { + fn default() -> Self { + Self { useful: Default::default(), covered_by: Default::default() } + } +} + /// Context that provides information for usefulness checking. -struct UsefulnessCtxt<'a, Cx: PatCx> { +struct UsefulnessCtxt<'a, 'p, Cx: PatCx> { /// The context for type information. tycx: &'a Cx, - /// Collect the patterns found useful during usefulness checking. This is used to lint - /// unreachable (sub)patterns. - useful_subpatterns: FxHashSet<PatId>, + /// Track information about the usefulness of branch patterns (see definition of "branch + /// pattern" at [`BranchPatUsefulness`]). + branch_usefulness: FxHashMap<PatId, BranchPatUsefulness<'p, Cx>>, complexity_limit: Option<usize>, complexity_level: usize, } -impl<'a, Cx: PatCx> UsefulnessCtxt<'a, Cx> { +impl<'a, 'p, Cx: PatCx> UsefulnessCtxt<'a, 'p, Cx> { fn increase_complexity_level(&mut self, complexity_add: usize) -> Result<(), Cx::Error> { self.complexity_level += complexity_add; if self @@ -875,6 +939,11 @@ impl<Cx: PatCx> PlaceInfo<Cx> { return Ok((smallvec![Constructor::PrivateUninhabited], vec![])); } + if ctors.clone().any(|c| matches!(c, Constructor::Or)) { + // If any constructor is `Or`, we expand or-patterns. + return Ok((smallvec![Constructor::Or], vec![])); + } + let ctors_for_ty = cx.ctors_for_ty(&self.ty)?; debug!(?ctors_for_ty); @@ -968,10 +1037,6 @@ impl<'p, Cx: PatCx> PatStack<'p, Cx> { PatStack { pats: smallvec![PatOrWild::Pat(pat)], relevant: true } } - fn is_empty(&self) -> bool { - self.pats.is_empty() - } - fn len(&self) -> usize { self.pats.len() } @@ -984,10 +1049,10 @@ impl<'p, Cx: PatCx> PatStack<'p, Cx> { self.pats.iter().copied() } - // Recursively expand the first or-pattern into its subpatterns. Only useful if the pattern is - // an or-pattern. Panics if `self` is empty. + // Expand the first or-pattern into its subpatterns. Only useful if the pattern is an + // or-pattern. Panics if `self` is empty. fn expand_or_pat(&self) -> impl Iterator<Item = PatStack<'p, Cx>> + Captures<'_> { - self.head().flatten_or_pat().into_iter().map(move |pat| { + self.head().expand_or_pat().into_iter().map(move |pat| { let mut new = self.clone(); new.pats[0] = pat; new @@ -1049,16 +1114,38 @@ struct MatrixRow<'p, Cx: PatCx> { /// [`compute_exhaustiveness_and_usefulness`] if the arm is found to be useful. /// This is reset to `false` when specializing. useful: bool, - /// Tracks which rows above this one have an intersection with this one, i.e. such that there is - /// a value that matches both rows. - /// Note: Because of relevancy we may miss some intersections. The intersections we do find are - /// correct. - intersects: BitSet<usize>, + /// Tracks some rows above this one that have an intersection with this one, i.e. such that + /// there is a value that matches both rows. + /// Because of relevancy we may miss some intersections. The intersections we do find are + /// correct. In other words, this is an underapproximation of the real set of intersections. + /// + /// For example: + /// ```rust,ignore(illustrative) + /// match ... { + /// (true, _, _) => {} // `intersects_at_least = []` + /// (_, true, 0..=10) => {} // `intersects_at_least = []` + /// (_, true, 5..15) => {} // `intersects_at_least = [1]` + /// } + /// ``` + /// Here the `(true, true)` case is irrelevant. Since we skip it, we will not detect that row 0 + /// intersects rows 1 and 2. + intersects_at_least: BitSet<usize>, + /// Whether the head pattern is a branch (see definition of "branch pattern" at + /// [`BranchPatUsefulness`]) + head_is_branch: bool, } impl<'p, Cx: PatCx> MatrixRow<'p, Cx> { - fn is_empty(&self) -> bool { - self.pats.is_empty() + fn new(arm: &MatchArm<'p, Cx>, arm_id: usize) -> Self { + MatrixRow { + pats: PatStack::from_pattern(arm.pat), + parent_row: arm_id, + is_under_guard: arm.has_guard, + useful: false, + intersects_at_least: BitSet::new_empty(0), // Initialized in `Matrix::push`. + // This pattern is a branch because it comes from a match arm. + head_is_branch: true, + } } fn len(&self) -> usize { @@ -1073,15 +1160,19 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> { self.pats.iter() } - // Recursively expand the first or-pattern into its subpatterns. Only useful if the pattern is - // an or-pattern. Panics if `self` is empty. - fn expand_or_pat(&self) -> impl Iterator<Item = MatrixRow<'p, Cx>> + Captures<'_> { - self.pats.expand_or_pat().map(|patstack| MatrixRow { + // Expand the first or-pattern (if any) into its subpatterns. Panics if `self` is empty. + fn expand_or_pat( + &self, + parent_row: usize, + ) -> impl Iterator<Item = MatrixRow<'p, Cx>> + Captures<'_> { + let is_or_pat = self.pats.head().is_or_pat(); + self.pats.expand_or_pat().map(move |patstack| MatrixRow { pats: patstack, - parent_row: self.parent_row, + parent_row, is_under_guard: self.is_under_guard, useful: false, - intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`. + intersects_at_least: BitSet::new_empty(0), // Initialized in `Matrix::push`. + head_is_branch: is_or_pat, }) } @@ -1100,7 +1191,8 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> { parent_row, is_under_guard: self.is_under_guard, useful: false, - intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`. + intersects_at_least: BitSet::new_empty(0), // Initialized in `Matrix::push`. + head_is_branch: false, }) } } @@ -1116,7 +1208,7 @@ impl<'p, Cx: PatCx> fmt::Debug for MatrixRow<'p, Cx> { /// Invariant: each row must have the same length, and each column must have the same type. /// /// Invariant: the first column must not contain or-patterns. This is handled by -/// [`Matrix::expand_and_push`]. +/// [`Matrix::push`]. /// /// In fact each column corresponds to a place inside the scrutinee of the match. E.g. after /// specializing `(,)` and `Some` on a pattern of type `(Option<u32>, bool)`, the first column of @@ -1136,19 +1228,10 @@ struct Matrix<'p, Cx: PatCx> { } impl<'p, Cx: PatCx> Matrix<'p, Cx> { - /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively - /// expands it. Internal method, prefer [`Matrix::new`]. - fn expand_and_push(&mut self, mut row: MatrixRow<'p, Cx>) { - if !row.is_empty() && row.head().is_or_pat() { - // Expand nested or-patterns. - for mut new_row in row.expand_or_pat() { - new_row.intersects = BitSet::new_empty(self.rows.len()); - self.rows.push(new_row); - } - } else { - row.intersects = BitSet::new_empty(self.rows.len()); - self.rows.push(row); - } + /// Pushes a new row to the matrix. Internal method, prefer [`Matrix::new`]. + fn push(&mut self, mut row: MatrixRow<'p, Cx>) { + row.intersects_at_least = BitSet::new_empty(self.rows.len()); + self.rows.push(row); } /// Build a new matrix from an iterator of `MatchArm`s. @@ -1165,14 +1248,7 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> { wildcard_row_is_relevant: true, }; for (arm_id, arm) in arms.iter().enumerate() { - let v = MatrixRow { - pats: PatStack::from_pattern(arm.pat), - parent_row: arm_id, - is_under_guard: arm.has_guard, - useful: false, - intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`. - }; - matrix.expand_and_push(v); + matrix.push(MatrixRow::new(arm, arm_id)); } matrix } @@ -1209,22 +1285,38 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> { ctor: &Constructor<Cx>, ctor_is_relevant: bool, ) -> Result<Matrix<'p, Cx>, Cx::Error> { - let subfield_place_info = self.place_info[0].specialize(pcx.cx, ctor); - let arity = subfield_place_info.len(); - let specialized_place_info = - subfield_place_info.chain(self.place_info[1..].iter().cloned()).collect(); - let mut matrix = Matrix { - rows: Vec::new(), - place_info: specialized_place_info, - wildcard_row_is_relevant: self.wildcard_row_is_relevant && ctor_is_relevant, - }; - for (i, row) in self.rows().enumerate() { - if ctor.is_covered_by(pcx.cx, row.head().ctor())? { - let new_row = row.pop_head_constructor(pcx.cx, ctor, arity, ctor_is_relevant, i)?; - matrix.expand_and_push(new_row); + if matches!(ctor, Constructor::Or) { + // Specializing with `Or` means expanding rows with or-patterns. + let mut matrix = Matrix { + rows: Vec::new(), + place_info: self.place_info.clone(), + wildcard_row_is_relevant: self.wildcard_row_is_relevant, + }; + for (i, row) in self.rows().enumerate() { + for new_row in row.expand_or_pat(i) { + matrix.push(new_row); + } + } + Ok(matrix) + } else { + let subfield_place_info = self.place_info[0].specialize(pcx.cx, ctor); + let arity = subfield_place_info.len(); + let specialized_place_info = + subfield_place_info.chain(self.place_info[1..].iter().cloned()).collect(); + let mut matrix = Matrix { + rows: Vec::new(), + place_info: specialized_place_info, + wildcard_row_is_relevant: self.wildcard_row_is_relevant && ctor_is_relevant, + }; + for (i, row) in self.rows().enumerate() { + if ctor.is_covered_by(pcx.cx, row.head().ctor())? { + let new_row = + row.pop_head_constructor(pcx.cx, ctor, arity, ctor_is_relevant, i)?; + matrix.push(new_row); + } } + Ok(matrix) } - Ok(matrix) } /// Recover row usefulness and intersection information from a processed specialized matrix. @@ -1235,12 +1327,12 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> { let parent_row = &mut self.rows[parent_row_id]; // A parent row is useful if any of its children is. parent_row.useful |= child_row.useful; - for child_intersection in child_row.intersects.iter() { + for child_intersection in child_row.intersects_at_least.iter() { // Convert the intersecting ids into ids for the parent matrix. let parent_intersection = specialized.rows[child_intersection].parent_row; // Note: self-intersection can happen with or-patterns. if parent_intersection != parent_row_id { - parent_row.intersects.insert(parent_intersection); + parent_row.intersects_at_least.insert(parent_intersection); } } } @@ -1465,7 +1557,9 @@ impl<Cx: PatCx> WitnessMatrix<Cx> { missing_ctors: &[Constructor<Cx>], ctor: &Constructor<Cx>, ) { - if self.is_empty() { + // The `Or` constructor indicates that we expanded or-patterns. This doesn't affect + // witnesses. + if self.is_empty() || matches!(ctor, Constructor::Or) { return; } if matches!(ctor, Constructor::Missing) { @@ -1535,7 +1629,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: PatCx>( let overlaps_with: Vec<_> = prefixes .iter() .filter(|&&(other_child_row_id, _)| { - child_row.intersects.contains(other_child_row_id) + child_row.intersects_at_least.contains(other_child_row_id) }) .map(|&(_, pat)| pat) .collect(); @@ -1551,7 +1645,7 @@ fn collect_overlapping_range_endpoints<'p, Cx: PatCx>( let overlaps_with: Vec<_> = suffixes .iter() .filter(|&&(other_child_row_id, _)| { - child_row.intersects.contains(other_child_row_id) + child_row.intersects_at_least.contains(other_child_row_id) }) .map(|&(_, pat)| pat) .collect(); @@ -1594,8 +1688,8 @@ fn collect_non_contiguous_range_endpoints<'p, Cx: PatCx>( /// The core of the algorithm. /// /// This recursively computes witnesses of the non-exhaustiveness of `matrix` (if any). Also tracks -/// usefulness of each row in the matrix (in `row.useful`). We track usefulness of each subpattern -/// in `mcx.useful_subpatterns`. +/// usefulness of each row in the matrix (in `row.useful`). We track usefulness of subpatterns in +/// `mcx.branch_usefulness`. /// /// The input `Matrix` and the output `WitnessMatrix` together match the type exhaustively. /// @@ -1607,7 +1701,7 @@ fn collect_non_contiguous_range_endpoints<'p, Cx: PatCx>( /// This is all explained at the top of the file. #[instrument(level = "debug", skip(mcx), ret)] fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>( - mcx: &mut UsefulnessCtxt<'a, Cx>, + mcx: &mut UsefulnessCtxt<'a, 'p, Cx>, matrix: &mut Matrix<'p, Cx>, ) -> Result<WitnessMatrix<Cx>, Cx::Error> { debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count())); @@ -1626,7 +1720,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>( let mut useful = true; // Whether the next row is useful. for (i, row) in matrix.rows_mut().enumerate() { row.useful = useful; - row.intersects.insert_range(0..i); + row.intersects_at_least.insert_range(0..i); // The next rows stays useful if this one is under a guard. useful &= row.is_under_guard; } @@ -1668,7 +1762,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>( if let Constructor::IntRange(overlap_range) = ctor { if overlap_range.is_singleton() && spec_matrix.rows.len() >= 2 - && spec_matrix.rows.iter().any(|row| !row.intersects.is_empty()) + && spec_matrix.rows.iter().any(|row| !row.intersects_at_least.is_empty()) { collect_overlapping_range_endpoints(mcx.tycx, overlap_range, matrix, &spec_matrix); } @@ -1688,14 +1782,11 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>( } } - // Record usefulness in the patterns. + // Record usefulness of the branch patterns. for row in matrix.rows() { - if row.useful { + if row.head_is_branch { if let PatOrWild::Pat(pat) = row.head() { - let newly_useful = mcx.useful_subpatterns.insert(pat.uid); - if newly_useful { - debug!("newly useful: {pat:?}"); - } + mcx.branch_usefulness.entry(pat.uid).or_default().update(row, matrix); } } } @@ -1703,58 +1794,25 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>( Ok(ret) } +/// Indicates why a given pattern is considered redundant. +#[derive(Clone, Debug)] +pub struct RedundancyExplanation<'p, Cx: PatCx> { + /// All the values matched by this pattern are already matched by the given set of patterns. + /// This list is not guaranteed to be minimal but the contained patterns are at least guaranteed + /// to intersect this pattern. + pub covered_by: Vec<&'p DeconstructedPat<Cx>>, +} + /// Indicates whether or not a given arm is useful. #[derive(Clone, Debug)] pub enum Usefulness<'p, Cx: PatCx> { /// The arm is useful. This additionally carries a set of or-pattern branches that have been /// found to be redundant despite the overall arm being useful. Used only in the presence of /// or-patterns, otherwise it stays empty. - Useful(Vec<&'p DeconstructedPat<Cx>>), + Useful(Vec<(&'p DeconstructedPat<Cx>, RedundancyExplanation<'p, Cx>)>), /// The arm is redundant and can be removed without changing the behavior of the match /// expression. - Redundant, -} - -/// Report whether this pattern was found useful, and its subpatterns that were not useful if any. -fn collect_pattern_usefulness<'p, Cx: PatCx>( - useful_subpatterns: &FxHashSet<PatId>, - pat: &'p DeconstructedPat<Cx>, -) -> Usefulness<'p, Cx> { - fn pat_is_useful<'p, Cx: PatCx>( - useful_subpatterns: &FxHashSet<PatId>, - pat: &'p DeconstructedPat<Cx>, - ) -> bool { - if useful_subpatterns.contains(&pat.uid) { - true - } else if pat.is_or_pat() - && pat.iter_fields().any(|f| pat_is_useful(useful_subpatterns, &f.pat)) - { - // We always expand or patterns in the matrix, so we will never see the actual - // or-pattern (the one with constructor `Or`) in the column. As such, it will not be - // marked as useful itself, only its children will. We recover this information here. - true - } else { - false - } - } - - let mut redundant_subpats = Vec::new(); - pat.walk(&mut |p| { - if pat_is_useful(useful_subpatterns, p) { - // The pattern is useful, so we recurse to find redundant subpatterns. - true - } else { - // The pattern is redundant. - redundant_subpats.push(p); - false // stop recursing - } - }); - - if pat_is_useful(useful_subpatterns, pat) { - Usefulness::Useful(redundant_subpats) - } else { - Usefulness::Redundant - } + Redundant(RedundancyExplanation<'p, Cx>), } /// The output of checking a match for exhaustiveness and arm usefulness. @@ -1780,7 +1838,7 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>( ) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> { let mut cx = UsefulnessCtxt { tycx, - useful_subpatterns: FxHashSet::default(), + branch_usefulness: FxHashMap::default(), complexity_limit, complexity_level: 0, }; @@ -1793,25 +1851,32 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>( .copied() .map(|arm| { debug!(?arm); - let usefulness = collect_pattern_usefulness(&cx.useful_subpatterns, arm.pat); + let usefulness = cx.branch_usefulness.get(&arm.pat.uid).unwrap(); + let usefulness = if let Some(explanation) = usefulness.is_redundant() { + Usefulness::Redundant(explanation) + } else { + let mut redundant_subpats = Vec::new(); + arm.pat.walk(&mut |subpat| { + if let Some(u) = cx.branch_usefulness.get(&subpat.uid) { + if let Some(explanation) = u.is_redundant() { + redundant_subpats.push((subpat, explanation)); + false // stop recursing + } else { + true // keep recursing + } + } else { + true // keep recursing + } + }); + Usefulness::Useful(redundant_subpats) + }; debug!(?usefulness); (arm, usefulness) }) .collect(); - let mut arm_intersections: Vec<_> = - arms.iter().enumerate().map(|(i, _)| BitSet::new_empty(i)).collect(); - for row in matrix.rows() { - let arm_id = row.parent_row; - for intersection in row.intersects.iter() { - // Convert the matrix row ids into arm ids (they can differ because we expand or-patterns). - let arm_intersection = matrix.rows[intersection].parent_row; - // Note: self-intersection can happen with or-patterns. - if arm_intersection != arm_id { - arm_intersections[arm_id].insert(arm_intersection); - } - } - } + let arm_intersections: Vec<_> = + matrix.rows().map(|row| row.intersects_at_least.clone()).collect(); Ok(UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses, arm_intersections }) } diff --git a/compiler/rustc_pattern_analysis/tests/common/mod.rs b/compiler/rustc_pattern_analysis/tests/common/mod.rs index 6e8bb505005..68ec75b7705 100644 --- a/compiler/rustc_pattern_analysis/tests/common/mod.rs +++ b/compiler/rustc_pattern_analysis/tests/common/mod.rs @@ -25,7 +25,7 @@ pub fn init_tracing() { /// A simple set of types. #[allow(dead_code)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Ty { /// Booleans Bool, @@ -33,6 +33,8 @@ pub enum Ty { U8, /// Tuples. Tuple(&'static [Ty]), + /// Enum with one variant of each given type. + Enum(&'static [Ty]), /// A struct with `arity` fields of type `ty`. BigStruct { arity: usize, ty: &'static Ty }, /// A enum with `arity` variants of type `ty`. @@ -46,12 +48,23 @@ impl Ty { match (ctor, *self) { (Struct, Ty::Tuple(tys)) => tys.iter().copied().collect(), (Struct, Ty::BigStruct { arity, ty }) => (0..arity).map(|_| *ty).collect(), + (Variant(i), Ty::Enum(tys)) => vec![tys[*i]], (Variant(_), Ty::BigEnum { ty, .. }) => vec![*ty], (Bool(..) | IntRange(..) | NonExhaustive | Missing | Wildcard, _) => vec![], _ => panic!("Unexpected ctor {ctor:?} for type {self:?}"), } } + fn is_empty(&self) -> bool { + match *self { + Ty::Bool | Ty::U8 => false, + Ty::Tuple(tys) => tys.iter().any(|ty| ty.is_empty()), + Ty::Enum(tys) => tys.iter().all(|ty| ty.is_empty()), + Ty::BigStruct { arity, ty } => arity != 0 && ty.is_empty(), + Ty::BigEnum { arity, ty } => arity == 0 || ty.is_empty(), + } + } + pub fn ctor_set(&self) -> ConstructorSet<Cx> { match *self { Ty::Bool => ConstructorSet::Bool, @@ -64,10 +77,32 @@ impl Ty { range_2: None, }, Ty::Tuple(..) | Ty::BigStruct { .. } => ConstructorSet::Struct { empty: false }, - Ty::BigEnum { arity, .. } => ConstructorSet::Variants { - variants: (0..arity).map(|_| VariantVisibility::Visible).collect(), + Ty::Enum(tys) if tys.is_empty() => ConstructorSet::NoConstructors, + Ty::Enum(tys) => ConstructorSet::Variants { + variants: tys + .iter() + .map(|ty| { + if ty.is_empty() { + VariantVisibility::Empty + } else { + VariantVisibility::Visible + } + }) + .collect(), non_exhaustive: false, }, + Ty::BigEnum { arity: 0, .. } => ConstructorSet::NoConstructors, + Ty::BigEnum { arity, ty } => { + let vis = if ty.is_empty() { + VariantVisibility::Empty + } else { + VariantVisibility::Visible + }; + ConstructorSet::Variants { + variants: (0..arity).map(|_| vis).collect(), + non_exhaustive: false, + } + } } } @@ -79,6 +114,7 @@ impl Ty { match (*self, ctor) { (Ty::Tuple(..), _) => Ok(()), (Ty::BigStruct { .. }, _) => write!(f, "BigStruct"), + (Ty::Enum(..), Constructor::Variant(i)) => write!(f, "Enum::Variant{i}"), (Ty::BigEnum { .. }, Constructor::Variant(i)) => write!(f, "BigEnum::Variant{i}"), _ => write!(f, "{:?}::{:?}", self, ctor), } @@ -119,7 +155,7 @@ impl PatCx for Cx { } fn is_min_exhaustive_patterns_feature_on(&self) -> bool { - false + true } fn ctor_arity(&self, ctor: &Constructor<Self>, ty: &Self::Ty) -> usize { diff --git a/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs b/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs index 4f8d68d5514..205d430d495 100644 --- a/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs +++ b/compiler/rustc_pattern_analysis/tests/exhaustiveness.rs @@ -76,3 +76,17 @@ fn test_nested() { Struct(Variant.1, Variant.1), )); } + +#[test] +fn test_empty() { + // `TY = Result<bool, !>` + const TY: Ty = Ty::Enum(&[Ty::Bool, Ty::Enum(&[])]); + assert_exhaustive(pats!(TY; + Variant.0, + )); + let ty = Ty::Tuple(&[Ty::Bool, TY]); + assert_exhaustive(pats!(ty; + (true, Variant.0), + (false, Variant.0), + )); +} diff --git a/compiler/rustc_pattern_analysis/tests/intersection.rs b/compiler/rustc_pattern_analysis/tests/intersection.rs index 4a96b7248da..8c8cb3c796d 100644 --- a/compiler/rustc_pattern_analysis/tests/intersection.rs +++ b/compiler/rustc_pattern_analysis/tests/intersection.rs @@ -67,4 +67,24 @@ fn test_nested() { ), &[&[], &[]], ); + let ty = Ty::Tuple(&[Ty::Bool; 3]); + assert_intersects( + pats!(ty; + (true, true, _), + (true, _, true), + (false, _, _), + ), + &[&[], &[], &[]], + ); + let ty = Ty::Tuple(&[Ty::Bool, Ty::Bool, Ty::U8]); + assert_intersects( + pats!(ty; + (true, _, _), + (_, true, 0..10), + (_, true, 10..), + (_, true, 3), + _, + ), + &[&[], &[], &[], &[1], &[0, 1, 2, 3]], + ); } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 7c0405c87e0..bf7972e392c 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -149,7 +149,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { BuiltinLintDiag::AmbiguousGlobImports { diag }, ); } else { - let mut err = struct_span_code_err!(self.dcx(), diag.span, E0659, "{}", &diag.msg); + let mut err = struct_span_code_err!(self.dcx(), diag.span, E0659, "{}", diag.msg); report_ambiguity_error(&mut err, diag); err.emit(); } @@ -798,7 +798,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } ResolutionError::FailedToResolve { segment, label, suggestion, module } => { let mut err = - struct_span_code_err!(self.dcx(), span, E0433, "failed to resolve: {}", &label); + struct_span_code_err!(self.dcx(), span, E0433, "failed to resolve: {label}"); err.span_label(span, label); if let Some((suggestions, msg, applicability)) = suggestion { @@ -2893,7 +2893,7 @@ fn show_candidates( "" }; candidate.0 = - format!("{add_use}{}{append}{trailing}{additional_newline}", &candidate.0); + format!("{add_use}{}{append}{trailing}{additional_newline}", candidate.0); } match mode { diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 3896fe4c4fa..d05326ee311 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -694,7 +694,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { .collect::<Vec<_>>(); let msg = format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); - let mut diag = struct_span_code_err!(self.dcx(), span, E0432, "{}", &msg); + let mut diag = struct_span_code_err!(self.dcx(), span, E0432, "{msg}"); if let Some((_, UnresolvedImportError { note: Some(note), .. })) = errors.iter().last() { diag.note(note.clone()); diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index dc7200465d9..51414d78596 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -2763,7 +2763,11 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { let res = match kind { RibKind::Item(..) | RibKind::AssocItem => Res::Def(def_kind, def_id.to_def_id()), RibKind::Normal => { - if self.r.tcx.features().non_lifetime_binders { + // FIXME(non_lifetime_binders): Stop special-casing + // const params to error out here. + if self.r.tcx.features().non_lifetime_binders + && matches!(param.kind, GenericParamKind::Type { .. }) + { Res::Def(def_kind, def_id.to_def_id()) } else { Res::Err diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs index 59460815321..4c49c15c472 100644 --- a/compiler/rustc_resolve/src/rustdoc.rs +++ b/compiler/rustc_resolve/src/rustdoc.rs @@ -339,7 +339,7 @@ pub fn strip_generics_from_path(path_str: &str) -> Result<Box<str>, MalformedGen } } - debug!("path_str: {:?}\nstripped segments: {:?}", path_str, &stripped_segments); + debug!("path_str: {path_str:?}\nstripped segments: {stripped_segments:?}"); let stripped_path = stripped_segments.join("::"); diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs index f998fd599b7..190ea443189 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/encode.rs @@ -238,12 +238,12 @@ fn encode_predicate<'tcx>( match predicate.as_ref().skip_binder() { ty::ExistentialPredicate::Trait(trait_ref) => { let name = encode_ty_name(tcx, trait_ref.def_id); - let _ = write!(s, "u{}{}", name.len(), &name); + let _ = write!(s, "u{}{}", name.len(), name); s.push_str(&encode_args(tcx, trait_ref.args, trait_ref.def_id, true, dict, options)); } ty::ExistentialPredicate::Projection(projection) => { let name = encode_ty_name(tcx, projection.def_id); - let _ = write!(s, "u{}{}", name.len(), &name); + let _ = write!(s, "u{}{}", name.len(), name); s.push_str(&encode_args(tcx, projection.args, projection.def_id, true, dict, options)); match projection.term.unpack() { TermKind::Ty(ty) => s.push_str(&encode_ty(tcx, ty, dict, options)), @@ -258,7 +258,7 @@ fn encode_predicate<'tcx>( } ty::ExistentialPredicate::AutoTrait(def_id) => { let name = encode_ty_name(tcx, *def_id); - let _ = write!(s, "u{}{}", name.len(), &name); + let _ = write!(s, "u{}{}", name.len(), name); } }; compress(dict, DictKey::Predicate(*predicate.as_ref().skip_binder()), &mut s); @@ -416,7 +416,7 @@ pub fn encode_ty<'tcx>( // A<array-length><element-type> let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()); let mut s = String::from("A"); - let _ = write!(s, "{}", &len); + let _ = write!(s, "{len}"); s.push_str(&encode_ty(tcx, *ty0, dict, options)); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); typeid.push_str(&s); @@ -492,13 +492,13 @@ pub fn encode_ty<'tcx>( // calling convention (or extern types [i.e., ty::Foreign]) as <length><name>, where // <name> is <unscoped-name>. let name = tcx.item_name(def_id).to_string(); - let _ = write!(s, "{}{}", name.len(), &name); + let _ = write!(s, "{}{}", name.len(), name); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); } else { // u<length><name>[I<element-type1..element-typeN>E], where <element-type> is // <subst>, as vendor extended type. let name = encode_ty_name(tcx, def_id); - let _ = write!(s, "u{}{}", name.len(), &name); + let _ = write!(s, "u{}{}", name.len(), name); s.push_str(&encode_args(tcx, args, def_id, false, dict, options)); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); } @@ -530,7 +530,7 @@ pub fn encode_ty<'tcx>( } } else { let name = tcx.item_name(*def_id).to_string(); - let _ = write!(s, "{}{}", name.len(), &name); + let _ = write!(s, "{}{}", name.len(), name); } compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); typeid.push_str(&s); @@ -542,7 +542,7 @@ pub fn encode_ty<'tcx>( // as vendor extended type. let mut s = String::new(); let name = encode_ty_name(tcx, *def_id); - let _ = write!(s, "u{}{}", name.len(), &name); + let _ = write!(s, "u{}{}", name.len(), name); s.push_str(&encode_args(tcx, args, *def_id, false, dict, options)); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); typeid.push_str(&s); @@ -553,7 +553,7 @@ pub fn encode_ty<'tcx>( // as vendor extended type. let mut s = String::new(); let name = encode_ty_name(tcx, *def_id); - let _ = write!(s, "u{}{}", name.len(), &name); + let _ = write!(s, "u{}{}", name.len(), name); let parent_args = tcx.mk_args(args.as_coroutine_closure().parent_args()); s.push_str(&encode_args(tcx, parent_args, *def_id, false, dict, options)); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); @@ -565,7 +565,7 @@ pub fn encode_ty<'tcx>( // as vendor extended type. let mut s = String::new(); let name = encode_ty_name(tcx, *def_id); - let _ = write!(s, "u{}{}", name.len(), &name); + let _ = write!(s, "u{}{}", name.len(), name); // Encode parent args only s.push_str(&encode_args( tcx, @@ -588,7 +588,7 @@ pub fn encode_ty<'tcx>( s.push('E'); compress(dict, DictKey::Ty(Ty::new_imm_ref(tcx, *region, *ty0), TyQ::None), &mut s); if ty.is_mutable_ptr() { - s = format!("{}{}", "U3mut", &s); + s = format!("{}{}", "U3mut", s); compress(dict, DictKey::Ty(ty, TyQ::Mut), &mut s); } typeid.push_str(&s); @@ -600,10 +600,10 @@ pub fn encode_ty<'tcx>( let mut s = String::new(); s.push_str(&encode_ty(tcx, *ptr_ty, dict, options)); if !ty.is_mutable_ptr() { - s = format!("{}{}", "K", &s); + s = format!("{}{}", "K", s); compress(dict, DictKey::Ty(*ptr_ty, TyQ::Const), &mut s); }; - s = format!("{}{}", "P", &s); + s = format!("{}{}", "P", s); compress(dict, DictKey::Ty(ty, TyQ::None), &mut s); typeid.push_str(&s); } @@ -722,7 +722,7 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String { s.push('C'); s.push_str(&to_disambiguator(tcx.stable_crate_id(def_path.krate).as_u64())); let crate_name = tcx.crate_name(def_path.krate).to_string(); - let _ = write!(s, "{}{}", crate_name.len(), &crate_name); + let _ = write!(s, "{}{}", crate_name.len(), crate_name); // Disambiguators and names def_path.data.reverse(); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b64efadb261..2b30ca8a894 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -177,6 +177,7 @@ symbols! { CoerceUnsized, Command, ConstParamTy, + ConstParamTy_, Context, Continue, Copy, @@ -336,6 +337,7 @@ symbols! { TyKind, Unknown, Unsize, + UnsizedConstParamTy, Upvars, Vec, VecDeque, @@ -1689,6 +1691,7 @@ symbols! { rvalue_static_promotion, rwpi, s, + s390x_target_feature, safety, sanitize, sanitizer_cfi_generalize_pointers, @@ -2001,6 +2004,8 @@ symbols! { unsafe_no_drop_flag, unsafe_pin_internals, unsize, + unsized_const_param_ty, + unsized_const_params, unsized_fn_params, unsized_locals, unsized_tuple_coercion, diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 607eeac7ccd..0efe68252af 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1840,6 +1840,19 @@ supported_targets! { ("x86_64-unknown-linux-none", x86_64_unknown_linux_none), + ("thumbv6m-nuttx-eabi", thumbv6m_nuttx_eabi), + ("thumbv7m-nuttx-eabi", thumbv7m_nuttx_eabi), + ("thumbv7em-nuttx-eabi", thumbv7em_nuttx_eabi), + ("thumbv7em-nuttx-eabihf", thumbv7em_nuttx_eabihf), + ("thumbv8m.base-nuttx-eabi", thumbv8m_base_nuttx_eabi), + ("thumbv8m.main-nuttx-eabi", thumbv8m_main_nuttx_eabi), + ("thumbv8m.main-nuttx-eabihf", thumbv8m_main_nuttx_eabihf), + ("riscv32imc-unknown-nuttx-elf", riscv32imc_unknown_nuttx_elf), + ("riscv32imac-unknown-nuttx-elf", riscv32imac_unknown_nuttx_elf), + ("riscv32imafc-unknown-nuttx-elf", riscv32imafc_unknown_nuttx_elf), + ("riscv64imac-unknown-nuttx-elf", riscv64imac_unknown_nuttx_elf), + ("riscv64gc-unknown-nuttx-elf", riscv64gc_unknown_nuttx_elf), + } /// Cow-Vec-Str: Cow<'static, [Cow<'static, str>]> diff --git a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs new file mode 100644 index 00000000000..aa51b8957fc --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs @@ -0,0 +1,29 @@ +use crate::spec::{cvs, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), + llvm_target: "riscv32".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "nuttx".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + cpu: "generic-rv32".into(), + max_atomic_width: Some(32), + features: "+m,+a,+c".into(), + panic_strategy: PanicStrategy::Unwind, + relocation_model: RelocModel::Static, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs new file mode 100644 index 00000000000..fe49951ebfd --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs @@ -0,0 +1,32 @@ +use crate::spec::{cvs, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), + llvm_target: "riscv32".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "nuttx".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + cpu: "generic-rv32".into(), + max_atomic_width: Some(32), + llvm_abiname: "ilp32f".into(), + features: "+m,+a,+c,+f".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + emit_debug_gdb_scripts: false, + eh_frame_header: false, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs new file mode 100644 index 00000000000..baee03e6833 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs @@ -0,0 +1,29 @@ +use crate::spec::{cvs, Cc, LinkerFlavor, Lld, PanicStrategy, RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), + llvm_target: "riscv32".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + pointer_width: 32, + arch: "riscv32".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "nuttx".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + cpu: "generic-rv32".into(), + max_atomic_width: Some(32), + features: "+m,+c".into(), + panic_strategy: PanicStrategy::Unwind, + relocation_model: RelocModel::Static, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs new file mode 100644 index 00000000000..6a468227ba6 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs @@ -0,0 +1,36 @@ +use crate::spec::SanitizerSet; +use crate::spec::{cvs, Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy}; +use crate::spec::{RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + llvm_target: "riscv64".into(), + pointer_width: 64, + arch: "riscv64".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "nuttx".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + llvm_abiname: "lp64d".into(), + cpu: "generic-rv64".into(), + max_atomic_width: Some(64), + features: "+m,+a,+f,+d,+c".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + code_model: Some(CodeModel::Medium), + emit_debug_gdb_scripts: false, + eh_frame_header: false, + supported_sanitizers: SanitizerSet::KERNELADDRESS, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs new file mode 100644 index 00000000000..1e18466c206 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs @@ -0,0 +1,36 @@ +use crate::spec::SanitizerSet; +use crate::spec::{cvs, Cc, CodeModel, LinkerFlavor, Lld, PanicStrategy}; +use crate::spec::{RelocModel, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: None, + host_tools: None, + std: None, + }, + llvm_target: "riscv64".into(), + pointer_width: 64, + arch: "riscv64".into(), + + options: TargetOptions { + families: cvs!["unix"], + os: "nuttx".into(), + linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes), + linker: Some("rust-lld".into()), + llvm_abiname: "lp64d".into(), + cpu: "generic-rv64".into(), + max_atomic_width: Some(64), + features: "+m,+a,+c".into(), + panic_strategy: PanicStrategy::Abort, + relocation_model: RelocModel::Static, + code_model: Some(CodeModel::Medium), + emit_debug_gdb_scripts: false, + eh_frame_header: false, + supported_sanitizers: SanitizerSet::KERNELADDRESS, + ..Default::default() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs new file mode 100644 index 00000000000..ac37f403860 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs @@ -0,0 +1,33 @@ +// Targets the Cortex-M0, Cortex-M0+ and Cortex-M1 processors (ARMv6-M architecture) + +use crate::spec::{base, cvs, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "thumbv6m-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(), + // The ARMv6-M architecture doesn't support unaligned loads/stores so we disable them + // with +strict-align. + // Also force-enable 32-bit atomics, which allows the use of atomic load/store only. + // The resulting atomics are ABI incompatible with atomics backed by libatomic. + features: "+strict-align,+atomics-32".into(), + // There are no atomic CAS instructions available in the instruction set of the ARMv6-M + // architecture + atomic_cas: false, + ..base::thumb::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs new file mode 100644 index 00000000000..e21facb78f9 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs @@ -0,0 +1,35 @@ +// Targets the Cortex-M4 and Cortex-M7 processors (ARMv7E-M) +// +// This target assumes that the device doesn't have a FPU (Floating Point Unit) and lowers all the +// floating point operations to software routines (intrinsics). +// +// As such, this target uses the "soft" calling convention (ABI) where floating point values are +// passed to/from subroutines via general purpose registers (R0, R1, etc.). +// +// To opt-in to hardware accelerated floating point operations, you can use, for example, +// `-C target-feature=+vfp4` or `-C target-cpu=cortex-m4`. + +use crate::spec::{base, cvs, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "thumbv7em-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(), + max_atomic_width: Some(32), + ..base::thumb::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs new file mode 100644 index 00000000000..45092711d2e --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs @@ -0,0 +1,43 @@ +// Targets the Cortex-M4F and Cortex-M7F processors (ARMv7E-M) +// +// This target assumes that the device does have a FPU (Floating Point Unit) and lowers all (single +// precision) floating point operations to hardware instructions. +// +// Additionally, 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.). +// +// To opt into double precision hardware support, use the `-C target-feature=+fp64` flag. + +use crate::spec::{base, cvs, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "thumbv7em-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(), + // vfp4 is the lowest common denominator between the Cortex-M4F (vfp4) and the + // Cortex-M7 (vfp5). + // Both the Cortex-M4 and the Cortex-M7 only have 16 double-precision registers + // available, and the Cortex-M4 only supports single-precision floating point operations + // whereas in the Cortex-M7 double-precision is optional. + // + // Reference: + // ARMv7-M Architecture Reference Manual - A2.5 The optional floating-point extension + features: "+vfp4d16sp".into(), + max_atomic_width: Some(32), + ..base::thumb::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs new file mode 100644 index 00000000000..4424b9eaade --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs @@ -0,0 +1,26 @@ +// Targets the Cortex-M3 processor (ARMv7-M) + +use crate::spec::{base, cvs, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "thumbv7m-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(), + max_atomic_width: Some(32), + ..base::thumb::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs new file mode 100644 index 00000000000..44c6fb0dd1b --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs @@ -0,0 +1,29 @@ +// Targets the Cortex-M23 processor (Baseline ARMv8-M) + +use crate::spec::{base, cvs, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "thumbv8m.base-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(), + // ARMv8-M baseline doesn't support unaligned loads/stores so we disable them + // with +strict-align. + features: "+strict-align".into(), + max_atomic_width: Some(32), + ..base::thumb::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs new file mode 100644 index 00000000000..2f0df7ae857 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs @@ -0,0 +1,27 @@ +// Targets the Cortex-M33 processor (Armv8-M Mainline architecture profile), +// without the Floating Point extension. + +use crate::spec::{base, cvs, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "thumbv8m.main-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(), + max_atomic_width: Some(32), + ..base::thumb::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs new file mode 100644 index 00000000000..ee31f17bc45 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs @@ -0,0 +1,32 @@ +// Targets the Cortex-M33 processor (Armv8-M Mainline architecture profile), +// with the Floating Point extension. + +use crate::spec::{base, cvs, Target, TargetOptions}; + +pub fn target() -> Target { + Target { + llvm_target: "thumbv8m.main-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(), + // If the Floating Point extension is implemented in the Cortex-M33 + // processor, the Cortex-M33 Technical Reference Manual states that + // the FPU uses the FPv5 architecture, single-precision instructions + // and 16 D registers. + features: "+fp-armv8d16sp".into(), + max_atomic_width: Some(32), + ..base::thumb::opts() + }, + } +} diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 6667efb14e2..4fb0323b6cf 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -4,6 +4,9 @@ use rustc_span::symbol::Symbol; /// Features that control behaviour of rustc, rather than the codegen. pub const RUSTC_SPECIFIC_FEATURES: &[&str] = &["crt-static"]; +/// Features that require special handling when passing to LLVM. +pub const RUSTC_SPECIAL_FEATURES: &[&str] = &["backchain"]; + /// Stability information for target features. #[derive(Debug, Clone, Copy)] pub enum Stability { @@ -397,6 +400,13 @@ const LOONGARCH_ALLOWED_FEATURES: &[(&str, Stability)] = &[ // tidy-alphabetical-end ]; +const IBMZ_ALLOWED_FEATURES: &[(&str, Stability)] = &[ + // tidy-alphabetical-start + ("backchain", Unstable(sym::s390x_target_feature)), + ("vector", Unstable(sym::s390x_target_feature)), + // tidy-alphabetical-end +]; + /// When rustdoc is running, provide a list of all known features so that all their respective /// primitives may be documented. /// @@ -414,6 +424,7 @@ pub fn all_known_features() -> impl Iterator<Item = (&'static str, Stability)> { .chain(BPF_ALLOWED_FEATURES.iter()) .chain(CSKY_ALLOWED_FEATURES) .chain(LOONGARCH_ALLOWED_FEATURES) + .chain(IBMZ_ALLOWED_FEATURES) .cloned() } @@ -431,6 +442,7 @@ impl super::spec::Target { "bpf" => BPF_ALLOWED_FEATURES, "csky" => CSKY_ALLOWED_FEATURES, "loongarch64" => LOONGARCH_ALLOWED_FEATURES, + "s390x" => IBMZ_ALLOWED_FEATURES, _ => &[], } } diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index f96bd985237..137850f31d3 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -1,3 +1,65 @@ +trait_selection_actual_impl_expl_but_actually_implemented_for_ty = ...but `{$trait_path}` is actually implemented for the type `{$ty}`{$has_lifetime -> + [true] , for some specific lifetime `'{$lifetime}` + *[false] {""} +} +trait_selection_actual_impl_expl_but_actually_implements_trait = ...but it actually implements `{$trait_path}`{$has_lifetime -> + [true] , for some specific lifetime `'{$lifetime}` + *[false] {""} +} +trait_selection_actual_impl_expl_but_actually_ty_implements = ...but `{$ty}` actually implements `{$trait_path}`{$has_lifetime -> + [true] , for some specific lifetime `'{$lifetime}` + *[false] {""} +} + +trait_selection_actual_impl_expl_expected_other_any = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`... +trait_selection_actual_impl_expl_expected_other_nothing = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$ty_or_sig}` must implement `{$trait_path}` + +trait_selection_actual_impl_expl_expected_other_some = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`... +trait_selection_actual_impl_expl_expected_other_two = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... +trait_selection_actual_impl_expl_expected_passive_any = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any lifetime `'{$lifetime_1}`... +trait_selection_actual_impl_expl_expected_passive_nothing = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}` +trait_selection_actual_impl_expl_expected_passive_some = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for some specific lifetime `'{$lifetime_1}`... +trait_selection_actual_impl_expl_expected_passive_two = {$leading_ellipsis -> + [true] ... + *[false] {""} +}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... +trait_selection_actual_impl_expl_expected_signature_any = {$leading_ellipsis -> + [true] ... + *[false] {""} +}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any lifetime `'{$lifetime_1}`... +trait_selection_actual_impl_expl_expected_signature_nothing = {$leading_ellipsis -> + [true] ... + *[false] {""} +}closure with signature `{$ty_or_sig}` must implement `{$trait_path}` +trait_selection_actual_impl_expl_expected_signature_some = {$leading_ellipsis -> + [true] ... + *[false] {""} +}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`... +trait_selection_actual_impl_expl_expected_signature_two = {$leading_ellipsis -> + [true] ... + *[false] {""} +}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for any two lifetimes `'{$lifetime_1}` and `'{$lifetime_2}`... trait_selection_adjust_signature_borrow = consider adjusting the signature so it borrows its {$len -> [one] argument *[other] arguments @@ -8,8 +70,48 @@ trait_selection_adjust_signature_remove_borrow = consider adjusting the signatur *[other] arguments } +trait_selection_ascribe_user_type_prove_predicate = ...so that the where clause holds + trait_selection_async_closure_not_fn = async closure does not implement `{$kind}` because it captures state from its environment +trait_selection_await_both_futures = consider `await`ing on both `Future`s +trait_selection_await_future = consider `await`ing on the `Future` +trait_selection_await_note = calling an async function returns a future + +trait_selection_but_calling_introduces = {$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$lifetime_kind -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` +} but calling `{$assoc_item}` introduces an implicit `'static` lifetime requirement + .label1 = {$has_lifetime -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` + } + .label2 = ...is used and required to live as long as `'static` here because of an implicit lifetime bound on the {$has_impl_path -> + [true] `impl` of `{$impl_path}` + *[false] inherent `impl` + } + +trait_selection_but_needs_to_satisfy = {$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$has_lifetime -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` +} but it needs to satisfy a `'static` lifetime requirement + .influencer = this data with {$has_lifetime -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` + }... + .require = {$spans_empty -> + *[true] ...is used and required to live as long as `'static` here + [false] ...and is required to live as long as `'static` here + } + .used_here = ...is used here... + .introduced_by_bound = `'static` lifetime requirement introduced by this bound + trait_selection_closure_fn_mut_label = closure is `{$trait_prefix}FnMut` because it mutates the variable `{$place}` here trait_selection_closure_fn_once_label = closure is `{$trait_prefix}FnOnce` because it moves the variable `{$place}` out of its environment @@ -19,18 +121,67 @@ trait_selection_closure_kind_mismatch = expected a closure that implements the ` trait_selection_closure_kind_requirement = the requirement to implement `{$trait_prefix}{$expected}` derives from here +trait_selection_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait +trait_selection_consider_specifying_length = consider specifying the actual array length +trait_selection_data_flows = ...but data{$label_var1_exists -> + [true] {" "}from `{$label_var1}` + *[false] {""} +} flows{$label_var2_exists -> + [true] {" "}into `{$label_var2}` + *[false] {""} +} here + +trait_selection_data_lifetime_flow = ...but data with one lifetime flows into the other here +trait_selection_data_returned = ...but data{$label_var1_exists -> + [true] {" "}from `{$label_var1}` + *[false] {""} +} is returned here + +trait_selection_declared_different = this parameter and the return type are declared with different lifetimes... +trait_selection_declared_multiple = this type is declared with multiple lifetimes... trait_selection_disallowed_positional_argument = positional format arguments are not allowed here .help = only named format arguments with the name of one of the generic types are allowed in this context +trait_selection_does_not_outlive_static_from_impl = ...does not necessarily outlive the static lifetime introduced by the compatible `impl` +trait_selection_dtcs_has_lifetime_req_label = this has an implicit `'static` lifetime requirement +trait_selection_dtcs_has_req_note = the used `impl` has a `'static` requirement +trait_selection_dtcs_introduces_requirement = calling this method introduces the `impl`'s `'static` requirement +trait_selection_dtcs_suggestion = consider relaxing the implicit `'static` requirement + trait_selection_dump_vtable_entries = vtable entries for `{$trait_ref}`: {$entries} trait_selection_empty_on_clause_in_rustc_on_unimplemented = empty `on`-clause in `#[rustc_on_unimplemented]` .label = empty on-clause here +trait_selection_explicit_lifetime_required_sugg_with_ident = add explicit lifetime `{$named}` to the type of `{$simple_ident}` + +trait_selection_explicit_lifetime_required_sugg_with_param_type = add explicit lifetime `{$named}` to type + +trait_selection_explicit_lifetime_required_with_ident = explicit lifetime required in the type of `{$simple_ident}` + .label = lifetime `{$named}` required + +trait_selection_explicit_lifetime_required_with_param_type = explicit lifetime required in parameter type + .label = lifetime `{$named}` required + +trait_selection_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}` + +trait_selection_fn_uniq_types = different fn items have unique types, even if their signatures are the same +trait_selection_fps_cast = consider casting to a fn pointer +trait_selection_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}` + +trait_selection_fps_items_are_distinct = fn items are distinct from fn pointers +trait_selection_fps_remove_ref = consider removing the reference +trait_selection_fps_use_ref = consider using a reference +trait_selection_fulfill_req_lifetime = the type `{$ty}` does not fulfill the required lifetime + +trait_selection_full_type_written = the full type name has been written to '{$path}' + trait_selection_ignored_diagnostic_option = `{$option_name}` is ignored due to previous definition of `{$option_name}` .other_label = `{$option_name}` is first declared here .label = `{$option_name}` is already declared here +trait_selection_implicit_static_lifetime_note = this has an implicit `'static` lifetime requirement +trait_selection_implicit_static_lifetime_suggestion = consider relaxing the implicit `'static` requirement trait_selection_inherent_projection_normalization_overflow = overflow evaluating associated type `{$ty}` trait_selection_invalid_format_specifier = invalid format specifier @@ -39,13 +190,52 @@ trait_selection_invalid_format_specifier = invalid format specifier trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-clause in `#[rustc_on_unimplemented]` .label = invalid on-clause here +trait_selection_label_bad = {$bad_kind -> + *[other] cannot infer type + [more_info] cannot infer {$prefix_kind -> + *[type] type for {$prefix} + [const_with_param] the value of const parameter + [const] the value of the constant + } `{$name}`{$has_parent -> + [true] {" "}declared on the {$parent_prefix} `{$parent_name}` + *[false] {""} + } +} + +trait_selection_lf_bound_not_satisfied = lifetime bound not satisfied +trait_selection_lifetime_mismatch = lifetime mismatch + +trait_selection_lifetime_param_suggestion = consider {$is_reuse -> + [true] reusing + *[false] introducing +} a named lifetime parameter{$is_impl -> + [true] {" "}and update trait if needed + *[false] {""} +} +trait_selection_lifetime_param_suggestion_elided = each elided lifetime in input position becomes a distinct lifetime + trait_selection_malformed_on_unimplemented_attr = malformed `on_unimplemented` attribute .help = only `message`, `note` and `label` are allowed as options .label = invalid option found here +trait_selection_meant_byte_literal = if you meant to write a byte literal, prefix with `b` +trait_selection_meant_char_literal = if you meant to write a `char` literal, use single quotes +trait_selection_meant_str_literal = if you meant to write a string literal, use double quotes +trait_selection_mismatched_static_lifetime = incompatible lifetime on type trait_selection_missing_options_for_on_unimplemented_attr = missing options for `on_unimplemented` attribute .help = at least one of the `message`, `note` and `label` options are expected +trait_selection_more_targeted = {$has_param_name -> + [true] `{$param_name}` + *[false] `fn` parameter +} has {$has_lifetime -> + [true] lifetime `{$lifetime}` + *[false] an anonymous lifetime `'_` +} but calling `{$ident}` introduces an implicit `'static` lifetime requirement + +trait_selection_msl_introduces_static = introduces a `'static` lifetime requirement +trait_selection_msl_unmet_req = because this has an unmet lifetime requirement + trait_selection_negative_positive_conflict = found both positive and negative implementation of trait `{$trait_desc}`{$self_desc -> [none] {""} *[default] {" "}for type `{$self_desc}` @@ -59,13 +249,214 @@ trait_selection_no_value_in_rustc_on_unimplemented = this attribute must have a .label = expected value here .note = eg `#[rustc_on_unimplemented(message="foo")]` +trait_selection_nothing = {""} + +trait_selection_oc_cant_coerce = cannot coerce intrinsics to function pointers +trait_selection_oc_closure_selfref = closure/coroutine type that references itself +trait_selection_oc_const_compat = const not compatible with trait +trait_selection_oc_fn_lang_correct_type = {$lang_item_name -> + [panic_impl] `#[panic_handler]` + *[lang_item_name] lang item `{$lang_item_name}` + } function has wrong type +trait_selection_oc_fn_main_correct_type = `main` function has wrong type +trait_selection_oc_fn_start_correct_type = `#[start]` function has wrong type +trait_selection_oc_generic = mismatched types + +trait_selection_oc_if_else_different = `if` and `else` have incompatible types +trait_selection_oc_intrinsic_correct_type = intrinsic has wrong type +trait_selection_oc_match_compat = `match` arms have incompatible types +trait_selection_oc_method_compat = method not compatible with trait +trait_selection_oc_method_correct_type = mismatched `self` parameter type +trait_selection_oc_no_diverge = `else` clause of `let...else` does not diverge +trait_selection_oc_no_else = `if` may be missing an `else` clause +trait_selection_oc_try_compat = `?` operator has incompatible types +trait_selection_oc_type_compat = type not compatible with trait +trait_selection_opaque_captures_lifetime = hidden type for `{$opaque_ty}` captures lifetime that does not appear in bounds + .label = opaque type defined here + +trait_selection_outlives_bound = lifetime of the source pointer does not outlive lifetime bound of the object type +trait_selection_outlives_content = lifetime of reference outlives lifetime of borrowed content... + +trait_selection_precise_capturing_existing = add `{$new_lifetime}` to the `use<...>` bound to explicitly capture it +trait_selection_precise_capturing_new = add a `use<...>` bound to explicitly capture `{$new_lifetime}` + +trait_selection_precise_capturing_new_but_apit = add a `use<...>` bound to explicitly capture `{$new_lifetime}` after turning all argument-position `impl Trait` into type parameters, noting that this possibly affects the API of this crate + +trait_selection_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here... +trait_selection_prlf_defined_without_sub = the lifetime defined here... +trait_selection_prlf_known_limitation = this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information) + +trait_selection_prlf_must_outlive_with_sup = ...must outlive the lifetime `{$sup_symbol}` defined here +trait_selection_prlf_must_outlive_without_sup = ...must outlive the lifetime defined here +trait_selection_reborrow = ...so that reference does not outlive borrowed content +trait_selection_ref_longer_than_data = in type `{$ty}`, reference has a longer lifetime than the data it references + +trait_selection_reference_outlives_referent = ...so that the reference type `{$name}` does not outlive the data it points at +trait_selection_region_explanation = {$pref_kind -> + *[should_not_happen] [{$pref_kind}] + [ref_valid_for] ...the reference is valid for + [content_valid_for] ...but the borrowed content is only valid for + [type_obj_valid_for] object type is valid for + [source_pointer_valid_for] source pointer is only valid for + [type_satisfy] type must satisfy + [type_outlive] type must outlive + [lf_param_instantiated_with] lifetime parameter instantiated with + [lf_param_must_outlive] but lifetime parameter must outlive + [lf_instantiated_with] lifetime instantiated with + [lf_must_outlive] but lifetime must outlive + [pointer_valid_for] the pointer is valid for + [data_valid_for] but the referenced data is only valid for + [empty] {""} +}{$pref_kind -> + [empty] {""} + *[other] {" "} +}{$desc_kind -> + *[should_not_happen] [{$desc_kind}] + [restatic] the static lifetime + [revar] lifetime {$desc_arg} + [as_defined] the lifetime `{$desc_arg}` as defined here + [as_defined_anon] the anonymous lifetime as defined here + [defined_here] the anonymous lifetime defined here + [defined_here_reg] the lifetime `{$desc_arg}` as defined here +}{$suff_kind -> + *[should_not_happen] [{$suff_kind}] + [empty]{""} + [continues] ... + [req_by_binding] {" "}as required by this binding +} + +trait_selection_relate_object_bound = ...so that it can be closed over into an object +trait_selection_relate_param_bound = ...so that the type `{$name}` will meet its required lifetime bounds{$continues -> + [true] ... + *[false] {""} +} +trait_selection_relate_param_bound_2 = ...that is required by this bound +trait_selection_relate_region_param_bound = ...so that the declared lifetime parameter bounds are satisfied +trait_selection_ril_because_of = because of this returned expression +trait_selection_ril_introduced_by = requirement introduced by this return type +trait_selection_ril_introduced_here = `'static` requirement introduced here +trait_selection_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type + +trait_selection_source_kind_closure_return = + try giving this closure an explicit return type + +# coroutine_kind may need to be translated +trait_selection_source_kind_fully_qualified = + try using a fully qualified path to specify the expected types + +trait_selection_source_kind_subdiag_generic_label = + cannot infer {$is_type -> + [true] type + *[false] the value + } of the {$is_type -> + [true] type + *[false] const + } {$parent_exists -> + [true] parameter `{$param_name}` declared on the {$parent_prefix} `{$parent_name}` + *[false] parameter {$param_name} + } + +trait_selection_source_kind_subdiag_generic_suggestion = + consider specifying the generic {$arg_count -> + [one] argument + *[other] arguments + } + +trait_selection_source_kind_subdiag_let = {$kind -> + [with_pattern] consider giving `{$name}` an explicit type + [closure] consider giving this closure parameter an explicit type + *[other] consider giving this pattern a type +}{$x_kind -> + [has_name] , where the {$prefix_kind -> + *[type] type for {$prefix} + [const_with_param] value of const parameter + [const] value of the constant + } `{$arg_name}` is specified + [underscore] , where the placeholders `_` are specified + *[empty] {""} +} + +trait_selection_srs_add = consider returning the local binding `{$ident}` +trait_selection_srs_add_one = consider returning one of these bindings + +trait_selection_srs_remove = consider removing this semicolon +trait_selection_srs_remove_and_box = consider removing this semicolon and boxing the expressions +trait_selection_stp_wrap_many = try wrapping the pattern in a variant of `{$path}` + +trait_selection_stp_wrap_one = try wrapping the pattern in `{$variant}` +trait_selection_subtype = ...so that the {$requirement -> + [method_compat] method type is compatible with trait + [type_compat] associated type is compatible with trait + [const_compat] const is compatible with trait + [expr_assignable] expression is assignable + [if_else_different] `if` and `else` have incompatible types + [no_else] `if` missing an `else` returns `()` + [fn_main_correct_type] `main` function has the correct type + [fn_start_correct_type] `#[start]` function has the correct type + [fn_lang_correct_type] lang item function has the correct type + [intrinsic_correct_type] intrinsic has the correct type + [method_correct_type] method receiver has the correct type + *[other] types are compatible +} +trait_selection_subtype_2 = ...so that {$requirement -> + [method_compat] method type is compatible with trait + [type_compat] associated type is compatible with trait + [const_compat] const is compatible with trait + [expr_assignable] expression is assignable + [if_else_different] `if` and `else` have incompatible types + [no_else] `if` missing an `else` returns `()` + [fn_main_correct_type] `main` function has the correct type + [fn_start_correct_type] `#[start]` function has the correct type + [fn_lang_correct_type] lang item function has the correct type + [intrinsic_correct_type] intrinsic has the correct type + [method_correct_type] method receiver has the correct type + *[other] types are compatible +} + +trait_selection_suggest_accessing_field = you might have meant to use field `{$name}` whose type is `{$ty}` + +trait_selection_suggest_add_let_for_letchains = consider adding `let` + +trait_selection_tid_consider_borrowing = consider borrowing this type parameter in the trait +trait_selection_tid_param_help = the lifetime requirements from the `impl` do not correspond to the requirements in the `trait` + +trait_selection_tid_rel_help = verify the lifetime relationships in the `trait` and `impl` between the `self` argument, the other inputs and its output trait_selection_trait_has_no_impls = this trait has no implementations, consider adding one +trait_selection_trait_impl_diff = `impl` item signature doesn't match `trait` item signature + .found = found `{$found}` + .expected = expected `{$expected}` + .expected_found = expected signature `{$expected}` + {" "}found signature `{$found}` + +trait_selection_trait_placeholder_mismatch = implementation of `{$trait_def_id}` is not general enough + .label_satisfy = doesn't satisfy where-clause + .label_where = due to a where-clause on `{$def_id}`... + .label_dup = implementation of `{$trait_def_id}` is not general enough + +trait_selection_try_cannot_convert = `?` operator cannot convert from `{$found}` to `{$expected}` + +trait_selection_tuple_trailing_comma = use a trailing comma to create a tuple with one element + trait_selection_ty_alias_overflow = in case this is a recursive type alias, consider using a struct, enum, or union instead +trait_selection_type_annotations_needed = {$source_kind -> + [closure] type annotations needed for the closure `{$source_name}` + [normal] type annotations needed for `{$source_name}` + *[other] type annotations needed +} + .label = type must be known at this point + +trait_selection_types_declared_different = these two types are declared with different lifetimes... + trait_selection_unable_to_construct_constant_value = unable to construct a constant value for the unevaluated constant {$unevaluated} trait_selection_unknown_format_parameter_for_on_unimplemented_attr = there is no parameter `{$argument_name}` on trait `{$trait_name}` .help = expect either a generic argument name or {"`{Self}`"} as format argument +trait_selection_warn_removing_apit_params = you could use a `use<...>` bound to explicitly capture `{$new_lifetime}`, but argument-position `impl Trait`s are not nameable + +trait_selection_where_copy_predicates = copy the `where` clause predicates from the trait + +trait_selection_where_remove = remove the `where` clause trait_selection_wrapped_parser_error = {$description} .label = {$label} diff --git a/compiler/rustc_infer/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 9998fbca056..daabdec8f9e 100644 --- a/compiler/rustc_infer/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -46,14 +46,12 @@ //! time of error detection. use std::borrow::Cow; -use std::ops::{ControlFlow, Deref}; +use std::ops::ControlFlow; use std::path::PathBuf; use std::{cmp, fmt, iter}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_errors::{ - pluralize, Applicability, Diag, DiagCtxtHandle, DiagStyledString, IntoDiagArg, StringPart, -}; +use rustc_errors::{pluralize, Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -72,23 +70,23 @@ use rustc_middle::ty::{ use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span}; use rustc_target::spec::abi; +use crate::error_reporting::TypeErrCtxt; use crate::errors::{ObligationCauseFailureCode, TypeErrorAdditionalDiags}; use crate::infer; use crate::infer::relate::{self, RelateResult, TypeRelation}; use crate::infer::{InferCtxt, TypeTrace, ValuePairs}; use crate::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, - PredicateObligation, }; mod note_and_explain; mod suggest; +pub mod need_type_info; +pub mod nice_region_error; pub mod region; pub mod sub_relations; -pub mod nice_region_error; - /// Makes a valid string literal from a string by escaping special characters (" and \), /// unless they are already escaped. fn escape_literal(s: &str) -> String { @@ -111,48 +109,59 @@ fn escape_literal(s: &str) -> String { escaped } -/// A helper for building type related errors. The `typeck_results` -/// field is only populated during an in-progress typeck. -/// Get an instance by calling `InferCtxt::err_ctxt` or `FnCtxt::err_ctxt`. -/// -/// You must only create this if you intend to actually emit an error (or -/// perhaps a warning, though preferably not.) It provides a lot of utility -/// methods which should not be used during the happy path. -pub struct TypeErrCtxt<'a, 'tcx> { - pub infcx: &'a InferCtxt<'tcx>, - pub sub_relations: std::cell::RefCell<sub_relations::SubRelations>, - - pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>, - pub fallback_has_occurred: bool, +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + // [Note-Type-error-reporting] + // An invariant is that anytime the expected or actual type is Error (the special + // error type, meaning that an error occurred when typechecking this expression), + // this is a derived error. The error cascaded from another error (that was already + // reported), so it's not useful to display it to the user. + // The following methods implement this logic. + // They check if either the actual or expected type is Error, and don't print the error + // in this case. The typechecker should only ever report type errors involving mismatched + // types using one of these methods, and should not call span_err directly for such + // errors. + pub fn type_error_struct_with_diag<M>( + &self, + sp: Span, + mk_diag: M, + actual_ty: Ty<'tcx>, + ) -> Diag<'a> + where + M: FnOnce(String) -> Diag<'a>, + { + let actual_ty = self.resolve_vars_if_possible(actual_ty); + debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty); - pub normalize_fn_sig: Box<dyn Fn(ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> + 'a>, + let mut err = mk_diag(self.ty_to_string(actual_ty)); - pub autoderef_steps: - Box<dyn Fn(Ty<'tcx>) -> Vec<(Ty<'tcx>, Vec<PredicateObligation<'tcx>>)> + 'a>, -} + // Don't report an error if actual type is `Error`. + if actual_ty.references_error() { + err.downgrade_to_delayed_bug(); + } -impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - pub fn dcx(&self) -> DiagCtxtHandle<'a> { - self.infcx.dcx() + err } - /// This is just to avoid a potential footgun of accidentally - /// dropping `typeck_results` by calling `InferCtxt::err_ctxt` - #[deprecated(note = "you already have a `TypeErrCtxt`")] - #[allow(unused)] - pub fn err_ctxt(&self) -> ! { - bug!("called `err_ctxt` on `TypeErrCtxt`. Try removing the call"); + pub fn report_mismatched_types( + &self, + cause: &ObligationCause<'tcx>, + expected: Ty<'tcx>, + actual: Ty<'tcx>, + err: TypeError<'tcx>, + ) -> Diag<'a> { + self.report_and_explain_type_error(TypeTrace::types(cause, true, expected, actual), err) } -} -impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> { - type Target = InferCtxt<'tcx>; - fn deref(&self) -> &InferCtxt<'tcx> { - self.infcx + pub fn report_mismatched_consts( + &self, + cause: &ObligationCause<'tcx>, + expected: ty::Const<'tcx>, + actual: ty::Const<'tcx>, + err: TypeError<'tcx>, + ) -> Diag<'a> { + self.report_and_explain_type_error(TypeTrace::consts(cause, true, expected, actual), err) } -} -impl<'tcx> InferCtxt<'tcx> { pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { let (def_id, args) = match *ty.kind() { ty::Alias(_, ty::AliasTy { def_id, args, .. }) @@ -189,9 +198,7 @@ impl<'tcx> InferCtxt<'tcx> { .flatten() }) } -} -impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// Adds a note if the types come from similarly named crates fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) { use hir::def_id::CrateNum; @@ -2190,32 +2197,3 @@ impl TyCategory { } } } - -impl<'tcx> InferCtxt<'tcx> { - /// Given a [`hir::Block`], get the span of its last expression or - /// statement, peeling off any inner blocks. - pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span { - let block = block.innermost_block(); - if let Some(expr) = &block.expr { - expr.span - } else if let Some(stmt) = block.stmts.last() { - // possibly incorrect trailing `;` in the else arm - stmt.span - } else { - // empty block; point at its entirety - block.span - } - } - - /// Given a [`hir::HirId`] for a block, get the span of its last expression - /// or statement, peeling off any inner blocks. - pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span { - match self.tcx.hir_node(hir_id) { - hir::Node::Block(blk) => self.find_block_span(blk), - // The parser was in a weird state if either of these happen, but - // it's better not to panic. - hir::Node::Expr(e) => e.span, - _ => rustc_span::DUMMY_SP, - } - } -} diff --git a/compiler/rustc_infer/src/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 4f3dcd9043f..56ea70bcf1d 100644 --- a/compiler/rustc_infer/src/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -1,4 +1,4 @@ -use crate::error_reporting::infer::TypeErrCtxt; +use crate::error_reporting::TypeErrCtxt; use crate::errors::{ AmbiguousImpl, AmbiguousReturn, AnnotationRequired, InferenceBadError, SourceKindMultiSuggestion, SourceKindSubdiag, @@ -13,7 +13,6 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource}; use rustc_middle::bug; use rustc_middle::hir::nested_filter; -use rustc_middle::infer::unify_key::ConstVariableValue; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; use rustc_middle::ty::{ @@ -183,9 +182,7 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinte warn!("resolved ty var in error message"); } - let mut infcx_inner = infcx.inner.borrow_mut(); - let ty_vars = infcx_inner.type_variables(); - let var_origin = ty_vars.var_origin(ty_vid); + let var_origin = infcx.type_var_origin(ty_vid); if let Some(def_id) = var_origin.param_def_id // The `Self` param of a trait has the def-id of the trait, // since it's a synthetic parameter. @@ -206,24 +203,8 @@ fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'tcx>, ns: Namespace) -> FmtPrinte } }; printer.ty_infer_name_resolver = Some(Box::new(ty_getter)); - let const_getter = move |ct_vid| match infcx - .inner - .borrow_mut() - .const_unification_table() - .probe_value(ct_vid) - { - ConstVariableValue::Known { value: _ } => { - warn!("resolved const var in error message"); - None - } - ConstVariableValue::Unknown { origin, universe: _ } => { - if let Some(def_id) = origin.param_def_id { - Some(infcx.tcx.item_name(def_id)) - } else { - None - } - } - }; + let const_getter = + move |ct_vid| Some(infcx.tcx.item_name(infcx.const_var_origin(ct_vid)?.param_def_id?)); printer.const_infer_name_resolver = Some(Box::new(const_getter)); printer } @@ -289,7 +270,7 @@ fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'tcx>, ty: Ty<'tcx>) -> String { format!("fn({args}){ret}") } -impl<'tcx> InferCtxt<'tcx> { +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// Extracts data used by diagnostic for either types or constants /// which were stuck during inference. pub fn extract_inference_diagnostics_data( @@ -300,9 +281,7 @@ impl<'tcx> InferCtxt<'tcx> { match arg.unpack() { GenericArgKind::Type(ty) => { if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() { - let mut inner = self.inner.borrow_mut(); - let ty_vars = &inner.type_variables(); - let var_origin = ty_vars.var_origin(ty_vid); + let var_origin = self.infcx.type_var_origin(ty_vid); if let Some(def_id) = var_origin.param_def_id // The `Self` param of a trait has the def-id of the trait, // since it's a synthetic parameter. @@ -332,13 +311,7 @@ impl<'tcx> InferCtxt<'tcx> { } GenericArgKind::Const(ct) => { if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() { - let origin = - match self.inner.borrow_mut().const_unification_table().probe_value(vid) { - ConstVariableValue::Known { value } => { - bug!("resolved infer var: {vid:?} {value}") - } - ConstVariableValue::Unknown { origin, universe: _ } => origin, - }; + let origin = self.const_var_origin(vid).expect("expected unresolved const var"); if let Some(def_id) = origin.param_def_id { return InferenceDiagnosticsData { name: self.tcx.item_name(def_id).to_string(), @@ -391,7 +364,7 @@ impl<'tcx> InferCtxt<'tcx> { span: Span, arg_data: InferenceDiagnosticsData, error_code: TypeAnnotationNeeded, - ) -> Diag<'_> { + ) -> Diag<'a> { let source_kind = "other"; let source_name = ""; let failure_span = None; @@ -434,9 +407,7 @@ impl<'tcx> InferCtxt<'tcx> { }), } } -} -impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { #[instrument(level = "debug", skip(self, error_code))] pub fn emit_inference_failure_err( &self, @@ -453,7 +424,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // If we don't have any typeck results we're outside // of a body, so we won't be able to get better info // here. - return self.infcx.bad_inference_failure_err(failure_span, arg_data, error_code); + return self.bad_inference_failure_err(failure_span, arg_data, error_code); }; let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, arg); @@ -465,7 +436,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } let Some(InferSource { span, kind }) = local_visitor.infer_source else { - return self.infcx.bad_inference_failure_err(failure_span, arg_data, error_code); + return self.bad_inference_failure_err(failure_span, arg_data, error_code); }; let (source_kind, name, path) = kind.ty_localized_msg(self); @@ -887,7 +858,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { use ty::InferConst::*; match (inner_ct.kind(), target_ct.kind()) { (ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => { - self.tecx.inner.borrow_mut().const_unification_table().unioned(a_vid, b_vid) + self.tecx.root_const_var(a_vid) == self.tecx.root_const_var(b_vid) } _ => false, } diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/different_lifetimes.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs index 74dcde03639..74dcde03639 100644 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/different_lifetimes.rs diff --git a/compiler/rustc_infer/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 b91b755d683..b91b755d683 100644 --- a/compiler/rustc_infer/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 diff --git a/compiler/rustc_infer/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 550cc455e01..550cc455e01 100644 --- a/compiler/rustc_infer/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 diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mod.rs index ced4c384f02..b83ecd8320c 100644 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/mod.rs @@ -1,4 +1,4 @@ -use crate::error_reporting::infer::TypeErrCtxt; +use crate::error_reporting::TypeErrCtxt; use crate::infer::RegionResolutionError; use crate::infer::RegionResolutionError::*; use rustc_errors::{Diag, ErrorGuaranteed}; diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs index d1802d2f5ee..d1802d2f5ee 100644 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/named_anon_conflict.rs diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_error.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs index 476ac3f1720..476ac3f1720 100644 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_error.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_error.rs diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_relation.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs index e9f17a3e3e2..e9f17a3e3e2 100644 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/placeholder_relation.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/placeholder_relation.rs diff --git a/compiler/rustc_infer/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 ce157ff3dc8..ce157ff3dc8 100644 --- a/compiler/rustc_infer/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 diff --git a/compiler/rustc_infer/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 c58c7e13551..c58c7e13551 100644 --- a/compiler/rustc_infer/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 diff --git a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/util.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs index 30fa98c5526..30fa98c5526 100644 --- a/compiler/rustc_infer/src/error_reporting/infer/nice_region_error/util.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/util.rs diff --git a/compiler/rustc_infer/src/error_reporting/infer/note.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs index aeb3049c2ae..aeb3049c2ae 100644 --- a/compiler/rustc_infer/src/error_reporting/infer/note.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note.rs diff --git a/compiler/rustc_infer/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index d5e7de897d0..f9110cfb3b9 100644 --- a/compiler/rustc_infer/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -1,4 +1,3 @@ -use super::TypeErrCtxt; use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect}; use rustc_errors::{pluralize, Diag, MultiSpan}; use rustc_hir as hir; @@ -12,6 +11,9 @@ use rustc_middle::{ }; use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol}; +use crate::error_reporting::TypeErrCtxt; +use crate::infer::InferCtxtExt; + impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub fn note_and_explain_type_err( &self, @@ -820,7 +822,7 @@ fn foo(&self) -> Self::T { String::new() } tcx.defaultness(item.id.owner_id) { let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity(); - if self.infcx.can_eq_shallow(param_env, assoc_ty, found) { + if self.infcx.can_eq(param_env, assoc_ty, found) { diag.span_label( item.span, "associated type defaults can't be assumed inside the \ @@ -843,7 +845,7 @@ fn foo(&self) -> Self::T { String::new() } let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity(); if let hir::Defaultness::Default { has_value: true } = tcx.defaultness(item.id.owner_id) - && self.infcx.can_eq_shallow(param_env, assoc_ty, found) + && self.infcx.can_eq(param_env, assoc_ty, found) { diag.span_label( item.span, diff --git a/compiler/rustc_infer/src/error_reporting/infer/region.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs index 5d41bb5d271..3cee8ff5f4c 100644 --- a/compiler/rustc_infer/src/error_reporting/infer/region.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/region.rs @@ -18,7 +18,8 @@ use rustc_type_ir::Upcast as _; use super::nice_region_error::find_anon_type; use super::{nice_region_error, ObligationCauseAsDiagArg}; -use crate::error_reporting::infer::{ObligationCauseExt as _, TypeErrCtxt}; +use crate::error_reporting::infer::ObligationCauseExt as _; +use crate::error_reporting::TypeErrCtxt; use crate::errors::{ self, note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent, RefLongerThanData, RegionOriginNote, WhereClauseSuggestions, @@ -224,16 +225,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } .add_to_diag(err), infer::Reborrow(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err) + RegionOriginNote::Plain { span, msg: fluent::trait_selection_reborrow } + .add_to_diag(err) } infer::RelateObjectBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound } + RegionOriginNote::Plain { span, msg: fluent::trait_selection_relate_object_bound } .add_to_diag(err); } infer::ReferenceOutlivesReferent(ty, span) => { RegionOriginNote::WithName { span, - msg: fluent::infer_reference_outlives_referent, + msg: fluent::trait_selection_reference_outlives_referent, name: &self.ty_to_string(ty), continues: false, } @@ -242,23 +244,32 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { infer::RelateParamBound(span, ty, opt_span) => { RegionOriginNote::WithName { span, - msg: fluent::infer_relate_param_bound, + msg: fluent::trait_selection_relate_param_bound, name: &self.ty_to_string(ty), continues: opt_span.is_some(), } .add_to_diag(err); if let Some(span) = opt_span { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 } - .add_to_diag(err); + RegionOriginNote::Plain { + span, + msg: fluent::trait_selection_relate_param_bound_2, + } + .add_to_diag(err); } } infer::RelateRegionParamBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound } - .add_to_diag(err); + RegionOriginNote::Plain { + span, + msg: fluent::trait_selection_relate_region_param_bound, + } + .add_to_diag(err); } infer::CompareImplItemObligation { span, .. } => { - RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation } - .add_to_diag(err); + RegionOriginNote::Plain { + span, + msg: fluent::trait_selection_compare_impl_item_obligation, + } + .add_to_diag(err); } infer::CheckAssociatedTypeBounds { ref parent, .. } => { self.note_region_origin(err, parent); @@ -266,7 +277,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { infer::AscribeUserTypeProvePredicate(span) => { RegionOriginNote::Plain { span, - msg: fluent::infer_ascribe_user_type_prove_predicate, + msg: fluent::trait_selection_ascribe_user_type_prove_predicate, } .add_to_diag(err); } @@ -445,7 +456,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) } infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => { - let mut err = self.infcx.report_extra_impl_obligation( + let mut err = self.report_extra_impl_obligation( span, impl_item_def_id, trait_item_def_id, @@ -645,7 +656,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { trait_item_def_id, }) = origin { - return self.infcx.report_extra_impl_obligation( + return self.report_extra_impl_obligation( span, impl_item_def_id, trait_item_def_id, diff --git a/compiler/rustc_infer/src/error_reporting/infer/sub_relations.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs index ef26a8ff7b8..ef26a8ff7b8 100644 --- a/compiler/rustc_infer/src/error_reporting/infer/sub_relations.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/sub_relations.rs diff --git a/compiler/rustc_infer/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index 4d11ab9fac6..1ef32d110b3 100644 --- a/compiler/rustc_infer/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -17,14 +17,13 @@ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt}; use rustc_span::{sym, Span}; +use crate::error_reporting::TypeErrCtxt; use crate::errors::{ ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags, }; -use super::TypeErrCtxt; - #[derive(Clone, Copy)] pub enum SuggestAsRefKind { Option, diff --git a/compiler/rustc_trait_selection/src/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/mod.rs index f6ac8fc7b61..cb7efeaae0b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/mod.rs @@ -1 +1,73 @@ +use std::ops::Deref; + +use rustc_errors::DiagCtxtHandle; +use rustc_infer::infer::InferCtxt; +use rustc_infer::traits::PredicateObligation; +use rustc_macros::extension; +use rustc_middle::bug; +use rustc_middle::ty::{self, Ty}; + +use crate::error_reporting::infer::sub_relations; + +pub mod infer; pub mod traits; + +/// A helper for building type related errors. The `typeck_results` +/// field is only populated during an in-progress typeck. +/// Get an instance by calling `InferCtxt::err_ctxt` or `FnCtxt::err_ctxt`. +/// +/// You must only create this if you intend to actually emit an error (or +/// perhaps a warning, though preferably not.) It provides a lot of utility +/// methods which should not be used during the happy path. +pub struct TypeErrCtxt<'a, 'tcx> { + pub infcx: &'a InferCtxt<'tcx>, + pub sub_relations: std::cell::RefCell<sub_relations::SubRelations>, + + pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>, + pub fallback_has_occurred: bool, + + pub normalize_fn_sig: Box<dyn Fn(ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> + 'a>, + + pub autoderef_steps: + Box<dyn Fn(Ty<'tcx>) -> Vec<(Ty<'tcx>, Vec<PredicateObligation<'tcx>>)> + 'a>, +} + +#[extension(pub trait InferCtxtErrorExt<'tcx>)] +impl<'tcx> InferCtxt<'tcx> { + /// Creates a `TypeErrCtxt` for emitting various inference errors. + /// During typeck, use `FnCtxt::err_ctxt` instead. + fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> { + TypeErrCtxt { + infcx: self, + sub_relations: Default::default(), + typeck_results: None, + fallback_has_occurred: false, + normalize_fn_sig: Box::new(|fn_sig| fn_sig), + autoderef_steps: Box::new(|ty| { + debug_assert!(false, "shouldn't be using autoderef_steps outside of typeck"); + vec![(ty, vec![])] + }), + } + } +} + +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + pub fn dcx(&self) -> DiagCtxtHandle<'a> { + self.infcx.dcx() + } + + /// This is just to avoid a potential footgun of accidentally + /// dropping `typeck_results` by calling `InferCtxt::err_ctxt` + #[deprecated(note = "you already have a `TypeErrCtxt`")] + #[allow(unused)] + pub fn err_ctxt(&self) -> ! { + bug!("called `err_ctxt` on `TypeErrCtxt`. Try removing the call"); + } +} + +impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> { + type Target = InferCtxt<'tcx>; + fn deref(&self) -> &InferCtxt<'tcx> { + self.infcx + } +} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index deab0451ccb..72a4d4c1205 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -8,21 +8,17 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor as _; use rustc_hir::LangItem; -use rustc_infer::error_reporting::infer::TypeErrCtxt; -use rustc_infer::infer::need_type_info::TypeAnnotationNeeded; use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt}; use rustc_infer::traits::util::elaborate; use rustc_infer::traits::{ Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation, }; -use rustc_macros::extension; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _}; use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; -use crate::error_reporting::traits::suggestions::TypeErrCtxtExt as _; -use crate::error_reporting::traits::{ - to_pretty_impl_header, FindExprBySpan, InferCtxtPrivExt as _, -}; +use crate::error_reporting::infer::need_type_info::TypeAnnotationNeeded; +use crate::error_reporting::traits::{to_pretty_impl_header, FindExprBySpan}; +use crate::error_reporting::TypeErrCtxt; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::ObligationCtxt; @@ -153,10 +149,12 @@ pub fn compute_applicable_impls_for_diagnostics<'tcx>( ambiguities } -#[extension(pub trait TypeErrCtxtAmbiguityExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { #[instrument(skip(self), level = "debug")] - fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed { + pub(super) fn maybe_report_ambiguity( + &self, + obligation: &PredicateObligation<'tcx>, + ) -> ErrorGuaranteed { // Unable to successfully determine, probably means // insufficient type information, but could mean // ambiguous impls. The latter *ought* to be a 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 0d040ddbacb..a7ea308a818 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 @@ -1,6 +1,8 @@ -use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _}; -use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _}; -use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt; +use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote}; +use super::suggestions::get_explanation_based_on_obligation; +use crate::error_reporting::infer::TyCategory; +use crate::error_reporting::traits::report_object_safety_error; +use crate::error_reporting::TypeErrCtxt; use crate::errors::{ AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch, }; @@ -24,10 +26,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::Node; use rustc_hir::{self as hir, LangItem}; -use rustc_infer::error_reporting::infer::TyCategory; -use rustc_infer::error_reporting::infer::TypeErrCtxt; use rustc_infer::infer::{InferOk, TypeTrace}; -use rustc_macros::extension; use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::SignatureMismatchData; use rustc_middle::ty::abstract_const::NotConstEvaluatable; @@ -49,14 +48,11 @@ use super::{ ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate, UnsatisfiedConst, }; -pub use rustc_infer::traits::error_reporting::*; - -#[extension(pub trait TypeErrCtxtSelectionErrExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// The `root_obligation` parameter should be the `root_obligation` field /// from a `FulfillmentError`. If no `FulfillmentError` is available, /// then it should be the same as `obligation`. - fn report_selection_error( + pub fn report_selection_error( &self, mut obligation: PredicateObligation<'tcx>, root_obligation: &PredicateObligation<'tcx>, @@ -682,9 +678,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } -#[extension(pub(super) trait TypeErrCtxtExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - fn apply_do_not_recommend(&self, obligation: &mut PredicateObligation<'tcx>) -> bool { + pub(super) fn apply_do_not_recommend( + &self, + obligation: &mut PredicateObligation<'tcx>, + ) -> bool { let mut base_cause = obligation.cause.code().clone(); let mut applied_do_not_recommend = false; loop { @@ -1142,7 +1140,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } -#[extension(pub(super) trait InferCtxtPrivExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn can_match_trait( &self, @@ -1182,7 +1179,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // returns if `cond` not occurring implies that `error` does not occur - i.e., that // `error` occurring implies that `cond` occurs. #[instrument(level = "debug", skip(self), ret)] - fn error_implies(&self, cond: ty::Predicate<'tcx>, error: ty::Predicate<'tcx>) -> bool { + pub(super) fn error_implies( + &self, + cond: ty::Predicate<'tcx>, + error: ty::Predicate<'tcx>, + ) -> bool { if cond == error { return true; } @@ -1205,7 +1206,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip_all)] - fn report_projection_error( + pub(super) fn report_projection_error( &self, obligation: &PredicateObligation<'tcx>, error: &MismatchedProjectionTypes<'tcx>, @@ -1455,7 +1456,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn fuzzy_match_tys( + pub fn fuzzy_match_tys( &self, mut a: Ty<'tcx>, mut b: Ty<'tcx>, @@ -1535,7 +1536,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str { + pub(super) fn describe_closure(&self, kind: hir::ClosureKind) -> &'static str { match kind { hir::ClosureKind::Closure => "a closure", hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(_)) => "a coroutine", @@ -1585,7 +1586,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn find_similar_impl_candidates( + pub(super) fn find_similar_impl_candidates( &self, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Vec<ImplCandidate<'tcx>> { @@ -1615,7 +1616,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { candidates } - fn report_similar_impl_candidates( + pub(super) fn report_similar_impl_candidates( &self, impl_candidates: &[ImplCandidate<'tcx>], trait_ref: ty::PolyTraitRef<'tcx>, @@ -1989,7 +1990,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// `trait_ref`. /// /// For this to work, `new_self_ty` must have no escaping bound variables. - fn mk_trait_obligation_with_new_self_ty( + pub(super) fn mk_trait_obligation_with_new_self_ty( &self, param_env: ty::ParamEnv<'tcx>, trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>, @@ -2041,7 +2042,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) } - fn note_obligation_cause(&self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>) { + pub fn note_obligation_cause( + &self, + err: &mut Diag<'_>, + obligation: &PredicateObligation<'tcx>, + ) { // First, attempt to add note to this error with an async-await-specific // message, and fall back to regular note otherwise. if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { @@ -2067,7 +2072,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn is_recursive_obligation( + pub(super) fn is_recursive_obligation( &self, obligated_types: &mut Vec<Ty<'tcx>>, cause_code: &ObligationCauseCode<'tcx>, @@ -2596,7 +2601,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) .unwrap_or((found_span, None, found)); - self.infcx.report_arg_count_mismatch( + self.report_arg_count_mismatch( span, closure_span, expected, @@ -2608,6 +2613,238 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) } + /// Given some node representing a fn-like thing in the HIR map, + /// returns a span and `ArgKind` information that describes the + /// arguments it expects. This can be supplied to + /// `report_arg_count_mismatch`. + pub fn get_fn_like_arguments( + &self, + node: Node<'_>, + ) -> Option<(Span, Option<Span>, Vec<ArgKind>)> { + let sm = self.tcx.sess.source_map(); + let hir = self.tcx.hir(); + Some(match node { + Node::Expr(&hir::Expr { + kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }), + .. + }) => ( + fn_decl_span, + fn_arg_span, + hir.body(body) + .params + .iter() + .map(|arg| { + if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat + { + Some(ArgKind::Tuple( + Some(span), + args.iter() + .map(|pat| { + sm.span_to_snippet(pat.span) + .ok() + .map(|snippet| (snippet, "_".to_owned())) + }) + .collect::<Option<Vec<_>>>()?, + )) + } else { + let name = sm.span_to_snippet(arg.pat.span).ok()?; + Some(ArgKind::Arg(name, "_".to_owned())) + } + }) + .collect::<Option<Vec<ArgKind>>>()?, + ), + Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) + | Node::TraitItem(&hir::TraitItem { + kind: hir::TraitItemKind::Fn(ref sig, _), .. + }) => ( + sig.span, + None, + sig.decl + .inputs + .iter() + .map(|arg| match arg.kind { + hir::TyKind::Tup(tys) => ArgKind::Tuple( + Some(arg.span), + vec![("_".to_owned(), "_".to_owned()); tys.len()], + ), + _ => ArgKind::empty(), + }) + .collect::<Vec<ArgKind>>(), + ), + Node::Ctor(variant_data) => { + let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); + (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) + } + _ => panic!("non-FnLike node found: {node:?}"), + }) + } + + /// Reports an error when the number of arguments needed by a + /// trait match doesn't match the number that the expression + /// provides. + pub fn report_arg_count_mismatch( + &self, + span: Span, + found_span: Option<Span>, + expected_args: Vec<ArgKind>, + found_args: Vec<ArgKind>, + is_closure: bool, + closure_arg_span: Option<Span>, + ) -> Diag<'a> { + let kind = if is_closure { "closure" } else { "function" }; + + let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { + let arg_length = arguments.len(); + let distinct = matches!(other, &[ArgKind::Tuple(..)]); + match (arg_length, arguments.get(0)) { + (1, Some(ArgKind::Tuple(_, fields))) => { + format!("a single {}-tuple as argument", fields.len()) + } + _ => format!( + "{} {}argument{}", + arg_length, + if distinct && arg_length > 1 { "distinct " } else { "" }, + pluralize!(arg_length) + ), + } + }; + + let expected_str = args_str(&expected_args, &found_args); + let found_str = args_str(&found_args, &expected_args); + + let mut err = struct_span_code_err!( + self.dcx(), + span, + E0593, + "{} is expected to take {}, but it takes {}", + kind, + expected_str, + found_str, + ); + + err.span_label(span, format!("expected {kind} that takes {expected_str}")); + + if let Some(found_span) = found_span { + err.span_label(found_span, format!("takes {found_str}")); + + // Suggest to take and ignore the arguments with expected_args_length `_`s if + // found arguments is empty (assume the user just wants to ignore args in this case). + // For example, if `expected_args_length` is 2, suggest `|_, _|`. + if found_args.is_empty() && is_closure { + let underscores = vec!["_"; expected_args.len()].join(", "); + err.span_suggestion_verbose( + closure_arg_span.unwrap_or(found_span), + format!( + "consider changing the closure to take and ignore the expected argument{}", + pluralize!(expected_args.len()) + ), + format!("|{underscores}|"), + Applicability::MachineApplicable, + ); + } + + if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { + if fields.len() == expected_args.len() { + let sugg = fields + .iter() + .map(|(name, _)| name.to_owned()) + .collect::<Vec<String>>() + .join(", "); + err.span_suggestion_verbose( + found_span, + "change the closure to take multiple arguments instead of a single tuple", + format!("|{sugg}|"), + Applicability::MachineApplicable, + ); + } + } + if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] + && fields.len() == found_args.len() + && is_closure + { + let sugg = format!( + "|({}){}|", + found_args + .iter() + .map(|arg| match arg { + ArgKind::Arg(name, _) => name.to_owned(), + _ => "_".to_owned(), + }) + .collect::<Vec<String>>() + .join(", "), + // add type annotations if available + if found_args.iter().any(|arg| match arg { + ArgKind::Arg(_, ty) => ty != "_", + _ => false, + }) { + format!( + ": ({})", + fields + .iter() + .map(|(_, ty)| ty.to_owned()) + .collect::<Vec<String>>() + .join(", ") + ) + } else { + String::new() + }, + ); + err.span_suggestion_verbose( + found_span, + "change the closure to accept a tuple instead of individual arguments", + sugg, + Applicability::MachineApplicable, + ); + } + } + + err + } + + /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` + /// in that order, and returns the generic type corresponding to the + /// argument of that trait (corresponding to the closure arguments). + pub fn type_implements_fn_trait( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: ty::Binder<'tcx, Ty<'tcx>>, + polarity: ty::PredicatePolarity, + ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { + self.commit_if_ok(|_| { + for trait_def_id in [ + self.tcx.lang_items().fn_trait(), + self.tcx.lang_items().fn_mut_trait(), + self.tcx.lang_items().fn_once_trait(), + ] { + let Some(trait_def_id) = trait_def_id else { continue }; + // Make a fresh inference variable so we can determine what the generic parameters + // of the trait are. + let var = self.next_ty_var(DUMMY_SP); + // FIXME(effects) + let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]); + let obligation = Obligation::new( + self.tcx, + ObligationCause::dummy(), + param_env, + ty.rebind(ty::TraitPredicate { trait_ref, polarity }), + ); + let ocx = ObligationCtxt::new(self); + ocx.register_obligation(obligation); + if ocx.select_all_or_error().is_empty() { + return Ok(( + self.tcx + .fn_trait_kind_from_def_id(trait_def_id) + .expect("expected to map DefId to ClosureKind"), + ty.rebind(self.resolve_vars_if_possible(var)), + )); + } + } + + Err(()) + }) + } + fn report_not_const_evaluatable_error( &self, obligation: &PredicateObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs deleted file mode 100644 index e8d7e80ac56..00000000000 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs +++ /dev/null @@ -1,244 +0,0 @@ -// FIXME(error_reporting): This should be made into private methods on `TypeErrCtxt`. - -use crate::infer::InferCtxt; -use crate::traits::{Obligation, ObligationCause, ObligationCtxt}; -use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, Diag}; -use rustc_hir as hir; -use rustc_hir::Node; -use rustc_macros::extension; -use rustc_middle::ty::{self, Ty}; -use rustc_span::{Span, DUMMY_SP}; - -use super::ArgKind; - -#[extension(pub trait InferCtxtExt<'tcx>)] -impl<'tcx> InferCtxt<'tcx> { - /// Given some node representing a fn-like thing in the HIR map, - /// returns a span and `ArgKind` information that describes the - /// arguments it expects. This can be supplied to - /// `report_arg_count_mismatch`. - fn get_fn_like_arguments(&self, node: Node<'_>) -> Option<(Span, Option<Span>, Vec<ArgKind>)> { - let sm = self.tcx.sess.source_map(); - let hir = self.tcx.hir(); - Some(match node { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, fn_arg_span, .. }), - .. - }) => ( - fn_decl_span, - fn_arg_span, - hir.body(body) - .params - .iter() - .map(|arg| { - if let hir::Pat { kind: hir::PatKind::Tuple(args, _), span, .. } = *arg.pat - { - Some(ArgKind::Tuple( - Some(span), - args.iter() - .map(|pat| { - sm.span_to_snippet(pat.span) - .ok() - .map(|snippet| (snippet, "_".to_owned())) - }) - .collect::<Option<Vec<_>>>()?, - )) - } else { - let name = sm.span_to_snippet(arg.pat.span).ok()?; - Some(ArgKind::Arg(name, "_".to_owned())) - } - }) - .collect::<Option<Vec<ArgKind>>>()?, - ), - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref sig, ..), .. }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref sig, _), .. }) - | Node::TraitItem(&hir::TraitItem { - kind: hir::TraitItemKind::Fn(ref sig, _), .. - }) => ( - sig.span, - None, - sig.decl - .inputs - .iter() - .map(|arg| match arg.kind { - hir::TyKind::Tup(tys) => ArgKind::Tuple( - Some(arg.span), - vec![("_".to_owned(), "_".to_owned()); tys.len()], - ), - _ => ArgKind::empty(), - }) - .collect::<Vec<ArgKind>>(), - ), - Node::Ctor(variant_data) => { - let span = variant_data.ctor_hir_id().map_or(DUMMY_SP, |id| hir.span(id)); - (span, None, vec![ArgKind::empty(); variant_data.fields().len()]) - } - _ => panic!("non-FnLike node found: {node:?}"), - }) - } - - /// Reports an error when the number of arguments needed by a - /// trait match doesn't match the number that the expression - /// provides. - fn report_arg_count_mismatch( - &self, - span: Span, - found_span: Option<Span>, - expected_args: Vec<ArgKind>, - found_args: Vec<ArgKind>, - is_closure: bool, - closure_arg_span: Option<Span>, - ) -> Diag<'_> { - let kind = if is_closure { "closure" } else { "function" }; - - let args_str = |arguments: &[ArgKind], other: &[ArgKind]| { - let arg_length = arguments.len(); - let distinct = matches!(other, &[ArgKind::Tuple(..)]); - match (arg_length, arguments.get(0)) { - (1, Some(ArgKind::Tuple(_, fields))) => { - format!("a single {}-tuple as argument", fields.len()) - } - _ => format!( - "{} {}argument{}", - arg_length, - if distinct && arg_length > 1 { "distinct " } else { "" }, - pluralize!(arg_length) - ), - } - }; - - let expected_str = args_str(&expected_args, &found_args); - let found_str = args_str(&found_args, &expected_args); - - let mut err = struct_span_code_err!( - self.dcx(), - span, - E0593, - "{} is expected to take {}, but it takes {}", - kind, - expected_str, - found_str, - ); - - err.span_label(span, format!("expected {kind} that takes {expected_str}")); - - if let Some(found_span) = found_span { - err.span_label(found_span, format!("takes {found_str}")); - - // Suggest to take and ignore the arguments with expected_args_length `_`s if - // found arguments is empty (assume the user just wants to ignore args in this case). - // For example, if `expected_args_length` is 2, suggest `|_, _|`. - if found_args.is_empty() && is_closure { - let underscores = vec!["_"; expected_args.len()].join(", "); - err.span_suggestion_verbose( - closure_arg_span.unwrap_or(found_span), - format!( - "consider changing the closure to take and ignore the expected argument{}", - pluralize!(expected_args.len()) - ), - format!("|{underscores}|"), - Applicability::MachineApplicable, - ); - } - - if let &[ArgKind::Tuple(_, ref fields)] = &found_args[..] { - if fields.len() == expected_args.len() { - let sugg = fields - .iter() - .map(|(name, _)| name.to_owned()) - .collect::<Vec<String>>() - .join(", "); - err.span_suggestion_verbose( - found_span, - "change the closure to take multiple arguments instead of a single tuple", - format!("|{sugg}|"), - Applicability::MachineApplicable, - ); - } - } - if let &[ArgKind::Tuple(_, ref fields)] = &expected_args[..] - && fields.len() == found_args.len() - && is_closure - { - let sugg = format!( - "|({}){}|", - found_args - .iter() - .map(|arg| match arg { - ArgKind::Arg(name, _) => name.to_owned(), - _ => "_".to_owned(), - }) - .collect::<Vec<String>>() - .join(", "), - // add type annotations if available - if found_args.iter().any(|arg| match arg { - ArgKind::Arg(_, ty) => ty != "_", - _ => false, - }) { - format!( - ": ({})", - fields - .iter() - .map(|(_, ty)| ty.to_owned()) - .collect::<Vec<String>>() - .join(", ") - ) - } else { - String::new() - }, - ); - err.span_suggestion_verbose( - found_span, - "change the closure to accept a tuple instead of individual arguments", - sugg, - Applicability::MachineApplicable, - ); - } - } - - err - } - - /// Checks if the type implements one of `Fn`, `FnMut`, or `FnOnce` - /// in that order, and returns the generic type corresponding to the - /// argument of that trait (corresponding to the closure arguments). - fn type_implements_fn_trait( - &self, - param_env: ty::ParamEnv<'tcx>, - ty: ty::Binder<'tcx, Ty<'tcx>>, - polarity: ty::PredicatePolarity, - ) -> Result<(ty::ClosureKind, ty::Binder<'tcx, Ty<'tcx>>), ()> { - self.commit_if_ok(|_| { - for trait_def_id in [ - self.tcx.lang_items().fn_trait(), - self.tcx.lang_items().fn_mut_trait(), - self.tcx.lang_items().fn_once_trait(), - ] { - let Some(trait_def_id) = trait_def_id else { continue }; - // Make a fresh inference variable so we can determine what the generic parameters - // of the trait are. - let var = self.next_ty_var(DUMMY_SP); - // FIXME(effects) - let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, [ty.skip_binder(), var]); - let obligation = Obligation::new( - self.tcx, - ObligationCause::dummy(), - param_env, - ty.rebind(ty::TraitPredicate { trait_ref, polarity }), - ); - let ocx = ObligationCtxt::new(self); - ocx.register_obligation(obligation); - if ocx.select_all_or_error().is_empty() { - return Ok(( - self.tcx - .fn_trait_kind_from_def_id(trait_def_id) - .expect("expected to map DefId to ClosureKind"), - ty.rebind(self.resolve_vars_if_possible(var)), - )); - } - } - - Err(()) - }) - } -} 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 16dfa27b75a..87fdc5ddff8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -1,33 +1,27 @@ pub mod ambiguity; mod fulfillment_errors; -mod infer_ctxt_ext; pub mod on_unimplemented; mod overflow; pub mod suggestions; -use std::iter; +use std::{fmt, iter}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; -use rustc_hir::def_id::DefId; +use rustc_errors::{struct_span_code_err, Applicability, Diag, MultiSpan, E0038, E0276}; +use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; use rustc_hir::{self as hir, LangItem}; -use rustc_infer::error_reporting::infer::TypeErrCtxt; use rustc_infer::traits::{ - Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError, + ObjectSafetyViolation, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, + SelectionError, }; -use rustc_macros::extension; -use rustc_middle::ty::print::PrintTraitRefExt as _; +use rustc_middle::ty::print::{with_no_trimmed_paths, PrintTraitRefExt as _}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::{ErrorGuaranteed, ExpnKind, Span}; -use ambiguity::TypeErrCtxtAmbiguityExt as _; -use fulfillment_errors::TypeErrCtxtExt as _; -use suggestions::TypeErrCtxtExt as _; - +use crate::error_reporting::TypeErrCtxt; use crate::traits::{FulfillmentError, FulfillmentErrorCode}; -pub use self::fulfillment_errors::*; -pub use self::infer_ctxt_ext::*; pub use self::overflow::*; // When outputting impl candidates, prefer showing those that are more similar. @@ -137,9 +131,8 @@ pub enum DefIdOrName { Name(&'static str), } -#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - fn report_fulfillment_errors( + pub fn report_fulfillment_errors( &self, mut errors: Vec<FulfillmentError<'tcx>>, ) -> ErrorGuaranteed { @@ -383,3 +376,194 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti w.push(';'); Some(w) } + +impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { + pub fn report_extra_impl_obligation( + &self, + error_span: Span, + impl_item_def_id: LocalDefId, + trait_item_def_id: DefId, + requirement: &dyn fmt::Display, + ) -> Diag<'a> { + let mut err = struct_span_code_err!( + self.dcx(), + error_span, + E0276, + "impl has stricter requirements than trait" + ); + + if !self.tcx.is_impl_trait_in_trait(trait_item_def_id) { + if let Some(span) = self.tcx.hir().span_if_local(trait_item_def_id) { + let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); + err.span_label(span, format!("definition of `{item_name}` from trait")); + } + } + + err.span_label(error_span, format!("impl has extra requirement {requirement}")); + + err + } +} + +pub fn report_object_safety_error<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + hir_id: Option<hir::HirId>, + trait_def_id: DefId, + violations: &[ObjectSafetyViolation], +) -> Diag<'tcx> { + let trait_str = tcx.def_path_str(trait_def_id); + let trait_span = tcx.hir().get_if_local(trait_def_id).and_then(|node| match node { + hir::Node::Item(item) => Some(item.ident.span), + _ => None, + }); + let mut err = struct_span_code_err!( + tcx.dcx(), + span, + E0038, + "the trait `{}` cannot be made into an object", + trait_str + ); + err.span_label(span, format!("`{trait_str}` cannot be made into an object")); + + if let Some(hir_id) = hir_id + && let hir::Node::Ty(ty) = tcx.hir_node(hir_id) + && let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind + { + let mut hir_id = hir_id; + while let hir::Node::Ty(ty) = tcx.parent_hir_node(hir_id) { + hir_id = ty.hir_id; + } + if tcx.parent_hir_node(hir_id).fn_sig().is_some() { + // Do not suggest `impl Trait` when dealing with things like super-traits. + err.span_suggestion_verbose( + ty.span.until(trait_ref.span), + "consider using an opaque type instead", + "impl ", + Applicability::MaybeIncorrect, + ); + } + } + let mut reported_violations = FxIndexSet::default(); + let mut multi_span = vec![]; + let mut messages = vec![]; + for violation in violations { + if let ObjectSafetyViolation::SizedSelf(sp) = &violation + && !sp.is_empty() + { + // Do not report `SizedSelf` without spans pointing at `SizedSelf` obligations + // with a `Span`. + reported_violations.insert(ObjectSafetyViolation::SizedSelf(vec![].into())); + } + if reported_violations.insert(violation.clone()) { + let spans = violation.spans(); + let msg = if trait_span.is_none() || spans.is_empty() { + format!("the trait cannot be made into an object because {}", violation.error_msg()) + } else { + format!("...because {}", violation.error_msg()) + }; + if spans.is_empty() { + err.note(msg); + } else { + for span in spans { + multi_span.push(span); + messages.push(msg.clone()); + } + } + } + } + let has_multi_span = !multi_span.is_empty(); + let mut note_span = MultiSpan::from_spans(multi_span.clone()); + if let (Some(trait_span), true) = (trait_span, has_multi_span) { + note_span.push_span_label(trait_span, "this trait cannot be made into an object..."); + } + for (span, msg) in iter::zip(multi_span, messages) { + note_span.push_span_label(span, msg); + } + err.span_note( + note_span, + "for a trait to be \"object safe\" it needs to allow building a vtable to allow the call \ + to be resolvable dynamically; for more information visit \ + <https://doc.rust-lang.org/reference/items/traits.html#object-safety>", + ); + + // Only provide the help if its a local trait, otherwise it's not actionable. + if trait_span.is_some() { + let mut reported_violations: Vec<_> = reported_violations.into_iter().collect(); + reported_violations.sort(); + + let mut potential_solutions: Vec<_> = + reported_violations.into_iter().map(|violation| violation.solution()).collect(); + potential_solutions.sort(); + // Allows us to skip suggesting that the same item should be moved to another trait multiple times. + potential_solutions.dedup(); + for solution in potential_solutions { + solution.add_to(&mut err); + } + } + + let impls_of = tcx.trait_impls_of(trait_def_id); + let impls = if impls_of.blanket_impls().is_empty() { + impls_of + .non_blanket_impls() + .values() + .flatten() + .filter(|def_id| { + !matches!(tcx.type_of(*def_id).instantiate_identity().kind(), ty::Dynamic(..)) + }) + .collect::<Vec<_>>() + } else { + vec![] + }; + let externally_visible = if !impls.is_empty() + && let Some(def_id) = trait_def_id.as_local() + // We may be executing this during typeck, which would result in cycle + // if we used effective_visibilities query, which looks into opaque types + // (and therefore calls typeck). + && tcx.resolutions(()).effective_visibilities.is_exported(def_id) + { + true + } else { + false + }; + match &impls[..] { + [] => {} + _ if impls.len() > 9 => {} + [only] if externally_visible => { + err.help(with_no_trimmed_paths!(format!( + "only type `{}` is seen to implement the trait in this crate, consider using it \ + directly instead", + tcx.type_of(*only).instantiate_identity(), + ))); + } + [only] => { + err.help(with_no_trimmed_paths!(format!( + "only type `{}` implements the trait, consider using it directly instead", + tcx.type_of(*only).instantiate_identity(), + ))); + } + impls => { + let types = impls + .iter() + .map(|t| { + with_no_trimmed_paths!(format!(" {}", tcx.type_of(*t).instantiate_identity(),)) + }) + .collect::<Vec<_>>(); + err.help(format!( + "the following types implement the trait, consider defining an enum where each \ + variant holds one of these types, implementing `{}` for this new enum and using \ + it instead:\n{}", + trait_str, + types.join("\n"), + )); + } + } + if externally_visible { + err.note(format!( + "`{trait_str}` can be implemented in other crates; if you want to support your users \ + passing their own types here, you can't refer to a specific type", + )); + } + + err +} diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index a448e1924c8..f65de590ccf 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -1,5 +1,5 @@ use super::{ObligationCauseCode, PredicateObligation}; -use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt; +use crate::error_reporting::TypeErrCtxt; use crate::errors::{ EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented, }; @@ -13,8 +13,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_infer::error_reporting::infer::TypeErrCtxt; -use rustc_macros::{extension, LintDiagnostic}; +use rustc_macros::LintDiagnostic; use rustc_middle::bug; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::GenericArgsRef; @@ -41,7 +40,6 @@ static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[ sym::Trait, ]; -#[extension(pub trait TypeErrCtxtExt<'tcx>)] impl<'tcx> TypeErrCtxt<'_, 'tcx> { fn impl_similar_to( &self, @@ -109,7 +107,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - fn on_unimplemented_note( + pub fn on_unimplemented_note( &self, trait_ref: ty::PolyTraitRef<'tcx>, obligation: &PredicateObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs index fe1771f9096..16fbff7816a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs @@ -5,17 +5,14 @@ use rustc_errors::{ }; use rustc_hir::def::Namespace; use rustc_hir::def_id::LOCAL_CRATE; -use rustc_infer::error_reporting::infer::TypeErrCtxt; use rustc_infer::traits::{Obligation, PredicateObligation}; -use rustc_macros::extension; use rustc_middle::ty::print::{FmtPrinter, Print}; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::Limit; use rustc_span::Span; use rustc_type_ir::Upcast; -use super::InferCtxtPrivExt; -use crate::error_reporting::traits::suggestions::TypeErrCtxtExt; +use crate::error_reporting::TypeErrCtxt; pub enum OverflowCause<'tcx> { DeeplyNormalize(ty::AliasTerm<'tcx>), @@ -38,7 +35,6 @@ pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>( )); } -#[extension(pub trait TypeErrCtxtOverflowExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// Reports that an overflow has occurred and halts compilation. We /// halt compilation unconditionally because it is important that @@ -46,7 +42,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// whose result could not be truly determined and thus we can't say /// if the program type checks or not -- and they are unusual /// occurrences in any case. - fn report_overflow_error( + pub fn report_overflow_error( &self, cause: OverflowCause<'tcx>, span: Span, @@ -59,7 +55,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { FatalError.raise(); } - fn build_overflow_error( + pub fn build_overflow_error( &self, cause: OverflowCause<'tcx>, span: Span, @@ -132,7 +128,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// whose result could not be truly determined and thus we can't say /// if the program type checks or not -- and they are unusual /// occurrences in any case. - fn report_overflow_obligation<T>( + pub fn report_overflow_obligation<T>( &self, obligation: &Obligation<'tcx, T>, suggest_increasing_limit: bool, @@ -165,7 +161,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// that we can give a more helpful error message (and, in particular, /// we do not suggest increasing the overflow limit, which is not /// going to help). - fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { + pub fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! { let cycle = self.resolve_vars_if_possible(cycle.to_owned()); assert!(!cycle.is_empty()); @@ -179,7 +175,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } - fn report_overflow_no_abort( + pub fn report_overflow_no_abort( &self, obligation: PredicateObligation<'tcx>, suggest_increasing_limit: bool, 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 fa2acdd4a54..885216e6216 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -5,11 +5,10 @@ use super::{ PredicateObligation, }; +use crate::error_reporting::TypeErrCtxt; use crate::errors; -use crate::infer::InferCtxt; use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt}; -use hir::def::CtorOf; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ @@ -17,15 +16,15 @@ use rustc_errors::{ Style, SuggestionStyle, }; use rustc_hir as hir; +use rustc_hir::def::CtorOf; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::is_range_literal; use rustc_hir::lang_items::LangItem; use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node}; -use rustc_infer::error_reporting::infer::TypeErrCtxt; +use rustc_infer::infer::InferCtxt; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk}; -use rustc_macros::extension; use rustc_middle::hir::map; use rustc_middle::traits::IsConstable; use rustc_middle::ty::error::TypeError; @@ -44,7 +43,6 @@ use std::assert_matches::debug_assert_matches; use std::borrow::Cow; use std::iter; -use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt; use crate::infer::InferCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_middle::ty::print::{ @@ -241,9 +239,8 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( } } -#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)] impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { - fn suggest_restricting_param_bound( + pub fn suggest_restricting_param_bound( &self, err: &mut Diag<'_>, trait_pred: ty::PolyTraitPredicate<'tcx>, @@ -453,7 +450,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// When after several dereferencing, the reference satisfies the trait /// bound. This function provides dereference suggestion for this /// specific situation. - fn suggest_dereferences( + pub(super) fn suggest_dereferences( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -782,7 +779,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// We tried to apply the bound to an `fn` or closure. Check whether calling it would /// evaluate to a type that *would* satisfy the trait bound. If it would, suggest calling /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`. - fn suggest_fn_call( + pub(super) fn suggest_fn_call( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -898,7 +895,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { true } - fn check_for_binding_assigned_block_without_tail_expression( + pub(super) fn check_for_binding_assigned_block_without_tail_expression( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -974,7 +971,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_add_clone_to_arg( + pub(super) fn suggest_add_clone_to_arg( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -1074,7 +1071,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// Extracts information about a callable type for diagnostics. This is a /// heuristic -- it doesn't necessarily mean that a type is always callable, /// because the callable type must also be well-formed to be called. - fn extract_callable_info( + pub fn extract_callable_info( &self, body_id: LocalDefId, param_env: ty::ParamEnv<'tcx>, @@ -1200,7 +1197,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) } } - fn suggest_add_reference_to_arg( + pub(super) fn suggest_add_reference_to_arg( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -1422,7 +1419,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } // Suggest borrowing the type - fn suggest_borrowing_for_object_cast( + pub(super) fn suggest_borrowing_for_object_cast( &self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, @@ -1457,7 +1454,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`, /// suggest removing these references until we reach a type that implements the trait. - fn suggest_remove_reference( + pub(super) fn suggest_remove_reference( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -1578,7 +1575,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { false } - fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>) { + pub(super) fn suggest_remove_await( + &self, + obligation: &PredicateObligation<'tcx>, + err: &mut Diag<'_>, + ) { let hir = self.tcx.hir(); if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives() && let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id) @@ -1644,7 +1645,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// Check if the trait bound is implemented for a different mutability and note it in the /// final error. - fn suggest_change_mut( + pub(super) fn suggest_change_mut( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -1720,7 +1721,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_semicolon_removal( + pub(super) fn suggest_semicolon_removal( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -1762,7 +1763,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { false } - fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> { + pub(super) fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> { let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. }) = self.tcx.hir_node_by_def_id(obligation.cause.body_id) else { @@ -1775,7 +1776,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if /// applicable and signal that the error has been expanded appropriately and needs to be /// emitted. - fn suggest_impl_trait( + pub(super) fn suggest_impl_trait( &self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, @@ -1793,25 +1794,42 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.children.clear(); let span = obligation.cause.span; - if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) + let body = self.tcx.hir().body_owned_by(obligation.cause.body_id); + + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(&body); + + let (pre, impl_span) = if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) && snip.starts_with("dyn ") { - err.span_suggestion( - span.with_hi(span.lo() + BytePos(4)), - "return an `impl Trait` instead of a `dyn Trait`, \ - if all returned values are the same type", + ("", span.with_hi(span.lo() + BytePos(4))) + } else { + ("dyn ", span.shrink_to_lo()) + }; + let alternatively = if visitor + .returns + .iter() + .map(|expr| self.typeck_results.as_ref().unwrap().expr_ty_adjusted_opt(expr)) + .collect::<FxHashSet<_>>() + .len() + <= 1 + { + err.span_suggestion_verbose( + impl_span, + "consider returning an `impl Trait` instead of a `dyn Trait`", "impl ", Applicability::MaybeIncorrect, ); - } - - let body = self.tcx.hir().body_owned_by(obligation.cause.body_id); - - let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(&body); + "alternatively, " + } else { + err.help("if there were a single returned type, you could use `impl Trait` instead"); + "" + }; - let mut sugg = - vec![(span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string())]; + let mut sugg = vec![ + (span.shrink_to_lo(), format!("Box<{pre}")), + (span.shrink_to_hi(), ">".to_string()), + ]; sugg.extend(visitor.returns.into_iter().flat_map(|expr| { let span = expr.span.find_ancestor_in_same_ctxt(obligation.cause.span).unwrap_or(expr.span); @@ -1837,7 +1855,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { })); err.multipart_suggestion( - "box the return type, and wrap all of the returned values in `Box::new`", + format!( + "{alternatively}box the return type, and wrap all of the returned values in \ + `Box::new`", + ), sugg, Applicability::MaybeIncorrect, ); @@ -1845,7 +1866,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { true } - fn point_at_returns_when_relevant( + pub(super) fn point_at_returns_when_relevant( &self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, @@ -1877,7 +1898,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn report_closure_arg_mismatch( + pub(super) fn report_closure_arg_mismatch( &self, span: Span, found_span: Option<Span>, @@ -2156,7 +2177,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_fully_qualified_path( + pub(super) fn suggest_fully_qualified_path( &self, err: &mut Diag<'_>, item_def_id: DefId, @@ -2223,7 +2244,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// /// Returns `true` if an async-await specific note was added to the diagnostic. #[instrument(level = "debug", skip_all, fields(?obligation.predicate, ?obligation.cause.span))] - fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>( + pub fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>( &self, err: &mut Diag<'_, G>, obligation: &PredicateObligation<'tcx>, @@ -2692,7 +2713,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } - fn note_obligation_cause_code<G: EmissionGuarantee, T>( + pub(super) fn note_obligation_cause_code<G: EmissionGuarantee, T>( &self, body_id: LocalDefId, err: &mut Diag<'_, G>, @@ -3534,7 +3555,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { #[instrument( level = "debug", skip(self, err), fields(trait_pred.self_ty = ?trait_pred.self_ty()) )] - fn suggest_await_before_try( + pub(super) fn suggest_await_before_try( &self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, @@ -3591,7 +3612,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_floating_point_literal( + pub(super) fn suggest_floating_point_literal( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -3615,7 +3636,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_derive( + pub fn suggest_derive( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -3681,7 +3702,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_dereferencing_index( + pub(super) fn suggest_dereferencing_index( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -4303,7 +4324,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// If the type that failed selection is an array or a reference to an array, /// but the trait is implemented for slices, suggest that the user converts /// the array into a slice. - fn suggest_convert_to_slice( + pub(super) fn suggest_convert_to_slice( &self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, @@ -4375,7 +4396,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn explain_hrtb_projection( + pub(super) fn explain_hrtb_projection( &self, diag: &mut Diag<'_>, pred: ty::PolyTraitPredicate<'tcx>, @@ -4441,7 +4462,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } - fn suggest_desugaring_async_fn_in_trait( + pub(super) fn suggest_desugaring_async_fn_in_trait( &self, err: &mut Diag<'_>, trait_ref: ty::PolyTraitRef<'tcx>, @@ -4525,7 +4546,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ); } - fn ty_kind_suggestion(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> Option<String> { + pub fn ty_kind_suggestion( + &self, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + ) -> Option<String> { let tcx = self.infcx.tcx; let implements_default = |ty| { let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else { @@ -4587,7 +4612,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }) } - fn suggest_add_result_as_return_type( + pub(super) fn suggest_add_result_as_return_type( &self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>, @@ -4628,7 +4653,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } #[instrument(level = "debug", skip_all)] - fn suggest_unsized_bound_if_applicable( + pub(super) fn suggest_unsized_bound_if_applicable( &self, err: &mut Diag<'_>, obligation: &PredicateObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index a46cba35b2d..0ee4485a365 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,11 +1,30 @@ -use crate::fluent_generated as fluent; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - codes::*, Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, - SubdiagMessageOp, Subdiagnostic, + codes::*, Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, + EmissionGuarantee, IntoDiagArg, Level, MultiSpan, SubdiagMessageOp, Subdiagnostic, }; +use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::{walk_ty, Visitor}; +use rustc_hir::FnRetTy; +use rustc_hir::GenericParamKind; use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_middle::ty::{self, print::PrintTraitRefExt as _, ClosureKind, PolyTraitRef, Ty}; -use rustc_span::{Span, Symbol}; +use rustc_middle::ty::print::TraitRefPrintOnlyTraitPath; +use rustc_middle::ty::{ + self, print::PrintTraitRefExt as _, Binder, ClosureKind, FnSig, PolyTraitRef, Region, Ty, + TyCtxt, +}; +use rustc_span::symbol::{kw, Ident, Symbol}; +use rustc_span::{BytePos, Span}; + +use crate::error_reporting::infer::need_type_info::UnderspecifiedArgKind; +use crate::error_reporting::infer::nice_region_error::placeholder_error::Highlighted; +use crate::error_reporting::infer::ObligationCauseAsDiagArg; +use crate::fluent_generated as fluent; + +use std::path::PathBuf; + +pub mod note_and_explain; #[derive(Diagnostic)] #[diag(trait_selection_dump_vtable_entries)] @@ -170,3 +189,1613 @@ pub(crate) struct AsyncClosureNotFn { pub span: Span, pub kind: &'static str, } + +#[derive(Diagnostic)] +#[diag(trait_selection_type_annotations_needed, code = E0282)] +pub struct AnnotationRequired<'a> { + #[primary_span] + pub span: Span, + pub source_kind: &'static str, + pub source_name: &'a str, + #[label] + pub failure_span: Option<Span>, + #[subdiagnostic] + pub bad_label: Option<InferenceBadError<'a>>, + #[subdiagnostic] + pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, + #[subdiagnostic] + pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, + #[note(trait_selection_full_type_written)] + pub was_written: Option<()>, + pub path: PathBuf, +} + +// Copy of `AnnotationRequired` for E0283 +#[derive(Diagnostic)] +#[diag(trait_selection_type_annotations_needed, code = E0283)] +pub struct AmbiguousImpl<'a> { + #[primary_span] + pub span: Span, + pub source_kind: &'static str, + pub source_name: &'a str, + #[label] + pub failure_span: Option<Span>, + #[subdiagnostic] + pub bad_label: Option<InferenceBadError<'a>>, + #[subdiagnostic] + pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, + #[subdiagnostic] + pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, + #[note(trait_selection_full_type_written)] + pub was_written: Option<()>, + pub path: PathBuf, +} + +// Copy of `AnnotationRequired` for E0284 +#[derive(Diagnostic)] +#[diag(trait_selection_type_annotations_needed, code = E0284)] +pub struct AmbiguousReturn<'a> { + #[primary_span] + pub span: Span, + pub source_kind: &'static str, + pub source_name: &'a str, + #[label] + pub failure_span: Option<Span>, + #[subdiagnostic] + pub bad_label: Option<InferenceBadError<'a>>, + #[subdiagnostic] + pub infer_subdiags: Vec<SourceKindSubdiag<'a>>, + #[subdiagnostic] + pub multi_suggestions: Vec<SourceKindMultiSuggestion<'a>>, + #[note(trait_selection_full_type_written)] + pub was_written: Option<()>, + pub path: PathBuf, +} + +// Used when a better one isn't available +#[derive(Subdiagnostic)] +#[label(trait_selection_label_bad)] +pub struct InferenceBadError<'a> { + #[primary_span] + pub span: Span, + pub bad_kind: &'static str, + pub prefix_kind: UnderspecifiedArgKind, + pub has_parent: bool, + pub prefix: &'a str, + pub parent_prefix: &'a str, + pub parent_name: String, + pub name: String, +} + +#[derive(Subdiagnostic)] +pub enum SourceKindSubdiag<'a> { + #[suggestion( + trait_selection_source_kind_subdiag_let, + style = "verbose", + code = ": {type_name}", + applicability = "has-placeholders" + )] + LetLike { + #[primary_span] + span: Span, + name: String, + type_name: String, + kind: &'static str, + x_kind: &'static str, + prefix_kind: UnderspecifiedArgKind, + prefix: &'a str, + arg_name: String, + }, + #[label(trait_selection_source_kind_subdiag_generic_label)] + GenericLabel { + #[primary_span] + span: Span, + is_type: bool, + param_name: String, + parent_exists: bool, + parent_prefix: String, + parent_name: String, + }, + #[suggestion( + trait_selection_source_kind_subdiag_generic_suggestion, + style = "verbose", + code = "::<{args}>", + applicability = "has-placeholders" + )] + GenericSuggestion { + #[primary_span] + span: Span, + arg_count: usize, + args: String, + }, +} + +#[derive(Subdiagnostic)] +pub enum SourceKindMultiSuggestion<'a> { + #[multipart_suggestion( + trait_selection_source_kind_fully_qualified, + style = "verbose", + applicability = "has-placeholders" + )] + FullyQualified { + #[suggestion_part(code = "{def_path}({adjustment}")] + span_lo: Span, + #[suggestion_part(code = "{successor_pos}")] + span_hi: Span, + def_path: String, + adjustment: &'a str, + successor_pos: &'a str, + }, + #[multipart_suggestion( + trait_selection_source_kind_closure_return, + style = "verbose", + applicability = "has-placeholders" + )] + ClosureReturn { + #[suggestion_part(code = "{start_span_code}")] + start_span: Span, + start_span_code: String, + #[suggestion_part(code = " }}")] + end_span: Option<Span>, + }, +} + +impl<'a> SourceKindMultiSuggestion<'a> { + pub fn new_fully_qualified( + span: Span, + def_path: String, + adjustment: &'a str, + successor: (&'a str, BytePos), + ) -> Self { + Self::FullyQualified { + span_lo: span.shrink_to_lo(), + span_hi: span.shrink_to_hi().with_hi(successor.1), + def_path, + adjustment, + successor_pos: successor.0, + } + } + + pub fn new_closure_return( + ty_info: String, + data: &'a FnRetTy<'a>, + should_wrap_expr: Option<Span>, + ) -> Self { + let arrow = match data { + FnRetTy::DefaultReturn(_) => " -> ", + _ => "", + }; + let (start_span, start_span_code, end_span) = match should_wrap_expr { + Some(end_span) => (data.span(), format!("{arrow}{ty_info} {{"), Some(end_span)), + None => (data.span(), format!("{arrow}{ty_info}"), None), + }; + Self::ClosureReturn { start_span, start_span_code, end_span } + } +} + +pub enum RegionOriginNote<'a> { + Plain { + span: Span, + msg: DiagMessage, + }, + WithName { + span: Span, + msg: DiagMessage, + name: &'a str, + continues: bool, + }, + WithRequirement { + span: Span, + requirement: ObligationCauseAsDiagArg<'a>, + expected_found: Option<(DiagStyledString, DiagStyledString)>, + }, +} + +impl Subdiagnostic for RegionOriginNote<'_> { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + self, + diag: &mut Diag<'_, G>, + _f: &F, + ) { + let mut label_or_note = |span, msg: DiagMessage| { + let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); + let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count(); + let span_is_primary = diag.span.primary_spans().iter().all(|&sp| sp == span); + if span_is_primary && sub_count == 0 && expanded_sub_count == 0 { + diag.span_label(span, msg); + } else if span_is_primary && expanded_sub_count == 0 { + diag.note(msg); + } else { + diag.span_note(span, msg); + } + }; + match self { + RegionOriginNote::Plain { span, msg } => { + label_or_note(span, msg); + } + RegionOriginNote::WithName { span, msg, name, continues } => { + label_or_note(span, msg); + diag.arg("name", name); + diag.arg("continues", continues); + } + RegionOriginNote::WithRequirement { + span, + requirement, + expected_found: Some((expected, found)), + } => { + label_or_note(span, fluent::trait_selection_subtype); + diag.arg("requirement", requirement); + + diag.note_expected_found(&"", expected, &"", found); + } + RegionOriginNote::WithRequirement { span, requirement, expected_found: None } => { + // FIXME: this really should be handled at some earlier stage. Our + // handling of region checking when type errors are present is + // *terrible*. + label_or_note(span, fluent::trait_selection_subtype_2); + diag.arg("requirement", requirement); + } + }; + } +} + +pub enum LifetimeMismatchLabels { + InRet { + param_span: Span, + ret_span: Span, + span: Span, + label_var1: Option<Ident>, + }, + Normal { + hir_equal: bool, + ty_sup: Span, + ty_sub: Span, + span: Span, + sup: Option<Ident>, + sub: Option<Ident>, + }, +} + +impl Subdiagnostic for LifetimeMismatchLabels { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + self, + diag: &mut Diag<'_, G>, + _f: &F, + ) { + match self { + LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { + diag.span_label(param_span, fluent::trait_selection_declared_different); + diag.span_label(ret_span, fluent::trait_selection_nothing); + diag.span_label(span, fluent::trait_selection_data_returned); + diag.arg("label_var1_exists", label_var1.is_some()); + diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default()); + } + LifetimeMismatchLabels::Normal { + hir_equal, + ty_sup, + ty_sub, + span, + sup: label_var1, + sub: label_var2, + } => { + if hir_equal { + diag.span_label(ty_sup, fluent::trait_selection_declared_multiple); + diag.span_label(ty_sub, fluent::trait_selection_nothing); + diag.span_label(span, fluent::trait_selection_data_lifetime_flow); + } else { + diag.span_label(ty_sup, fluent::trait_selection_types_declared_different); + diag.span_label(ty_sub, fluent::trait_selection_nothing); + diag.span_label(span, fluent::trait_selection_data_flows); + diag.arg("label_var1_exists", label_var1.is_some()); + diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default()); + diag.arg("label_var2_exists", label_var2.is_some()); + diag.arg("label_var2", label_var2.map(|x| x.to_string()).unwrap_or_default()); + } + } + } + } +} + +pub struct AddLifetimeParamsSuggestion<'a> { + pub tcx: TyCtxt<'a>, + pub generic_param_scope: LocalDefId, + pub sub: Region<'a>, + pub ty_sup: &'a hir::Ty<'a>, + pub ty_sub: &'a hir::Ty<'a>, + pub add_note: bool, +} + +impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + self, + diag: &mut Diag<'_, G>, + _f: &F, + ) { + let mut mk_suggestion = || { + let Some(anon_reg) = self.tcx.is_suitable_region(self.generic_param_scope, self.sub) + else { + return false; + }; + + let node = self.tcx.hir_node_by_def_id(anon_reg.def_id); + let is_impl = matches!(&node, hir::Node::ImplItem(_)); + let (generics, parent_generics) = match node { + hir::Node::Item(&hir::Item { + kind: hir::ItemKind::Fn(_, ref generics, ..), + .. + }) + | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) + | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => ( + generics, + match self.tcx.parent_hir_node(self.tcx.local_def_id_to_hir_id(anon_reg.def_id)) + { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Trait(_, _, ref generics, ..), + .. + }) + | hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { ref generics, .. }), + .. + }) => Some(generics), + _ => None, + }, + ), + _ => return false, + }; + + let suggestion_param_name = generics + .params + .iter() + .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) + .map(|p| p.name.ident().name) + .find(|i| *i != kw::UnderscoreLifetime); + let introduce_new = suggestion_param_name.is_none(); + + let mut default = "'a".to_string(); + if let Some(parent_generics) = parent_generics { + let used: FxHashSet<_> = parent_generics + .params + .iter() + .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) + .map(|p| p.name.ident().name) + .filter(|i| *i != kw::UnderscoreLifetime) + .map(|l| l.to_string()) + .collect(); + if let Some(lt) = + ('a'..='z').map(|it| format!("'{it}")).find(|it| !used.contains(it)) + { + // We want a lifetime that *isn't* present in the `trait` or `impl` that assoc + // `fn` belongs to. We could suggest reusing one of their lifetimes, but it is + // likely to be an over-constraining lifetime requirement, so we always add a + // lifetime to the `fn`. + default = lt; + } + } + let suggestion_param_name = + suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| default); + + struct ImplicitLifetimeFinder { + suggestions: Vec<(Span, String)>, + suggestion_param_name: String, + } + + impl<'v> Visitor<'v> for ImplicitLifetimeFinder { + fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { + let make_suggestion = |ident: Ident| { + if ident.name == kw::Empty && ident.span.is_empty() { + format!("{}, ", self.suggestion_param_name) + } else if ident.name == kw::UnderscoreLifetime && ident.span.is_empty() { + format!("{} ", self.suggestion_param_name) + } else { + self.suggestion_param_name.clone() + } + }; + match ty.kind { + hir::TyKind::Path(hir::QPath::Resolved(_, path)) => { + for segment in path.segments { + if let Some(args) = segment.args { + if args.args.iter().all(|arg| { + matches!( + arg, + hir::GenericArg::Lifetime(lifetime) + if lifetime.ident.name == kw::Empty + ) + }) { + self.suggestions.push(( + segment.ident.span.shrink_to_hi(), + format!( + "<{}>", + args.args + .iter() + .map(|_| self.suggestion_param_name.clone()) + .collect::<Vec<_>>() + .join(", ") + ), + )); + } else { + for arg in args.args { + if let hir::GenericArg::Lifetime(lifetime) = arg + && lifetime.is_anonymous() + { + self.suggestions.push(( + lifetime.ident.span, + make_suggestion(lifetime.ident), + )); + } + } + } + } + } + } + hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => { + self.suggestions + .push((lifetime.ident.span, make_suggestion(lifetime.ident))); + } + _ => {} + } + walk_ty(self, ty); + } + } + let mut visitor = ImplicitLifetimeFinder { + suggestions: vec![], + suggestion_param_name: suggestion_param_name.clone(), + }; + if let Some(fn_decl) = node.fn_decl() + && let hir::FnRetTy::Return(ty) = fn_decl.output + { + visitor.visit_ty(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(self.ty_sub); + if visitor.suggestions.is_empty() { + return false; + } + if introduce_new { + let new_param_suggestion = if let Some(first) = + generics.params.iter().find(|p| !p.name.ident().span.is_empty()) + { + (first.span.shrink_to_lo(), format!("{suggestion_param_name}, ")) + } else { + (generics.span, format!("<{suggestion_param_name}>")) + }; + + visitor.suggestions.push(new_param_suggestion); + } + diag.multipart_suggestion_verbose( + fluent::trait_selection_lifetime_param_suggestion, + visitor.suggestions, + Applicability::MaybeIncorrect, + ); + diag.arg("is_impl", is_impl); + diag.arg("is_reuse", !introduce_new); + + true + }; + if mk_suggestion() && self.add_note { + diag.note(fluent::trait_selection_lifetime_param_suggestion_elided); + } + } +} + +#[derive(Diagnostic)] +#[diag(trait_selection_lifetime_mismatch, code = E0623)] +pub struct LifetimeMismatch<'a> { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub labels: LifetimeMismatchLabels, + #[subdiagnostic] + pub suggestion: AddLifetimeParamsSuggestion<'a>, +} + +pub struct IntroducesStaticBecauseUnmetLifetimeReq { + pub unmet_requirements: MultiSpan, + pub binding_span: Span, +} + +impl Subdiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + mut self, + diag: &mut Diag<'_, G>, + _f: &F, + ) { + self.unmet_requirements + .push_span_label(self.binding_span, fluent::trait_selection_msl_introduces_static); + diag.span_note(self.unmet_requirements, fluent::trait_selection_msl_unmet_req); + } +} + +// FIXME(#100717): replace with a `Option<Span>` when subdiagnostic supports that +#[derive(Subdiagnostic)] +pub enum DoesNotOutliveStaticFromImpl { + #[note(trait_selection_does_not_outlive_static_from_impl)] + Spanned { + #[primary_span] + span: Span, + }, + #[note(trait_selection_does_not_outlive_static_from_impl)] + Unspanned, +} + +#[derive(Subdiagnostic)] +pub enum ImplicitStaticLifetimeSubdiag { + #[note(trait_selection_implicit_static_lifetime_note)] + Note { + #[primary_span] + span: Span, + }, + #[suggestion( + trait_selection_implicit_static_lifetime_suggestion, + style = "verbose", + code = " + '_", + applicability = "maybe-incorrect" + )] + Sugg { + #[primary_span] + span: Span, + }, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_mismatched_static_lifetime)] +pub struct MismatchedStaticLifetime<'a> { + #[primary_span] + pub cause_span: Span, + #[subdiagnostic] + pub unmet_lifetime_reqs: IntroducesStaticBecauseUnmetLifetimeReq, + #[subdiagnostic] + pub expl: Option<note_and_explain::RegionExplanation<'a>>, + #[subdiagnostic] + pub does_not_outlive_static_from_impl: DoesNotOutliveStaticFromImpl, + #[subdiagnostic] + pub implicit_static_lifetimes: Vec<ImplicitStaticLifetimeSubdiag>, +} + +#[derive(Diagnostic)] +pub enum ExplicitLifetimeRequired<'a> { + #[diag(trait_selection_explicit_lifetime_required_with_ident, code = E0621)] + WithIdent { + #[primary_span] + #[label] + span: Span, + simple_ident: Ident, + named: String, + #[suggestion( + trait_selection_explicit_lifetime_required_sugg_with_ident, + code = "{new_ty}", + applicability = "unspecified" + )] + new_ty_span: Span, + #[skip_arg] + new_ty: Ty<'a>, + }, + #[diag(trait_selection_explicit_lifetime_required_with_param_type, code = E0621)] + WithParamType { + #[primary_span] + #[label] + span: Span, + named: String, + #[suggestion( + trait_selection_explicit_lifetime_required_sugg_with_param_type, + code = "{new_ty}", + applicability = "unspecified" + )] + new_ty_span: Span, + #[skip_arg] + new_ty: Ty<'a>, + }, +} + +pub enum TyOrSig<'tcx> { + Ty(Highlighted<'tcx, Ty<'tcx>>), + ClosureSig(Highlighted<'tcx, Binder<'tcx, FnSig<'tcx>>>), +} + +impl IntoDiagArg for TyOrSig<'_> { + fn into_diag_arg(self) -> rustc_errors::DiagArgValue { + match self { + TyOrSig::Ty(ty) => ty.into_diag_arg(), + TyOrSig::ClosureSig(sig) => sig.into_diag_arg(), + } + } +} + +#[derive(Subdiagnostic)] +pub enum ActualImplExplNotes<'tcx> { + #[note(trait_selection_actual_impl_expl_expected_signature_two)] + ExpectedSignatureTwo { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + lifetime_2: usize, + }, + #[note(trait_selection_actual_impl_expl_expected_signature_any)] + ExpectedSignatureAny { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + }, + #[note(trait_selection_actual_impl_expl_expected_signature_some)] + ExpectedSignatureSome { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + }, + #[note(trait_selection_actual_impl_expl_expected_signature_nothing)] + ExpectedSignatureNothing { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + }, + #[note(trait_selection_actual_impl_expl_expected_passive_two)] + ExpectedPassiveTwo { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + lifetime_2: usize, + }, + #[note(trait_selection_actual_impl_expl_expected_passive_any)] + ExpectedPassiveAny { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + }, + #[note(trait_selection_actual_impl_expl_expected_passive_some)] + ExpectedPassiveSome { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + }, + #[note(trait_selection_actual_impl_expl_expected_passive_nothing)] + ExpectedPassiveNothing { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + }, + #[note(trait_selection_actual_impl_expl_expected_other_two)] + ExpectedOtherTwo { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + lifetime_2: usize, + }, + #[note(trait_selection_actual_impl_expl_expected_other_any)] + ExpectedOtherAny { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + }, + #[note(trait_selection_actual_impl_expl_expected_other_some)] + ExpectedOtherSome { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + }, + #[note(trait_selection_actual_impl_expl_expected_other_nothing)] + ExpectedOtherNothing { + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + }, + #[note(trait_selection_actual_impl_expl_but_actually_implements_trait)] + ButActuallyImplementsTrait { + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + has_lifetime: bool, + lifetime: usize, + }, + #[note(trait_selection_actual_impl_expl_but_actually_implemented_for_ty)] + ButActuallyImplementedForTy { + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + has_lifetime: bool, + lifetime: usize, + ty: String, + }, + #[note(trait_selection_actual_impl_expl_but_actually_ty_implements)] + ButActuallyTyImplements { + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + has_lifetime: bool, + lifetime: usize, + ty: String, + }, +} + +pub enum ActualImplExpectedKind { + Signature, + Passive, + Other, +} + +pub enum ActualImplExpectedLifetimeKind { + Two, + Any, + Some, + Nothing, +} + +impl<'tcx> ActualImplExplNotes<'tcx> { + pub fn new_expected( + kind: ActualImplExpectedKind, + lt_kind: ActualImplExpectedLifetimeKind, + leading_ellipsis: bool, + ty_or_sig: TyOrSig<'tcx>, + trait_path: Highlighted<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, + lifetime_1: usize, + lifetime_2: usize, + ) -> Self { + match (kind, lt_kind) { + (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Two) => { + Self::ExpectedSignatureTwo { + leading_ellipsis, + ty_or_sig, + trait_path, + lifetime_1, + lifetime_2, + } + } + (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Any) => { + Self::ExpectedSignatureAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } + } + (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Some) => { + Self::ExpectedSignatureSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } + } + (ActualImplExpectedKind::Signature, ActualImplExpectedLifetimeKind::Nothing) => { + Self::ExpectedSignatureNothing { leading_ellipsis, ty_or_sig, trait_path } + } + (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Two) => { + Self::ExpectedPassiveTwo { + leading_ellipsis, + ty_or_sig, + trait_path, + lifetime_1, + lifetime_2, + } + } + (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Any) => { + Self::ExpectedPassiveAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } + } + (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Some) => { + Self::ExpectedPassiveSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } + } + (ActualImplExpectedKind::Passive, ActualImplExpectedLifetimeKind::Nothing) => { + Self::ExpectedPassiveNothing { leading_ellipsis, ty_or_sig, trait_path } + } + (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Two) => { + Self::ExpectedOtherTwo { + leading_ellipsis, + ty_or_sig, + trait_path, + lifetime_1, + lifetime_2, + } + } + (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Any) => { + Self::ExpectedOtherAny { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } + } + (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Some) => { + Self::ExpectedOtherSome { leading_ellipsis, ty_or_sig, trait_path, lifetime_1 } + } + (ActualImplExpectedKind::Other, ActualImplExpectedLifetimeKind::Nothing) => { + Self::ExpectedOtherNothing { leading_ellipsis, ty_or_sig, trait_path } + } + } + } +} + +#[derive(Diagnostic)] +#[diag(trait_selection_trait_placeholder_mismatch)] +pub struct TraitPlaceholderMismatch<'tcx> { + #[primary_span] + pub span: Span, + #[label(trait_selection_label_satisfy)] + pub satisfy_span: Option<Span>, + #[label(trait_selection_label_where)] + pub where_span: Option<Span>, + #[label(trait_selection_label_dup)] + pub dup_span: Option<Span>, + pub def_id: String, + pub trait_def_id: String, + + #[subdiagnostic] + pub actual_impl_expl_notes: Vec<ActualImplExplNotes<'tcx>>, +} + +pub struct ConsiderBorrowingParamHelp { + pub spans: Vec<Span>, +} + +impl Subdiagnostic for ConsiderBorrowingParamHelp { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + self, + diag: &mut Diag<'_, G>, + f: &F, + ) { + let mut type_param_span: MultiSpan = self.spans.clone().into(); + for &span in &self.spans { + // Seems like we can't call f() here as Into<DiagMessage> is required + type_param_span.push_span_label(span, fluent::trait_selection_tid_consider_borrowing); + } + let msg = f(diag, fluent::trait_selection_tid_param_help.into()); + diag.span_help(type_param_span, msg); + } +} + +#[derive(Subdiagnostic)] +#[help(trait_selection_tid_rel_help)] +pub struct RelationshipHelp; + +#[derive(Diagnostic)] +#[diag(trait_selection_trait_impl_diff)] +pub struct TraitImplDiff { + #[primary_span] + #[label(trait_selection_found)] + pub sp: Span, + #[label(trait_selection_expected)] + pub trait_sp: Span, + #[note(trait_selection_expected_found)] + pub note: (), + #[subdiagnostic] + pub param_help: ConsiderBorrowingParamHelp, + #[subdiagnostic] + // Seems like subdiagnostics are always pushed to the end, so this one + // also has to be a subdiagnostic to maintain order. + pub rel_help: Option<RelationshipHelp>, + pub expected: String, + pub found: String, +} + +pub struct DynTraitConstraintSuggestion { + pub span: Span, + pub ident: Ident, +} + +impl Subdiagnostic for DynTraitConstraintSuggestion { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + self, + diag: &mut Diag<'_, G>, + f: &F, + ) { + let mut multi_span: MultiSpan = vec![self.span].into(); + multi_span.push_span_label(self.span, fluent::trait_selection_dtcs_has_lifetime_req_label); + multi_span + .push_span_label(self.ident.span, fluent::trait_selection_dtcs_introduces_requirement); + let msg = f(diag, fluent::trait_selection_dtcs_has_req_note.into()); + diag.span_note(multi_span, msg); + let msg = f(diag, fluent::trait_selection_dtcs_suggestion.into()); + diag.span_suggestion_verbose( + self.span.shrink_to_hi(), + msg, + " + '_", + Applicability::MaybeIncorrect, + ); + } +} + +#[derive(Diagnostic)] +#[diag(trait_selection_but_calling_introduces, code = E0772)] +pub struct ButCallingIntroduces { + #[label(trait_selection_label1)] + pub param_ty_span: Span, + #[primary_span] + #[label(trait_selection_label2)] + pub cause_span: Span, + + pub has_param_name: bool, + pub param_name: String, + pub has_lifetime: bool, + pub lifetime: String, + pub assoc_item: Symbol, + pub has_impl_path: bool, + pub impl_path: String, +} + +pub struct ReqIntroducedLocations { + pub span: MultiSpan, + pub spans: Vec<Span>, + pub fn_decl_span: Span, + pub cause_span: Span, + pub add_label: bool, +} + +impl Subdiagnostic for ReqIntroducedLocations { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + mut self, + diag: &mut Diag<'_, G>, + f: &F, + ) { + for sp in self.spans { + self.span.push_span_label(sp, fluent::trait_selection_ril_introduced_here); + } + + if self.add_label { + self.span.push_span_label(self.fn_decl_span, fluent::trait_selection_ril_introduced_by); + } + self.span.push_span_label(self.cause_span, fluent::trait_selection_ril_because_of); + let msg = f(diag, fluent::trait_selection_ril_static_introduced_by.into()); + diag.span_note(self.span, msg); + } +} + +pub struct MoreTargeted { + pub ident: Symbol, +} + +impl Subdiagnostic for MoreTargeted { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + self, + diag: &mut Diag<'_, G>, + _f: &F, + ) { + diag.code(E0772); + diag.primary_message(fluent::trait_selection_more_targeted); + diag.arg("ident", self.ident); + } +} + +#[derive(Diagnostic)] +#[diag(trait_selection_but_needs_to_satisfy, code = E0759)] +pub struct ButNeedsToSatisfy { + #[primary_span] + pub sp: Span, + #[label(trait_selection_influencer)] + pub influencer_point: Span, + #[label(trait_selection_used_here)] + pub spans: Vec<Span>, + #[label(trait_selection_require)] + pub require_span_as_label: Option<Span>, + #[note(trait_selection_require)] + pub require_span_as_note: Option<Span>, + #[note(trait_selection_introduced_by_bound)] + pub bound: Option<Span>, + + #[subdiagnostic] + pub req_introduces_loc: Option<ReqIntroducedLocations>, + + pub has_param_name: bool, + pub param_name: String, + pub spans_empty: bool, + pub has_lifetime: bool, + pub lifetime: String, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_outlives_content, code = E0312)] +pub struct OutlivesContent<'a> { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub notes: Vec<note_and_explain::RegionExplanation<'a>>, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_outlives_bound, code = E0476)] +pub struct OutlivesBound<'a> { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub notes: Vec<note_and_explain::RegionExplanation<'a>>, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_fulfill_req_lifetime, code = E0477)] +pub struct FulfillReqLifetime<'a> { + #[primary_span] + pub span: Span, + pub ty: Ty<'a>, + #[subdiagnostic] + pub note: Option<note_and_explain::RegionExplanation<'a>>, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_lf_bound_not_satisfied, code = E0478)] +pub struct LfBoundNotSatisfied<'a> { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub notes: Vec<note_and_explain::RegionExplanation<'a>>, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_ref_longer_than_data, code = E0491)] +pub struct RefLongerThanData<'a> { + #[primary_span] + pub span: Span, + pub ty: Ty<'a>, + #[subdiagnostic] + pub notes: Vec<note_and_explain::RegionExplanation<'a>>, +} + +#[derive(Subdiagnostic)] +pub enum WhereClauseSuggestions { + #[suggestion( + trait_selection_where_remove, + code = "", + applicability = "machine-applicable", + style = "verbose" + )] + Remove { + #[primary_span] + span: Span, + }, + #[suggestion( + trait_selection_where_copy_predicates, + code = "{space}where {trait_predicates}", + applicability = "machine-applicable", + style = "verbose" + )] + CopyPredicates { + #[primary_span] + span: Span, + space: &'static str, + trait_predicates: String, + }, +} + +#[derive(Subdiagnostic)] +pub enum SuggestRemoveSemiOrReturnBinding { + #[multipart_suggestion( + trait_selection_srs_remove_and_box, + applicability = "machine-applicable" + )] + RemoveAndBox { + #[suggestion_part(code = "Box::new(")] + first_lo: Span, + #[suggestion_part(code = ")")] + first_hi: Span, + #[suggestion_part(code = "Box::new(")] + second_lo: Span, + #[suggestion_part(code = ")")] + second_hi: Span, + #[suggestion_part(code = "")] + sp: Span, + }, + #[suggestion( + trait_selection_srs_remove, + style = "short", + code = "", + applicability = "machine-applicable" + )] + Remove { + #[primary_span] + sp: Span, + }, + #[suggestion( + trait_selection_srs_add, + style = "verbose", + code = "{code}", + applicability = "maybe-incorrect" + )] + Add { + #[primary_span] + sp: Span, + code: String, + ident: Ident, + }, + #[note(trait_selection_srs_add_one)] + AddOne { + #[primary_span] + spans: MultiSpan, + }, +} + +#[derive(Subdiagnostic)] +pub enum ConsiderAddingAwait { + #[help(trait_selection_await_both_futures)] + BothFuturesHelp, + #[multipart_suggestion(trait_selection_await_both_futures, applicability = "maybe-incorrect")] + BothFuturesSugg { + #[suggestion_part(code = ".await")] + first: Span, + #[suggestion_part(code = ".await")] + second: Span, + }, + #[suggestion( + trait_selection_await_future, + code = ".await", + style = "verbose", + applicability = "maybe-incorrect" + )] + FutureSugg { + #[primary_span] + span: Span, + }, + #[note(trait_selection_await_note)] + FutureSuggNote { + #[primary_span] + span: Span, + }, + #[multipart_suggestion( + trait_selection_await_future, + style = "verbose", + applicability = "maybe-incorrect" + )] + FutureSuggMultiple { + #[suggestion_part(code = ".await")] + spans: Vec<Span>, + }, +} + +#[derive(Diagnostic)] +pub enum PlaceholderRelationLfNotSatisfied { + #[diag(trait_selection_lf_bound_not_satisfied)] + HasBoth { + #[primary_span] + span: Span, + #[note(trait_selection_prlf_defined_with_sub)] + sub_span: Span, + #[note(trait_selection_prlf_must_outlive_with_sup)] + sup_span: Span, + sub_symbol: Symbol, + sup_symbol: Symbol, + #[note(trait_selection_prlf_known_limitation)] + note: (), + }, + #[diag(trait_selection_lf_bound_not_satisfied)] + HasSub { + #[primary_span] + span: Span, + #[note(trait_selection_prlf_defined_with_sub)] + sub_span: Span, + #[note(trait_selection_prlf_must_outlive_without_sup)] + sup_span: Span, + sub_symbol: Symbol, + #[note(trait_selection_prlf_known_limitation)] + note: (), + }, + #[diag(trait_selection_lf_bound_not_satisfied)] + HasSup { + #[primary_span] + span: Span, + #[note(trait_selection_prlf_defined_without_sub)] + sub_span: Span, + #[note(trait_selection_prlf_must_outlive_with_sup)] + sup_span: Span, + sup_symbol: Symbol, + #[note(trait_selection_prlf_known_limitation)] + note: (), + }, + #[diag(trait_selection_lf_bound_not_satisfied)] + HasNone { + #[primary_span] + span: Span, + #[note(trait_selection_prlf_defined_without_sub)] + sub_span: Span, + #[note(trait_selection_prlf_must_outlive_without_sup)] + sup_span: Span, + #[note(trait_selection_prlf_known_limitation)] + note: (), + }, + #[diag(trait_selection_lf_bound_not_satisfied)] + OnlyPrimarySpan { + #[primary_span] + span: Span, + #[note(trait_selection_prlf_known_limitation)] + note: (), + }, +} + +#[derive(Diagnostic)] +#[diag(trait_selection_opaque_captures_lifetime, code = E0700)] +pub struct OpaqueCapturesLifetime<'tcx> { + #[primary_span] + pub span: Span, + #[label] + pub opaque_ty_span: Span, + pub opaque_ty: Ty<'tcx>, +} + +#[derive(Subdiagnostic)] +pub enum FunctionPointerSuggestion<'a> { + #[suggestion( + trait_selection_fps_use_ref, + code = "&{fn_name}", + style = "verbose", + applicability = "maybe-incorrect" + )] + UseRef { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + }, + #[suggestion( + trait_selection_fps_remove_ref, + code = "{fn_name}", + style = "verbose", + applicability = "maybe-incorrect" + )] + RemoveRef { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + }, + #[suggestion( + trait_selection_fps_cast, + code = "&({fn_name} as {sig})", + style = "verbose", + applicability = "maybe-incorrect" + )] + CastRef { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + #[skip_arg] + sig: Binder<'a, FnSig<'a>>, + }, + #[suggestion( + trait_selection_fps_cast, + code = "{fn_name} as {sig}", + style = "verbose", + applicability = "maybe-incorrect" + )] + Cast { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + #[skip_arg] + sig: Binder<'a, FnSig<'a>>, + }, + #[suggestion( + trait_selection_fps_cast_both, + code = "{fn_name} as {found_sig}", + style = "hidden", + applicability = "maybe-incorrect" + )] + CastBoth { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + #[skip_arg] + found_sig: Binder<'a, FnSig<'a>>, + expected_sig: Binder<'a, FnSig<'a>>, + }, + #[suggestion( + trait_selection_fps_cast_both, + code = "&({fn_name} as {found_sig})", + style = "hidden", + applicability = "maybe-incorrect" + )] + CastBothRef { + #[primary_span] + span: Span, + #[skip_arg] + fn_name: String, + #[skip_arg] + found_sig: Binder<'a, FnSig<'a>>, + expected_sig: Binder<'a, FnSig<'a>>, + }, +} + +#[derive(Subdiagnostic)] +#[note(trait_selection_fps_items_are_distinct)] +pub struct FnItemsAreDistinct; + +#[derive(Subdiagnostic)] +#[note(trait_selection_fn_uniq_types)] +pub struct FnUniqTypes; + +#[derive(Subdiagnostic)] +#[help(trait_selection_fn_consider_casting)] +pub struct FnConsiderCasting { + pub casting: String, +} + +#[derive(Subdiagnostic)] +pub enum SuggestAccessingField<'a> { + #[suggestion( + trait_selection_suggest_accessing_field, + code = "{snippet}.{name}", + applicability = "maybe-incorrect" + )] + Safe { + #[primary_span] + span: Span, + snippet: String, + name: Symbol, + ty: Ty<'a>, + }, + #[suggestion( + trait_selection_suggest_accessing_field, + code = "unsafe {{ {snippet}.{name} }}", + applicability = "maybe-incorrect" + )] + Unsafe { + #[primary_span] + span: Span, + snippet: String, + name: Symbol, + ty: Ty<'a>, + }, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion(trait_selection_stp_wrap_one, applicability = "maybe-incorrect")] +pub struct SuggestTuplePatternOne { + pub variant: String, + #[suggestion_part(code = "{variant}(")] + pub span_low: Span, + #[suggestion_part(code = ")")] + pub span_high: Span, +} + +pub struct SuggestTuplePatternMany { + pub path: String, + pub cause_span: Span, + pub compatible_variants: Vec<String>, +} + +impl Subdiagnostic for SuggestTuplePatternMany { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + self, + diag: &mut Diag<'_, G>, + f: &F, + ) { + diag.arg("path", self.path); + let message = f(diag, crate::fluent_generated::trait_selection_stp_wrap_many.into()); + diag.multipart_suggestions( + message, + self.compatible_variants.into_iter().map(|variant| { + vec![ + (self.cause_span.shrink_to_lo(), format!("{variant}(")), + (self.cause_span.shrink_to_hi(), ")".to_string()), + ] + }), + rustc_errors::Applicability::MaybeIncorrect, + ); + } +} + +#[derive(Subdiagnostic)] +pub enum TypeErrorAdditionalDiags { + #[suggestion( + trait_selection_meant_byte_literal, + code = "b'{code}'", + applicability = "machine-applicable" + )] + MeantByteLiteral { + #[primary_span] + span: Span, + code: String, + }, + #[suggestion( + trait_selection_meant_char_literal, + code = "'{code}'", + applicability = "machine-applicable" + )] + MeantCharLiteral { + #[primary_span] + span: Span, + code: String, + }, + #[multipart_suggestion( + trait_selection_meant_str_literal, + applicability = "machine-applicable" + )] + MeantStrLiteral { + #[suggestion_part(code = "\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + }, + #[suggestion( + trait_selection_consider_specifying_length, + code = "{length}", + applicability = "maybe-incorrect" + )] + ConsiderSpecifyingLength { + #[primary_span] + span: Span, + length: u64, + }, + #[note(trait_selection_try_cannot_convert)] + TryCannotConvert { found: String, expected: String }, + #[suggestion( + trait_selection_tuple_trailing_comma, + code = ",", + applicability = "machine-applicable" + )] + TupleOnlyComma { + #[primary_span] + span: Span, + }, + #[multipart_suggestion( + trait_selection_tuple_trailing_comma, + applicability = "machine-applicable" + )] + TupleAlsoParentheses { + #[suggestion_part(code = "(")] + span_low: Span, + #[suggestion_part(code = ",)")] + span_high: Span, + }, + #[suggestion( + trait_selection_suggest_add_let_for_letchains, + style = "verbose", + applicability = "machine-applicable", + code = "let " + )] + AddLetForLetChains { + #[primary_span] + span: Span, + }, +} + +#[derive(Diagnostic)] +pub enum ObligationCauseFailureCode { + #[diag(trait_selection_oc_method_compat, code = E0308)] + MethodCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_type_compat, code = E0308)] + TypeCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_const_compat, code = E0308)] + ConstCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_try_compat, code = E0308)] + TryCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_match_compat, code = E0308)] + MatchCompat { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_if_else_different, code = E0308)] + IfElseDifferent { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_no_else, code = E0317)] + NoElse { + #[primary_span] + span: Span, + }, + #[diag(trait_selection_oc_no_diverge, code = E0308)] + NoDiverge { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_fn_main_correct_type, code = E0580)] + FnMainCorrectType { + #[primary_span] + span: Span, + }, + #[diag(trait_selection_oc_fn_start_correct_type, code = E0308)] + FnStartCorrectType { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_fn_lang_correct_type, code = E0308)] + FnLangCorrectType { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + lang_item_name: Symbol, + }, + #[diag(trait_selection_oc_intrinsic_correct_type, code = E0308)] + IntrinsicCorrectType { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_method_correct_type, code = E0308)] + MethodCorrectType { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_closure_selfref, code = E0644)] + ClosureSelfref { + #[primary_span] + span: Span, + }, + #[diag(trait_selection_oc_cant_coerce, code = E0308)] + CantCoerce { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, + #[diag(trait_selection_oc_generic, code = E0308)] + Generic { + #[primary_span] + span: Span, + #[subdiagnostic] + subdiags: Vec<TypeErrorAdditionalDiags>, + }, +} + +#[derive(Subdiagnostic)] +pub enum AddPreciseCapturing { + #[suggestion( + trait_selection_precise_capturing_new, + style = "verbose", + code = " + use<{concatenated_bounds}>", + applicability = "machine-applicable" + )] + New { + #[primary_span] + span: Span, + new_lifetime: Symbol, + concatenated_bounds: String, + }, + #[suggestion( + trait_selection_precise_capturing_existing, + style = "verbose", + code = "{pre}{new_lifetime}{post}", + applicability = "machine-applicable" + )] + Existing { + #[primary_span] + span: Span, + new_lifetime: Symbol, + pre: &'static str, + post: &'static str, + }, +} + +pub struct AddPreciseCapturingAndParams { + pub suggs: Vec<(Span, String)>, + pub new_lifetime: Symbol, + pub apit_spans: Vec<Span>, +} + +impl Subdiagnostic for AddPreciseCapturingAndParams { + fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( + self, + diag: &mut Diag<'_, G>, + _f: &F, + ) { + diag.arg("new_lifetime", self.new_lifetime); + diag.multipart_suggestion_verbose( + fluent::trait_selection_precise_capturing_new_but_apit, + self.suggs, + Applicability::MaybeIncorrect, + ); + diag.span_note(self.apit_spans, fluent::trait_selection_warn_removing_apit_params); + } +} diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs index d71b7f3c264..1f18cd8c8d8 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs @@ -173,7 +173,7 @@ impl Subdiagnostic for RegionExplanation<'_> { diag.arg("desc_kind", self.desc.kind); diag.arg("desc_arg", self.desc.arg); - let msg = f(diag, fluent::infer_region_explanation.into()); + let msg = f(diag, fluent::trait_selection_region_explanation.into()); if let Some(span) = self.desc.span { diag.span_note(span, msg); } else { diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index d0a12d73941..1bd66266936 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -22,11 +22,14 @@ #![feature(control_flow_enum)] #![feature(extract_if)] #![feature(if_let_guard)] +#![feature(iter_intersperse)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustdoc_internals)] +#![feature(try_blocks)] #![feature(type_alias_impl_trait)] #![feature(unwrap_infallible)] +#![feature(yeet_expr)] #![recursion_limit = "512"] // For rustdoc // tidy-alphabetical-end diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index ca313590265..ddaef7c159f 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -1,7 +1,8 @@ use std::fmt::Debug; use std::marker::PhantomData; -use crate::error_reporting::traits::{OverflowCause, TypeErrCtxtOverflowExt}; +use crate::error_reporting::traits::OverflowCause; +use crate::error_reporting::InferCtxtErrorExt; use crate::traits::query::evaluate_obligation::InferCtxtExt; use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError}; use rustc_data_structures::stack::ensure_sufficient_stack; diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs index bdc27e734f9..49730b532a3 100644 --- a/compiler/rustc_trait_selection/src/traits/engine.rs +++ b/compiler/rustc_trait_selection/src/traits/engine.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use super::{FromSolverError, TraitEngine}; use super::{FulfillmentContext, ScrubbedTraitError}; -use crate::error_reporting::traits::TypeErrCtxtExt; +use crate::error_reporting::InferCtxtErrorExt; use crate::regions::InferCtxtRegionExt; use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt; use crate::solve::NextSolverError; diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 5597c8be592..cc0bb7a60b2 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -1,4 +1,3 @@ -use crate::error_reporting::traits::TypeErrCtxtOverflowExt; use crate::infer::{InferCtxt, TyOrConstInferVar}; use crate::traits::normalize::normalize_with_depth_to; use rustc_data_structures::captures::Captures; @@ -25,6 +24,7 @@ use super::Unimplemented; use super::{const_evaluatable, ScrubbedTraitError}; use super::{FulfillmentError, FulfillmentErrorCode}; +use crate::error_reporting::InferCtxtErrorExt; use crate::traits::project::PolyProjectionObligation; use crate::traits::project::ProjectionCacheKeyExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt; diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs index baec2268629..d749b686803 100644 --- a/compiler/rustc_trait_selection/src/traits/misc.rs +++ b/compiler/rustc_trait_selection/src/traits/misc.rs @@ -4,6 +4,7 @@ use crate::regions::InferCtxtRegionExt; use crate::traits::{self, FulfillmentError, ObligationCause}; use hir::LangItem; +use rustc_ast::Mutability; use rustc_data_structures::fx::FxIndexSet; use rustc_hir as hir; use rustc_infer::infer::outlives::env::OutlivesEnvironment; @@ -19,6 +20,8 @@ pub enum CopyImplementationError<'tcx> { } pub enum ConstParamTyImplementationError<'tcx> { + UnsizedConstParamsFeatureRequired, + InvalidInnerTyOfBuiltinTy(Vec<(Ty<'tcx>, InfringingFieldsReason<'tcx>)>), InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>), NotAnAdtOrBuiltinAllowed, } @@ -77,9 +80,9 @@ pub fn type_allowed_to_implement_copy<'tcx>( Ok(()) } -/// Checks that the fields of the type (an ADT) all implement `ConstParamTy`. +/// Checks that the fields of the type (an ADT) all implement `(Unsized?)ConstParamTy`. /// -/// If fields don't implement `ConstParamTy`, return an error containing a list of +/// If fields don't implement `(Unsized?)ConstParamTy`, return an error containing a list of /// those violating fields. /// /// If it's not an ADT, int ty, `bool` or `char`, returns `Err(NotAnAdtOrBuiltinAllowed)`. @@ -87,35 +90,95 @@ pub fn type_allowed_to_implement_const_param_ty<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, self_type: Ty<'tcx>, + lang_item: LangItem, parent_cause: ObligationCause<'tcx>, ) -> Result<(), ConstParamTyImplementationError<'tcx>> { - let (adt, args) = match self_type.kind() { - // `core` provides these impls. - ty::Uint(_) - | ty::Int(_) - | ty::Bool - | ty::Char - | ty::Str - | ty::Array(..) - | ty::Slice(_) - | ty::Ref(.., hir::Mutability::Not) - | ty::Tuple(_) => return Ok(()), + assert!(matches!(lang_item, LangItem::ConstParamTy | LangItem::UnsizedConstParamTy)); - &ty::Adt(adt, args) => (adt, args), + let inner_tys: Vec<_> = match *self_type.kind() { + // Trivially okay as these types are all: + // - Sized + // - Contain no nested types + // - Have structural equality + ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => return Ok(()), + + // Handle types gated under `feature(unsized_const_params)` + // FIXME(unsized_const_params): Make `const N: [u8]` work then forbid references + ty::Slice(inner_ty) | ty::Ref(_, inner_ty, Mutability::Not) + if lang_item == LangItem::UnsizedConstParamTy => + { + vec![inner_ty] + } + ty::Str if lang_item == LangItem::UnsizedConstParamTy => { + vec![Ty::new_slice(tcx, tcx.types.u8)] + } + ty::Str | ty::Slice(..) | ty::Ref(_, _, Mutability::Not) => { + return Err(ConstParamTyImplementationError::UnsizedConstParamsFeatureRequired); + } + + ty::Array(inner_ty, _) => vec![inner_ty], + + // `str` morally acts like a newtype around `[u8]` + ty::Tuple(inner_tys) => inner_tys.into_iter().collect(), + + ty::Adt(adt, args) if adt.is_enum() || adt.is_struct() => { + all_fields_implement_trait( + tcx, + param_env, + self_type, + adt, + args, + parent_cause.clone(), + lang_item, + ) + .map_err(ConstParamTyImplementationError::InfrigingFields)?; + + vec![] + } _ => return Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed), }; - all_fields_implement_trait( - tcx, - param_env, - self_type, - adt, - args, - parent_cause, - hir::LangItem::ConstParamTy, - ) - .map_err(ConstParamTyImplementationError::InfrigingFields)?; + let mut infringing_inner_tys = vec![]; + for inner_ty in inner_tys { + // We use an ocx per inner ty for better diagnostics + let infcx = tcx.infer_ctxt().build(); + let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx); + + ocx.register_bound( + parent_cause.clone(), + param_env, + inner_ty, + tcx.require_lang_item(lang_item, Some(parent_cause.span)), + ); + + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Fulfill(errors))); + continue; + } + + // Check regions assuming the self type of the impl is WF + let outlives_env = OutlivesEnvironment::with_bounds( + param_env, + infcx.implied_bounds_tys( + param_env, + parent_cause.body_id, + &FxIndexSet::from_iter([self_type]), + ), + ); + let errors = infcx.resolve_regions(&outlives_env); + if !errors.is_empty() { + infringing_inner_tys.push((inner_ty, InfringingFieldsReason::Regions(errors))); + continue; + } + } + + if !infringing_inner_tys.is_empty() { + return Err(ConstParamTyImplementationError::InvalidInnerTyOfBuiltinTy( + infringing_inner_tys, + )); + } Ok(()) } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index f7eb1730582..c57ca014799 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -22,7 +22,7 @@ mod util; pub mod vtable; pub mod wf; -use crate::error_reporting::traits::TypeErrCtxtExt as _; +use crate::error_reporting::InferCtxtErrorExt; use crate::infer::outlives::env::OutlivesEnvironment; use crate::infer::{InferCtxt, TyCtxtInferExt}; use crate::regions::InferCtxtRegionExt; diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs index 01ba8c02ea6..26cb9bb5a3d 100644 --- a/compiler/rustc_trait_selection/src/traits/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/normalize.rs @@ -3,7 +3,7 @@ use super::SelectionContext; use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer}; use crate::error_reporting::traits::OverflowCause; -use crate::error_reporting::traits::TypeErrCtxtOverflowExt; +use crate::error_reporting::InferCtxtErrorExt; use crate::solve::NextSolverError; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_infer::infer::at::At; diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index c11e86abef8..75f1af7fcf5 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -3,7 +3,7 @@ //! `normalize_canonicalized_projection_ty` query when it encounters projections. use crate::error_reporting::traits::OverflowCause; -use crate::error_reporting::traits::TypeErrCtxtOverflowExt; +use crate::error_reporting::InferCtxtErrorExt; use crate::infer::at::At; use crate::infer::canonical::OriginalQueryValues; use crate::infer::{InferCtxt, InferOk}; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 7a93f59f163..d6590322caa 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -18,7 +18,7 @@ use super::{ TraitQueryMode, }; -use crate::error_reporting::traits::TypeErrCtxtOverflowExt; +use crate::error_reporting::InferCtxtErrorExt; use crate::infer::{InferCtxt, InferCtxtExt, InferOk, TypeFreshener}; use crate::solve::InferCtxtSelectExt as _; use crate::traits::normalize::normalize_with_depth; diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs index 3ee5fd876ff..ada2c8e81de 100644 --- a/compiler/rustc_traits/src/codegen.rs +++ b/compiler/rustc_traits/src/codegen.rs @@ -7,7 +7,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::bug; use rustc_middle::traits::CodegenObligationError; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtOverflowExt; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::{ ImplSource, Obligation, ObligationCause, ObligationCtxt, ScrubbedTraitError, SelectionContext, Unimplemented, diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs index 2d70fdc3935..06cd6389efc 100644 --- a/compiler/rustc_traits/src/normalize_projection_ty.rs +++ b/compiler/rustc_traits/src/normalize_projection_ty.rs @@ -2,7 +2,7 @@ use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::query::Providers; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; -use rustc_trait_selection::error_reporting::traits::TypeErrCtxtOverflowExt; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::{ normalize::NormalizationResult, CanonicalAliasGoal, NoSolution, diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index f1683f5449f..1a51c95ecdf 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -65,15 +65,13 @@ impl<I: Interner> fmt::Debug for ConstKind<I> { match self { Param(param) => write!(f, "{param:?}"), - Infer(var) => write!(f, "{:?}", &var), + Infer(var) => write!(f, "{var:?}"), Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var), Placeholder(placeholder) => write!(f, "{placeholder:?}"), - Unevaluated(uv) => { - write!(f, "{:?}", &uv) - } - Value(ty, valtree) => write!(f, "({valtree:?}: {:?})", &ty), + Unevaluated(uv) => write!(f, "{uv:?}"), + Value(ty, valtree) => write!(f, "({valtree:?}: {ty:?})"), Error(_) => write!(f, "{{const error}}"), - Expr(expr) => write!(f, "{:?}", &expr), + Expr(expr) => write!(f, "{expr:?}"), } } } diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index 7abcc370c88..140c89af147 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -232,7 +232,7 @@ impl<I: Interner> fmt::Debug for RegionKind<I> { ReStatic => f.write_str("'static"), - ReVar(vid) => write!(f, "{:?}", &vid), + ReVar(vid) => write!(f, "{vid:?}"), RePlaceholder(placeholder) => write!(f, "{placeholder:?}"), diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 4ffebef9f1f..9896425a341 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -367,18 +367,16 @@ impl<I: Interner> fmt::Debug for TyKind<I> { } Foreign(d) => f.debug_tuple("Foreign").field(d).finish(), Str => write!(f, "str"), - Array(t, c) => write!(f, "[{:?}; {:?}]", &t, &c), - Pat(t, p) => write!(f, "pattern_type!({:?} is {:?})", &t, &p), + Array(t, c) => write!(f, "[{t:?}; {c:?}]"), + Pat(t, p) => write!(f, "pattern_type!({t:?} is {p:?})"), Slice(t) => write!(f, "[{:?}]", &t), RawPtr(ty, mutbl) => write!(f, "*{} {:?}", mutbl.ptr_str(), ty), Ref(r, t, m) => write!(f, "&{:?} {}{:?}", r, m.prefix_str(), t), FnDef(d, s) => f.debug_tuple("FnDef").field(d).field(&s).finish(), - FnPtr(s) => write!(f, "{:?}", &s), + FnPtr(s) => write!(f, "{s:?}"), Dynamic(p, r, repr) => match repr { - DynKind::Dyn => write!(f, "dyn {:?} + {:?}", &p, &r), - DynKind::DynStar => { - write!(f, "dyn* {:?} + {:?}", &p, &r) - } + DynKind::Dyn => write!(f, "dyn {p:?} + {r:?}"), + DynKind::DynStar => write!(f, "dyn* {p:?} + {r:?}"), }, Closure(d, s) => f.debug_tuple("Closure").field(d).field(&s).finish(), CoroutineClosure(d, s) => f.debug_tuple("CoroutineClosure").field(d).field(&s).finish(), @@ -392,7 +390,7 @@ impl<I: Interner> fmt::Debug for TyKind<I> { if count > 0 { write!(f, ", ")?; } - write!(f, "{:?}", &ty)?; + write!(f, "{ty:?}")?; count += 1; } // unary tuples need a trailing comma @@ -1050,7 +1048,7 @@ impl<I: Interner> fmt::Debug for FnSig<I> { if i > 0 { write!(f, ", ")?; } - write!(f, "{:?}", &ty)?; + write!(f, "{ty:?}")?; } if *c_variadic { if inputs.is_empty() { diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs index 83734a0d138..18ecccb4536 100644 --- a/compiler/stable_mir/src/mir/pretty.rs +++ b/compiler/stable_mir/src/mir/pretty.rs @@ -179,7 +179,7 @@ fn pretty_terminator_head<W: Write>(writer: &mut W, terminator: &TerminatorKind) if !expected { write!(writer, "!")?; } - write!(writer, "{}, ", &pretty_operand(cond))?; + write!(writer, "{}, ", pretty_operand(cond))?; pretty_assert_message(writer, msg)?; write!(writer, ")") } @@ -325,7 +325,7 @@ fn pretty_ty_const(ct: &TyConst) -> String { fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> { match rval { Rvalue::AddressOf(mutability, place) => { - write!(writer, "&raw {}(*{:?})", &pretty_mut(*mutability), place) + write!(writer, "&raw {}(*{:?})", pretty_mut(*mutability), place) } Rvalue::Aggregate(aggregate_kind, operands) => { // FIXME: Add pretty_aggregate function that returns a pretty string @@ -336,13 +336,13 @@ fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> { write!(writer, ")") } Rvalue::BinaryOp(bin, op1, op2) => { - write!(writer, "{:?}({}, {})", bin, &pretty_operand(op1), pretty_operand(op2)) + write!(writer, "{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2)) } Rvalue::Cast(_, op, ty) => { write!(writer, "{} as {}", pretty_operand(op), ty) } Rvalue::CheckedBinaryOp(bin, op1, op2) => { - write!(writer, "Checked{:?}({}, {})", bin, &pretty_operand(op1), pretty_operand(op2)) + write!(writer, "Checked{:?}({}, {})", bin, pretty_operand(op1), pretty_operand(op2)) } Rvalue::CopyForDeref(deref) => { write!(writer, "CopyForDeref({:?})", deref) @@ -363,7 +363,7 @@ fn pretty_rvalue<W: Write>(writer: &mut W, rval: &Rvalue) -> io::Result<()> { write!(writer, "{kind}{:?}", place) } Rvalue::Repeat(op, cnst) => { - write!(writer, "{} \" \" {}", &pretty_operand(op), &pretty_ty_const(cnst)) + write!(writer, "{} \" \" {}", pretty_operand(op), pretty_ty_const(cnst)) } Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::ThreadLocalRef(item) => { |
