diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:11:38 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:13:23 +0000 |
commit | 20431706a863f92cb37dc512fef6e48d192aaf2c (patch) | |
tree | 2867f13f5fd5437ba628c67d7f87309ccadcd286 /compiler/rustc_typeck | |
parent | Releasing progress-linux version 1.65.0+dfsg1-2~progress7.99u1. (diff) | |
download | rustc-20431706a863f92cb37dc512fef6e48d192aaf2c.tar.xz rustc-20431706a863f92cb37dc512fef6e48d192aaf2c.zip |
Merging upstream version 1.66.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_hir_analysis/Cargo.toml (renamed from compiler/rustc_typeck/Cargo.toml) | 3 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/README.md (renamed from compiler/rustc_typeck/README.md) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/astconv/errors.rs (renamed from compiler/rustc_typeck/src/astconv/errors.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/astconv/generics.rs (renamed from compiler/rustc_typeck/src/astconv/generics.rs) | 15 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/astconv/mod.rs (renamed from compiler/rustc_typeck/src/astconv/mod.rs) | 214 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/bounds.rs (renamed from compiler/rustc_typeck/src/bounds.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/check/check.rs (renamed from compiler/rustc_typeck/src/check/check.rs) | 588 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/check/compare_method.rs (renamed from compiler/rustc_typeck/src/check/compare_method.rs) | 1099 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/check/dropck.rs (renamed from compiler/rustc_typeck/src/check/dropck.rs) | 12 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/check/intrinsic.rs (renamed from compiler/rustc_typeck/src/check/intrinsic.rs) | 32 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/check/intrinsicck.rs (renamed from compiler/rustc_typeck/src/check/intrinsicck.rs) | 112 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/check/mod.rs (renamed from compiler/rustc_typeck/src/check/mod.rs) | 504 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/check/region.rs (renamed from compiler/rustc_typeck/src/check/region.rs) | 8 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/check/wfcheck.rs (renamed from compiler/rustc_typeck/src/check/wfcheck.rs) | 195 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/check_unused.rs (renamed from compiler/rustc_typeck/src/check_unused.rs) | 31 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs (renamed from compiler/rustc_typeck/src/coherence/inherent_impls.rs) | 18 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs (renamed from compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs) | 58 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/mod.rs (renamed from compiler/rustc_typeck/src/coherence/mod.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/orphan.rs (renamed from compiler/rustc_typeck/src/coherence/orphan.rs) | 38 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/coherence/unsafety.rs (renamed from compiler/rustc_typeck/src/coherence/unsafety.rs) | 32 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/collect.rs (renamed from compiler/rustc_typeck/src/collect.rs) | 1302 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/collect/item_bounds.rs (renamed from compiler/rustc_typeck/src/collect/item_bounds.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/collect/type_of.rs (renamed from compiler/rustc_typeck/src/collect/type_of.rs) | 128 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/constrained_generic_params.rs (renamed from compiler/rustc_typeck/src/constrained_generic_params.rs) | 10 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/errors.rs (renamed from compiler/rustc_typeck/src/errors.rs) | 233 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/hir_wf_check.rs (renamed from compiler/rustc_typeck/src/hir_wf_check.rs) | 58 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/impl_wf_check.rs (renamed from compiler/rustc_typeck/src/impl_wf_check.rs) | 43 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs (renamed from compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs) | 98 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/lib.rs (renamed from compiler/rustc_typeck/src/lib.rs) | 81 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/outlives/explicit.rs (renamed from compiler/rustc_typeck/src/outlives/explicit.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs (renamed from compiler/rustc_typeck/src/outlives/implicit_infer.rs) | 4 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/outlives/mod.rs (renamed from compiler/rustc_typeck/src/outlives/mod.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/outlives/test.rs (renamed from compiler/rustc_typeck/src/outlives/test.rs) | 6 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/outlives/utils.rs (renamed from compiler/rustc_typeck/src/outlives/utils.rs) | 17 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/structured_errors.rs (renamed from compiler/rustc_typeck/src/structured_errors.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs (renamed from compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs (renamed from compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs (renamed from compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/variance/constraints.rs (renamed from compiler/rustc_typeck/src/variance/constraints.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/variance/mod.rs (renamed from compiler/rustc_typeck/src/variance/mod.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/variance/solve.rs (renamed from compiler/rustc_typeck/src/variance/solve.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/variance/terms.rs (renamed from compiler/rustc_typeck/src/variance/terms.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/variance/test.rs (renamed from compiler/rustc_typeck/src/variance/test.rs) | 7 | ||||
-rw-r--r-- | compiler/rustc_hir_analysis/src/variance/xform.rs (renamed from compiler/rustc_typeck/src/variance/xform.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/_match.rs (renamed from compiler/rustc_typeck/src/check/_match.rs) | 19 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/autoderef.rs (renamed from compiler/rustc_typeck/src/check/autoderef.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/callee.rs (renamed from compiler/rustc_typeck/src/check/callee.rs) | 401 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/cast.rs (renamed from compiler/rustc_typeck/src/check/cast.rs) | 174 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/closure.rs (renamed from compiler/rustc_typeck/src/check/closure.rs) | 37 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/coercion.rs (renamed from compiler/rustc_typeck/src/check/coercion.rs) | 83 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/demand.rs (renamed from compiler/rustc_typeck/src/check/demand.rs) | 135 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/diverges.rs (renamed from compiler/rustc_typeck/src/check/diverges.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/expectation.rs (renamed from compiler/rustc_typeck/src/check/expectation.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/expr.rs (renamed from compiler/rustc_typeck/src/check/expr.rs) | 118 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/expr_use_visitor.rs (renamed from compiler/rustc_typeck/src/expr_use_visitor.rs) | 15 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fallback.rs (renamed from compiler/rustc_typeck/src/check/fallback.rs) | 4 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs (renamed from compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs) | 116 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs (renamed from compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs (renamed from compiler/rustc_typeck/src/check/fn_ctxt/checks.rs) | 72 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs (renamed from compiler/rustc_typeck/src/check/fn_ctxt/mod.rs) | 26 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs (renamed from compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs) | 199 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/gather_locals.rs (renamed from compiler/rustc_typeck/src/check/gather_locals.rs) | 3 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs (renamed from compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs) | 2 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs (renamed from compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs (renamed from compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs (renamed from compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs) | 2 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs (renamed from compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs) | 15 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/generator_interior/mod.rs (renamed from compiler/rustc_typeck/src/check/generator_interior.rs) | 49 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/inherited.rs (renamed from compiler/rustc_typeck/src/check/inherited.rs) | 38 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/mem_categorization.rs (renamed from compiler/rustc_typeck/src/mem_categorization.rs) | 24 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/confirm.rs (renamed from compiler/rustc_typeck/src/check/method/confirm.rs) | 6 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/mod.rs (renamed from compiler/rustc_typeck/src/check/method/mod.rs) | 5 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/prelude2021.rs (renamed from compiler/rustc_typeck/src/check/method/prelude2021.rs) | 121 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/probe.rs (renamed from compiler/rustc_typeck/src/check/method/probe.rs) | 163 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/suggest.rs (renamed from compiler/rustc_typeck/src/check/method/suggest.rs) | 388 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/op.rs (renamed from compiler/rustc_typeck/src/check/op.rs) | 15 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/pat.rs (renamed from compiler/rustc_typeck/src/check/pat.rs) | 10 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/place_op.rs (renamed from compiler/rustc_typeck/src/check/place_op.rs) | 4 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/rvalue_scopes.rs (renamed from compiler/rustc_typeck/src/check/rvalue_scopes.rs) | 0 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/upvar.rs (renamed from compiler/rustc_typeck/src/check/upvar.rs) | 28 | ||||
-rw-r--r-- | compiler/rustc_hir_typeck/src/writeback.rs (renamed from compiler/rustc_typeck/src/check/writeback.rs) | 79 | ||||
-rw-r--r-- | compiler/rustc_typeck/src/coherence/builtin.rs | 594 |
82 files changed, 2990 insertions, 4901 deletions
diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index cae29c1d3..0761d8cdb 100644 --- a/compiler/rustc_typeck/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rustc_typeck" +name = "rustc_hir_analysis" version = "0.0.0" edition = "2021" @@ -26,7 +26,6 @@ rustc_span = { path = "../rustc_span" } rustc_index = { path = "../rustc_index" } rustc_infer = { path = "../rustc_infer" } rustc_trait_selection = { path = "../rustc_trait_selection" } -rustc_ty_utils = { path = "../rustc_ty_utils" } rustc_lint = { path = "../rustc_lint" } rustc_serialize = { path = "../rustc_serialize" } rustc_type_ir = { path = "../rustc_type_ir" } diff --git a/compiler/rustc_typeck/README.md b/compiler/rustc_hir_analysis/README.md index b61dbd8c9..b61dbd8c9 100644 --- a/compiler/rustc_typeck/README.md +++ b/compiler/rustc_hir_analysis/README.md diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index a9152bdc5..a9152bdc5 100644 --- a/compiler/rustc_typeck/src/astconv/errors.rs +++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs index afac75de2..47915b4bd 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -83,9 +83,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Res::Def(DefKind::TyParam, src_def_id) => { if let Some(param_local_id) = param.def_id.as_local() { let param_name = tcx.hir().ty_param_name(param_local_id); - let param_type = tcx.infer_ctxt().enter(|infcx| { - infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id)) - }); + let infcx = tcx.infer_ctxt().build(); + let param_type = + infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id)); if param_type.is_suggestable(tcx, false) { err.span_suggestion( tcx.def_span(src_def_id), @@ -448,8 +448,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let infer_lifetimes = (gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params(); - if gen_pos != GenericArgPosition::Type && !gen_args.bindings.is_empty() { - Self::prohibit_assoc_ty_binding(tcx, gen_args.bindings[0].span); + if gen_pos != GenericArgPosition::Type && let Some(b) = gen_args.bindings.first() { + Self::prohibit_assoc_ty_binding(tcx, b.span); } let explicit_late_bound = @@ -649,9 +649,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { LATE_BOUND_LIFETIME_ARGUMENTS, args.args[0].hir_id(), multispan, - |lint| { - lint.build(msg).emit(); - }, + msg, + |lint| lint, ); } diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 4bf9562e2..38f195dab 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -26,7 +26,7 @@ use rustc_hir::intravisit::{walk_generics, Visitor as _}; use rustc_hir::lang_items::LangItem; use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin}; use rustc_middle::middle::stability::AllowUnstable; -use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, Subst, SubstsRef}; +use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef}; use rustc_middle::ty::DynKind; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::{ @@ -36,7 +36,7 @@ use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECT use rustc_span::edition::Edition; use rustc_span::lev_distance::find_best_match_for_name; use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::Span; +use rustc_span::{sym, Span}; use rustc_target::spec::abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::astconv_object_safety_violations; @@ -275,10 +275,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { item_segment.args(), item_segment.infer_args, None, + None, ); - let assoc_bindings = self.create_assoc_bindings_for_generic_args(item_segment.args()); - - if let Some(b) = assoc_bindings.first() { + if let Some(b) = item_segment.args().bindings.first() { Self::prohibit_assoc_ty_binding(self.tcx(), b.span); } @@ -326,6 +325,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { generic_args: &'a hir::GenericArgs<'_>, infer_args: bool, self_ty: Option<Ty<'tcx>>, + constness: Option<ty::BoundConstness>, ) -> (SubstsRef<'tcx>, GenericArgCountResult) { // If the type is parameterized by this region, then replace this // region with the current anon region binding (in other words, @@ -365,7 +365,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // here and so associated type bindings will be handled regardless of whether there are any // non-`Self` generic parameters. if generics.params.is_empty() { - return (tcx.intern_substs(&[]), arg_count); + return (tcx.intern_substs(parent_substs), arg_count); } struct SubstsForAstPathCtxt<'a, 'tcx> { @@ -536,6 +536,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &mut substs_ctx, ); + if let Some(ty::BoundConstness::ConstIfConst) = constness + && generics.has_self && !tcx.has_attr(def_id, sym::const_trait) + { + tcx.sess.emit_err(crate::errors::ConstBoundForNonConstTrait { span } ); + } + (substs, arg_count) } @@ -584,9 +590,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { assoc_bindings } - pub(crate) fn create_substs_for_associated_item( + pub fn create_substs_for_associated_item( &self, - tcx: TyCtxt<'tcx>, span: Span, item_def_id: DefId, item_segment: &hir::PathSegment<'_>, @@ -596,28 +601,22 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { "create_substs_for_associated_item(span: {:?}, item_def_id: {:?}, item_segment: {:?}", span, item_def_id, item_segment ); - if tcx.generics_of(item_def_id).params.is_empty() { - self.prohibit_generics(slice::from_ref(item_segment).iter(), |_| {}); - - parent_substs - } else { - let (args, _) = self.create_substs_for_ast_path( - span, - item_def_id, - parent_substs, - item_segment, - item_segment.args(), - item_segment.infer_args, - None, - ); - - let assoc_bindings = self.create_assoc_bindings_for_generic_args(item_segment.args()); - if let Some(b) = assoc_bindings.first() { - Self::prohibit_assoc_ty_binding(self.tcx(), b.span); - } + let (args, _) = self.create_substs_for_ast_path( + span, + item_def_id, + parent_substs, + item_segment, + item_segment.args(), + item_segment.infer_args, + None, + None, + ); - args + if let Some(b) = item_segment.args().bindings.first() { + Self::prohibit_assoc_ty_binding(self.tcx(), b.span); } + + args } /// Instantiates the path for the given trait reference, assuming that it's @@ -630,6 +629,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &self, trait_ref: &hir::TraitRef<'_>, self_ty: Ty<'tcx>, + constness: ty::BoundConstness, ) -> ty::TraitRef<'tcx> { self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {}); @@ -639,6 +639,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self_ty, trait_ref.path.segments.last().unwrap(), true, + Some(constness), ) } @@ -665,6 +666,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { args, infer_args, Some(self_ty), + Some(constness), ); let tcx = self.tcx(); @@ -690,6 +692,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { speculative, &mut dup_bindings, binding_span.unwrap_or(binding.span), + constness, ); // Okay to ignore `Err` because of `ErrorGuaranteed` (see above). } @@ -793,6 +796,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self_ty: Ty<'tcx>, trait_segment: &hir::PathSegment<'_>, is_impl: bool, + constness: Option<ty::BoundConstness>, ) -> ty::TraitRef<'tcx> { let (substs, _) = self.create_substs_for_ast_trait_ref( span, @@ -800,9 +804,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self_ty, trait_segment, is_impl, + constness, ); - let assoc_bindings = self.create_assoc_bindings_for_generic_args(trait_segment.args()); - if let Some(b) = assoc_bindings.first() { + if let Some(b) = trait_segment.args().bindings.first() { Self::prohibit_assoc_ty_binding(self.tcx(), b.span); } ty::TraitRef::new(trait_def_id, substs) @@ -816,6 +820,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { self_ty: Ty<'tcx>, trait_segment: &'a hir::PathSegment<'a>, is_impl: bool, + constness: Option<ty::BoundConstness>, ) -> (SubstsRef<'tcx>, GenericArgCountResult) { self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl); @@ -827,6 +832,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { trait_segment.args(), trait_segment.infer_args, Some(self_ty), + constness, ) } @@ -1038,6 +1044,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { speculative: bool, dup_bindings: &mut FxHashMap<DefId, Span>, path_span: Span, + constness: ty::BoundConstness, ) -> Result<(), ErrorGuaranteed> { // Given something like `U: SomeTrait<T = X>`, we want to produce a // predicate like `<U as SomeTrait>::T = X`. This is somewhat @@ -1127,17 +1134,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }; let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item( - tcx, path_span, assoc_item.def_id, &item_segment, trait_ref.substs, ); - debug!( - "add_predicates_for_ast_type_binding: substs for trait-ref and assoc_item: {:?}", - substs_trait_ref_and_assoc_item - ); + debug!(?substs_trait_ref_and_assoc_item); ty::ProjectionTy { item_def_id: assoc_item.def_id, @@ -1158,8 +1161,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx.collect_constrained_late_bound_regions(&projection_ty); let late_bound_in_ty = tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty)); - debug!("late_bound_in_trait_ref = {:?}", late_bound_in_trait_ref); - debug!("late_bound_in_ty = {:?}", late_bound_in_ty); + debug!(?late_bound_in_trait_ref); + debug!(?late_bound_in_ty); // FIXME: point at the type params that don't have appropriate lifetimes: // struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F); @@ -1660,6 +1663,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Checks that `bounds` contains exactly one element and reports appropriate // errors otherwise. + #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, is_equality), ret)] fn one_bound_for_assoc_type<I>( &self, all_candidates: impl Fn() -> I, @@ -1689,10 +1693,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return Err(reported); } }; - debug!("one_bound_for_assoc_type: bound = {:?}", bound); + debug!(?bound); if let Some(bound2) = next_cand { - debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2); + debug!(?bound2); let is_equality = is_equality(); let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates); @@ -1788,6 +1792,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // parameter or `Self`. // NOTE: When this function starts resolving `Trait::AssocTy` successfully // it should also start reporting the `BARE_TRAIT_OBJECTS` lint. + #[instrument(level = "debug", skip(self, hir_ref_id, span, qself, assoc_segment), fields(assoc_ident=?assoc_segment.ident), ret)] pub fn associated_path_to_ty( &self, hir_ref_id: hir::HirId, @@ -1805,8 +1810,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Res::Err }; - debug!("associated_path_to_ty: {:?}::{}", qself_ty, assoc_ident); - // Check if we have an enum variant. let mut variant_resolution = None; if let ty::Adt(adt_def, _) = qself_ty.kind() { @@ -1910,7 +1913,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Find the type of the associated item, and the trait where the associated // item is declared. let bound = match (&qself_ty.kind(), qself_res) { - (_, Res::SelfTy { trait_: Some(_), alias_to: Some((impl_def_id, _)) }) => { + (_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => { // `Self` in an impl of a trait -- we have a concrete self type and a // trait reference. let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else { @@ -1929,8 +1932,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } ( &ty::Param(_), - Res::SelfTy { trait_: Some(param_did), alias_to: None } - | Res::Def(DefKind::TyParam, param_did), + Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), ) => self.find_bound_for_assoc_item(param_did.expect_local(), assoc_ident, span)?, _ => { let reported = if variant_resolution.is_some() { @@ -2023,30 +2025,35 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { tcx.check_stability(item.def_id, Some(hir_ref_id), span, None); if let Some(variant_def_id) = variant_resolution { - tcx.struct_span_lint_hir(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| { - let mut err = lint.build("ambiguous associated item"); - let mut could_refer_to = |kind: DefKind, def_id, also| { - let note_msg = format!( - "`{}` could{} refer to the {} defined here", - assoc_ident, - also, - kind.descr(def_id) - ); - err.span_note(tcx.def_span(def_id), ¬e_msg); - }; + tcx.struct_span_lint_hir( + AMBIGUOUS_ASSOCIATED_ITEMS, + hir_ref_id, + span, + "ambiguous associated item", + |lint| { + let mut could_refer_to = |kind: DefKind, def_id, also| { + let note_msg = format!( + "`{}` could{} refer to the {} defined here", + assoc_ident, + also, + kind.descr(def_id) + ); + lint.span_note(tcx.def_span(def_id), ¬e_msg); + }; - could_refer_to(DefKind::Variant, variant_def_id, ""); - could_refer_to(kind, item.def_id, " also"); + could_refer_to(DefKind::Variant, variant_def_id, ""); + could_refer_to(kind, item.def_id, " also"); - err.span_suggestion( - span, - "use fully-qualified syntax", - format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), - Applicability::MachineApplicable, - ); + lint.span_suggestion( + span, + "use fully-qualified syntax", + format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), + Applicability::MachineApplicable, + ); - err.emit(); - }); + lint + }, + ); } Ok((ty, kind, item.def_id)) } @@ -2058,6 +2065,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { item_def_id: DefId, trait_segment: &hir::PathSegment<'_>, item_segment: &hir::PathSegment<'_>, + constness: ty::BoundConstness, ) -> Ty<'tcx> { let tcx = self.tcx(); @@ -2102,11 +2110,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { debug!("qpath_to_ty: self_type={:?}", self_ty); - let trait_ref = - self.ast_path_to_mono_trait_ref(span, trait_def_id, self_ty, trait_segment, false); + let trait_ref = self.ast_path_to_mono_trait_ref( + span, + trait_def_id, + self_ty, + trait_segment, + false, + Some(constness), + ); let item_substs = self.create_substs_for_associated_item( - tcx, span, item_def_id, item_segment, @@ -2217,8 +2230,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { for segment in segments { // Only emit the first error to avoid overloading the user with error messages. - if let [binding, ..] = segment.args().bindings { - Self::prohibit_assoc_ty_binding(self.tcx(), binding.span); + if let Some(b) = segment.args().bindings.first() { + Self::prohibit_assoc_ty_binding(self.tcx(), b.span); return true; } } @@ -2426,7 +2439,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let index = generics.param_def_id_to_index[&def_id.to_def_id()]; tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id)) } - Res::SelfTy { trait_: Some(_), alias_to: None } => { + Res::SelfTyParam { .. } => { // `Self` in trait or type alias. assert_eq!(opt_self_ty, None); self.prohibit_generics(path.segments.iter(), |err| { @@ -2441,7 +2454,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { }); tcx.types.self_param } - Res::SelfTy { trait_: _, alias_to: Some((def_id, forbid_generic)) } => { + Res::SelfTyAlias { alias_to: def_id, forbid_generic, .. } => { // `Self` in impl (we know the concrete type). assert_eq!(opt_self_ty, None); // Try to evaluate any array length constants. @@ -2543,12 +2556,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Res::Def(DefKind::AssocTy, def_id) => { debug_assert!(path.segments.len() >= 2); self.prohibit_generics(path.segments[..path.segments.len() - 2].iter(), |_| {}); + // HACK: until we support `<Type as ~const Trait>`, assume all of them are. + let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) { + ty::BoundConstness::ConstIfConst + } else { + ty::BoundConstness::NotConst + }; self.qpath_to_ty( span, opt_self_ty, def_id, &path.segments[path.segments.len() - 2], path.segments.last().unwrap(), + constness, ) } Res::PrimTy(prim_ty) => { @@ -2641,7 +2661,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => { let opaque_ty = tcx.hir().item(item_id); - let def_id = item_id.def_id.to_def_id(); + let def_id = item_id.owner_id.to_def_id(); match opaque_ty.kind { hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => { @@ -2667,6 +2687,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { &GenericArgs::none(), true, None, + None, ); EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id))) .subst(tcx, substs) @@ -2775,6 +2796,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } + #[instrument(level = "debug", skip(self, hir_id, unsafety, abi, decl, generics, hir_ty), ret)] pub fn ty_of_fn( &self, hir_id: hir::HirId, @@ -2784,8 +2806,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { generics: Option<&hir::Generics<'_>>, hir_ty: Option<&hir::Ty<'_>>, ) -> ty::PolyFnSig<'tcx> { - debug!("ty_of_fn"); - let tcx = self.tcx(); let bound_vars = tcx.late_bound_vars(hir_id); debug!(?bound_vars); @@ -2835,7 +2855,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(), }; - debug!("ty_of_fn: output_ty={:?}", output_ty); + debug!(?output_ty); let fn_ty = tcx.mk_fn_sig(input_tys.into_iter(), output_ty, decl.c_variadic, unsafety, abi); let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars); @@ -2912,8 +2932,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) = hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") }; - let trait_ref = - self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty)); + let trait_ref = self.instantiate_mono_trait_ref( + i.of_trait.as_ref()?, + self.ast_ty_to_ty(i.self_ty), + ty::BoundConstness::NotConst, + ); let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind( tcx, @@ -3009,7 +3032,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { /// Make sure that we are in the condition to suggest the blanket implementation. fn maybe_lint_blanket_trait_impl(&self, self_ty: &hir::Ty<'_>, diag: &mut Diagnostic) { let tcx = self.tcx(); - let parent_id = tcx.hir().get_parent_item(self_ty.hir_id); + let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id; if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(hir::Impl { @@ -3060,24 +3083,27 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { .map_or(false, |s| s.trim_end().ends_with('<')); let is_global = poly_trait_ref.trait_ref.path.is_global(); - let sugg = Vec::from_iter([ - ( - self_ty.span.shrink_to_lo(), - format!( - "{}dyn {}", - if needs_bracket { "<" } else { "" }, - if is_global { "(" } else { "" }, - ), + + let mut sugg = Vec::from_iter([( + self_ty.span.shrink_to_lo(), + format!( + "{}dyn {}", + if needs_bracket { "<" } else { "" }, + if is_global { "(" } else { "" }, ), - ( + )]); + + if is_global || needs_bracket { + sugg.push(( self_ty.span.shrink_to_hi(), format!( "{}{}", if is_global { ")" } else { "" }, if needs_bracket { ">" } else { "" }, ), - ), - ]); + )); + } + if self_ty.span.edition() >= Edition::Edition2021 { let msg = "trait objects must include the `dyn` keyword"; let label = "add `dyn` keyword before this trait"; @@ -3093,15 +3119,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { BARE_TRAIT_OBJECTS, self_ty.hir_id, self_ty.span, + msg, |lint| { - let mut diag = lint.build(msg); - diag.multipart_suggestion_verbose( + lint.multipart_suggestion_verbose( "use `dyn`", sugg, Applicability::MachineApplicable, ); - self.maybe_lint_blanket_trait_impl(&self_ty, &mut diag); - diag.emit(); + self.maybe_lint_blanket_trait_impl(&self_ty, lint); + lint }, ); } diff --git a/compiler/rustc_typeck/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs index 6a28bb16a..6a28bb16a 100644 --- a/compiler/rustc_typeck/src/bounds.rs +++ b/compiler/rustc_hir_analysis/src/bounds.rs diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index d6fa74c87..b70ac0205 100644 --- a/compiler/rustc_typeck/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -1,8 +1,7 @@ use crate::check::intrinsicck::InlineAsmCtxt; -use super::coercion::CoerceMany; use super::compare_method::check_type_bounds; -use super::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl}; +use super::compare_method::{compare_impl_method, compare_ty_impl}; use super::*; use rustc_attr as attr; use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; @@ -10,10 +9,8 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemKind, Node, PathSegment}; use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::Obligation; use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS; @@ -29,13 +26,12 @@ use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVE use rustc_span::symbol::sym; use rustc_span::{self, Span}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCtxt}; -use rustc_ty_utils::representability::{self, Representability}; use std::ops::ControlFlow; -pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) { +pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) { match tcx.sess.target.is_abi_supported(abi) { Some(true) => (), Some(false) => { @@ -48,9 +44,13 @@ pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ab .emit(); } None => { - tcx.struct_span_lint_hir(UNSUPPORTED_CALLING_CONVENTIONS, hir_id, span, |lint| { - lint.build("use of calling convention not supported on this target").emit(); - }); + tcx.struct_span_lint_hir( + UNSUPPORTED_CALLING_CONVENTIONS, + hir_id, + span, + "use of calling convention not supported on this target", + |lint| lint, + ); } } @@ -66,318 +66,10 @@ pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ab } } -/// Helper used for fns and closures. Does the grungy work of checking a function -/// body and returns the function context used for that purpose, since in the case of a fn item -/// there is still a bit more to do. -/// -/// * ... -/// * inherited: other fields inherited from the enclosing fn (if any) -#[instrument(skip(inherited, body), level = "debug")] -pub(super) fn check_fn<'a, 'tcx>( - inherited: &'a Inherited<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - fn_sig: ty::FnSig<'tcx>, - decl: &'tcx hir::FnDecl<'tcx>, - fn_id: hir::HirId, - body: &'tcx hir::Body<'tcx>, - can_be_generator: Option<hir::Movability>, - return_type_pre_known: bool, -) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) { - // Create the function context. This is either derived from scratch or, - // in the case of closures, based on the outer context. - let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id); - fcx.ps.set(UnsafetyState::function(fn_sig.unsafety, fn_id)); - fcx.return_type_pre_known = return_type_pre_known; - - let tcx = fcx.tcx; - let hir = tcx.hir(); - - let declared_ret_ty = fn_sig.output(); - - let ret_ty = - fcx.register_infer_ok_obligations(fcx.infcx.replace_opaque_types_with_inference_vars( - declared_ret_ty, - body.value.hir_id, - decl.output.span(), - param_env, - )); - // If we replaced declared_ret_ty with infer vars, then we must be inferring - // an opaque type, so set a flag so we can improve diagnostics. - fcx.return_type_has_opaque = ret_ty != declared_ret_ty; - - fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty))); - - let span = body.value.span; - - fn_maybe_err(tcx, span, fn_sig.abi); - - if fn_sig.abi == Abi::RustCall { - let expected_args = if let ImplicitSelfKind::None = decl.implicit_self { 1 } else { 2 }; - - let err = || { - let item = match tcx.hir().get(fn_id) { - Node::Item(hir::Item { kind: ItemKind::Fn(header, ..), .. }) => Some(header), - Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(header, ..), .. - }) => Some(header), - Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(header, ..), - .. - }) => Some(header), - // Closures are RustCall, but they tuple their arguments, so shouldn't be checked - Node::Expr(hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => None, - node => bug!("Item being checked wasn't a function/closure: {:?}", node), - }; - - if let Some(header) = item { - tcx.sess.span_err(header.span, "functions with the \"rust-call\" ABI must take a single non-self argument that is a tuple"); - } - }; - - if fn_sig.inputs().len() != expected_args { - err() - } else { - // FIXME(CraftSpider) Add a check on parameter expansion, so we don't just make the ICE happen later on - // This will probably require wide-scale changes to support a TupleKind obligation - // We can't resolve this without knowing the type of the param - if !matches!(fn_sig.inputs()[expected_args - 1].kind(), ty::Tuple(_) | ty::Param(_)) { - err() - } - } - } - - if body.generator_kind.is_some() && can_be_generator.is_some() { - let yield_ty = fcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); - fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); - - // Resume type defaults to `()` if the generator has no argument. - let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit()); - - fcx.resume_yield_tys = Some((resume_ty, yield_ty)); - } - - GatherLocalsVisitor::new(&fcx).visit_body(body); - - // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` - // (as it's created inside the body itself, not passed in from outside). - let maybe_va_list = if fn_sig.c_variadic { - let span = body.params.last().unwrap().span; - let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span)); - let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span)); - - Some(tcx.bound_type_of(va_list_did).subst(tcx, &[region.into()])) - } else { - None - }; - - // Add formal parameters. - let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs); - let inputs_fn = fn_sig.inputs().iter().copied(); - for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { - // Check the pattern. - let ty_span = try { inputs_hir?.get(idx)?.span }; - fcx.check_pat_top(¶m.pat, param_ty, ty_span, false); - - // Check that argument is Sized. - // The check for a non-trivial pattern is a hack to avoid duplicate warnings - // for simple cases like `fn foo(x: Trait)`, - // where we would error once on the parameter as a whole, and once on the binding `x`. - if param.pat.simple_ident().is_none() && !tcx.features().unsized_fn_params { - fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span)); - } - - fcx.write_ty(param.hir_id, param_ty); - } - - inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); - - fcx.in_tail_expr = true; - if let ty::Dynamic(..) = declared_ret_ty.kind() { - // FIXME: We need to verify that the return type is `Sized` after the return expression has - // been evaluated so that we have types available for all the nodes being returned, but that - // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this - // causes unsized errors caused by the `declared_ret_ty` to point at the return expression, - // while keeping the current ordering we will ignore the tail expression's type because we - // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr` - // because we will trigger "unreachable expression" lints unconditionally. - // Because of all of this, we perform a crude check to know whether the simplest `!Sized` - // case that a newcomer might make, returning a bare trait, and in that case we populate - // the tail expression's type so that the suggestion will be correct, but ignore all other - // possible cases. - fcx.check_expr(&body.value); - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - } else { - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - fcx.check_return_expr(&body.value, false); - } - fcx.in_tail_expr = false; - - // We insert the deferred_generator_interiors entry after visiting the body. - // This ensures that all nested generators appear before the entry of this generator. - // resolve_generator_interiors relies on this property. - let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) { - let interior = fcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }); - fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind)); - - let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap(); - Some(GeneratorTypes { - resume_ty, - yield_ty, - interior, - movability: can_be_generator.unwrap(), - }) - } else { - None - }; - - // Finalize the return check by taking the LUB of the return types - // we saw and assigning it to the expected return type. This isn't - // really expected to fail, since the coercions would have failed - // earlier when trying to find a LUB. - let coercion = fcx.ret_coercion.take().unwrap().into_inner(); - let mut actual_return_ty = coercion.complete(&fcx); - debug!("actual_return_ty = {:?}", actual_return_ty); - if let ty::Dynamic(..) = declared_ret_ty.kind() { - // We have special-cased the case where the function is declared - // `-> dyn Foo` and we don't actually relate it to the - // `fcx.ret_coercion`, so just substitute a type variable. - actual_return_ty = - fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::DynReturnFn, span }); - debug!("actual_return_ty replaced with {:?}", actual_return_ty); - } - - // HACK(oli-obk, compiler-errors): We should be comparing this against - // `declared_ret_ty`, but then anything uninferred would be inferred to - // the opaque type itself. That again would cause writeback to assume - // we have a recursive call site and do the sadly stabilized fallback to `()`. - fcx.demand_suptype(span, ret_ty, actual_return_ty); - - // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !` - if let Some(panic_impl_did) = tcx.lang_items().panic_impl() - && panic_impl_did == hir.local_def_id(fn_id).to_def_id() - { - check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty); - } - - // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !` - if let Some(alloc_error_handler_did) = tcx.lang_items().oom() - && alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id() - { - check_alloc_error_fn(tcx, alloc_error_handler_did.expect_local(), fn_sig, decl, declared_ret_ty); - } - - (fcx, gen_ty) -} - -fn check_panic_info_fn( - tcx: TyCtxt<'_>, - fn_id: LocalDefId, - fn_sig: ty::FnSig<'_>, - decl: &hir::FnDecl<'_>, - declared_ret_ty: Ty<'_>, -) { - let Some(panic_info_did) = tcx.lang_items().panic_info() else { - tcx.sess.err("language item required, but not found: `panic_info`"); - return; - }; - - if *declared_ret_ty.kind() != ty::Never { - tcx.sess.span_err(decl.output.span(), "return type should be `!`"); - } - - let inputs = fn_sig.inputs(); - if inputs.len() != 1 { - tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument"); - return; - } - - let arg_is_panic_info = match *inputs[0].kind() { - ty::Ref(region, ty, mutbl) => match *ty.kind() { - ty::Adt(ref adt, _) => { - adt.did() == panic_info_did && mutbl == hir::Mutability::Not && !region.is_static() - } - _ => false, - }, - _ => false, - }; - - if !arg_is_panic_info { - tcx.sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`"); - } - - let DefKind::Fn = tcx.def_kind(fn_id) else { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "should be a function"); - return; - }; - - let generic_counts = tcx.generics_of(fn_id).own_counts(); - if generic_counts.types != 0 { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "should have no type parameters"); - } - if generic_counts.consts != 0 { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "should have no const parameters"); - } -} - -fn check_alloc_error_fn( - tcx: TyCtxt<'_>, - fn_id: LocalDefId, - fn_sig: ty::FnSig<'_>, - decl: &hir::FnDecl<'_>, - declared_ret_ty: Ty<'_>, -) { - let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() else { - tcx.sess.err("language item required, but not found: `alloc_layout`"); - return; - }; - - if *declared_ret_ty.kind() != ty::Never { - tcx.sess.span_err(decl.output.span(), "return type should be `!`"); - } - - let inputs = fn_sig.inputs(); - if inputs.len() != 1 { - tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument"); - return; - } - - let arg_is_alloc_layout = match inputs[0].kind() { - ty::Adt(ref adt, _) => adt.did() == alloc_layout_did, - _ => false, - }; - - if !arg_is_alloc_layout { - tcx.sess.span_err(decl.inputs[0].span, "argument should be `Layout`"); - } - - let DefKind::Fn = tcx.def_kind(fn_id) else { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "`#[alloc_error_handler]` should be a function"); - return; - }; - - let generic_counts = tcx.generics_of(fn_id).own_counts(); - if generic_counts.types != 0 { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "`#[alloc_error_handler]` function should have no type parameters"); - } - if generic_counts.consts != 0 { - let span = tcx.def_span(fn_id); - tcx.sess - .span_err(span, "`#[alloc_error_handler]` function should have no const parameters"); - } -} - fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) { let def = tcx.adt_def(def_id); let span = tcx.def_span(def_id); def.destructor(tcx); // force the destructor to be evaluated - check_representable(tcx, span, def_id); if def.repr().simd() { check_simd(tcx, span, def_id); @@ -391,7 +83,6 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) { let def = tcx.adt_def(def_id); let span = tcx.def_span(def_id); def.destructor(tcx); // force the destructor to be evaluated - check_representable(tcx, span, def_id); check_transparent(tcx, span, def); check_union_fields(tcx, span, def_id); check_packed(tcx, span, def); @@ -423,7 +114,7 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b _ => { // Fallback case: allow `ManuallyDrop` and things that are `Copy`. ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop()) - || ty.is_copy_modulo_regions(tcx.at(span), param_env) + || ty.is_copy_modulo_regions(tcx, param_env) } } } @@ -510,10 +201,10 @@ fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { UNINHABITED_STATIC, tcx.hir().local_def_id_to_hir_id(def_id), span, + "static of uninhabited type", |lint| { - lint.build("static of uninhabited type") + lint .note("uninhabited statics cannot be initialized, and any access would be an immediate error") - .emit(); }, ); } @@ -521,23 +212,33 @@ fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { /// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` /// projections that would result in "inheriting lifetimes". -pub(super) fn check_opaque<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - substs: SubstsRef<'tcx>, - origin: &hir::OpaqueTyOrigin, -) { - let span = tcx.def_span(def_id); - check_opaque_for_inheriting_lifetimes(tcx, def_id, span); - if tcx.type_of(def_id).references_error() { +fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { + let item = tcx.hir().item(id); + let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else { + tcx.sess.delay_span_bug(tcx.hir().span(id.hir_id()), "expected opaque item"); + return; + }; + + // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting + // `async-std` (and `pub async fn` in general). + // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it! + // See https://github.com/rust-lang/rust/issues/75100 + if tcx.sess.opts.actually_rustdoc { + return; + } + + let substs = InternalSubsts::identity_for_item(tcx, item.owner_id.to_def_id()); + let span = tcx.def_span(item.owner_id.def_id); + + check_opaque_for_inheriting_lifetimes(tcx, item.owner_id.def_id, span); + if tcx.type_of(item.owner_id.def_id).references_error() { return; } - if check_opaque_for_cycles(tcx, def_id, substs, span, origin).is_err() { + if check_opaque_for_cycles(tcx, item.owner_id.def_id, substs, span, &origin).is_err() { return; } - check_opaque_meets_bounds(tcx, def_id, substs, span, origin); + check_opaque_meets_bounds(tcx, item.owner_id.def_id, substs, span, &origin); } - /// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result /// in "inheriting lifetimes". #[instrument(level = "debug", skip(tcx, span))] @@ -609,9 +310,12 @@ pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>( fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { match arg.kind { hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { - [PathSegment { res: Res::SelfTy { trait_: _, alias_to: impl_ref }, .. }] => { - let impl_ty_name = - impl_ref.map(|(def_id, _)| self.tcx.def_path_str(def_id)); + [PathSegment { res: Res::SelfTyParam { .. }, .. }] => { + let impl_ty_name = None; + self.selftys.push((path.span, impl_ty_name)); + } + [PathSegment { res: Res::SelfTyAlias { alias_to: def_id, .. }, .. }] => { + let impl_ty_name = Some(self.tcx.def_path_str(*def_id)); self.selftys.push((path.span, impl_ty_name)); } _ => {} @@ -701,10 +405,12 @@ pub(super) fn check_opaque_for_cycles<'tcx>( /// check those cases in the `param_env` of that function, which may have /// bounds not on this opaque type: /// -/// type X<T> = impl Clone +/// ```ignore (illustrative) +/// type X<T> = impl Clone; /// fn f<T: Clone>(t: T) -> X<T> { /// t /// } +/// ``` /// /// Without this check the above code is incorrectly accepted: we would ICE if /// some tried, for example, to clone an `Option<X<&mut ()>>`. @@ -716,8 +422,6 @@ fn check_opaque_meets_bounds<'tcx>( span: Span, origin: &hir::OpaqueTyOrigin, ) { - let hidden_type = tcx.bound_type_of(def_id.to_def_id()).subst(tcx, substs); - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let defining_use_anchor = match *origin { hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did, @@ -725,76 +429,88 @@ fn check_opaque_meets_bounds<'tcx>( }; let param_env = tcx.param_env(defining_use_anchor); - tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor)).enter( - move |infcx| { - let ocx = ObligationCtxt::new(&infcx); - let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); + let infcx = tcx + .infer_ctxt() + .with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor)) + .build(); + let ocx = ObligationCtxt::new(&infcx); + let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); + + // `ReErased` regions appear in the "parent_substs" of closures/generators. + // We're ignoring them here and replacing them with fresh region variables. + // See tests in ui/type-alias-impl-trait/closure_{parent_substs,wf_outlives}.rs. + // + // FIXME: Consider wrapping the hidden type in an existential `Binder` and instantiating it + // here rather than using ReErased. + let hidden_ty = tcx.bound_type_of(def_id.to_def_id()).subst(tcx, substs); + let hidden_ty = tcx.fold_regions(hidden_ty, |re, _dbi| match re.kind() { + ty::ReErased => infcx.next_region_var(RegionVariableOrigin::MiscVariable(span)), + _ => re, + }); - let misc_cause = traits::ObligationCause::misc(span, hir_id); + let misc_cause = traits::ObligationCause::misc(span, hir_id); - match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_type) { - Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok), - Err(ty_err) => { - tcx.sess.delay_span_bug( - span, - &format!("could not unify `{hidden_type}` with revealed type:\n{ty_err}"), - ); - } - } + match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_ty) { + Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok), + Err(ty_err) => { + tcx.sess.delay_span_bug( + span, + &format!("could not unify `{hidden_ty}` with revealed type:\n{ty_err}"), + ); + } + } - // Additionally require the hidden type to be well-formed with only the generics of the opaque type. - // Defining use functions may have more bounds than the opaque type, which is ok, as long as the - // hidden type is well formed even without those bounds. - let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_type.into())) - .to_predicate(tcx); - ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate)); - - // Check that all obligations are satisfied by the implementation's - // version. - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - infcx.report_fulfillment_errors(&errors, None, false); - } - match origin { - // Checked when type checking the function containing them. - hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {} - // Can have different predicates to their defining use - hir::OpaqueTyOrigin::TyAlias => { - let outlives_environment = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors( - defining_use_anchor, - &outlives_environment, - ); - } - } - // Clean up after ourselves - let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - }, - ); + // Additionally require the hidden type to be well-formed with only the generics of the opaque type. + // Defining use functions may have more bounds than the opaque type, which is ok, as long as the + // hidden type is well formed even without those bounds. + let predicate = + ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_ty.into())).to_predicate(tcx); + ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate)); + + // Check that all obligations are satisfied by the implementation's + // version. + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + } + match origin { + // Checked when type checking the function containing them. + hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {} + // Can have different predicates to their defining use + hir::OpaqueTyOrigin::TyAlias => { + let outlives_environment = OutlivesEnvironment::new(param_env); + infcx.check_region_obligations_and_report_errors( + defining_use_anchor, + &outlives_environment, + ); + } + } + // Clean up after ourselves + let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); } fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { debug!( "check_item_type(it.def_id={:?}, it.name={})", - id.def_id, - tcx.def_path_str(id.def_id.to_def_id()) + id.owner_id, + tcx.def_path_str(id.owner_id.to_def_id()) ); let _indenter = indenter(); - match tcx.def_kind(id.def_id) { + match tcx.def_kind(id.owner_id) { DefKind::Static(..) => { - tcx.ensure().typeck(id.def_id); - maybe_check_static_with_link_section(tcx, id.def_id); - check_static_inhabited(tcx, id.def_id); + tcx.ensure().typeck(id.owner_id.def_id); + maybe_check_static_with_link_section(tcx, id.owner_id.def_id); + check_static_inhabited(tcx, id.owner_id.def_id); } DefKind::Const => { - tcx.ensure().typeck(id.def_id); + tcx.ensure().typeck(id.owner_id.def_id); } DefKind::Enum => { let item = tcx.hir().item(id); let hir::ItemKind::Enum(ref enum_definition, _) = item.kind else { return; }; - check_enum(tcx, &enum_definition.variants, item.def_id); + check_enum(tcx, &enum_definition.variants, item.owner_id.def_id); } DefKind::Fn => {} // entirely within check_item_body DefKind::Impl => { @@ -802,12 +518,12 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { let hir::ItemKind::Impl(ref impl_) = it.kind else { return; }; - debug!("ItemKind::Impl {} with id {:?}", it.ident, it.def_id); - if let Some(impl_trait_ref) = tcx.impl_trait_ref(it.def_id) { + debug!("ItemKind::Impl {} with id {:?}", it.ident, it.owner_id); + if let Some(impl_trait_ref) = tcx.impl_trait_ref(it.owner_id) { check_impl_items_against_trait( tcx, it.span, - it.def_id, + it.owner_id.def_id, impl_trait_ref, &impl_.items, ); @@ -829,15 +545,15 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { fn_maybe_err(tcx, item.ident.span, abi); } hir::TraitItemKind::Type(.., Some(default)) => { - let assoc_item = tcx.associated_item(item.def_id); + let assoc_item = tcx.associated_item(item.owner_id); let trait_substs = - InternalSubsts::identity_for_item(tcx, it.def_id.to_def_id()); + InternalSubsts::identity_for_item(tcx, it.owner_id.to_def_id()); let _: Result<_, rustc_errors::ErrorGuaranteed> = check_type_bounds( tcx, assoc_item, assoc_item, default.span, - ty::TraitRef { def_id: it.def_id.to_def_id(), substs: trait_substs }, + ty::TraitRef { def_id: it.owner_id.to_def_id(), substs: trait_substs }, ); } _ => {} @@ -845,28 +561,28 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { } } DefKind::Struct => { - check_struct(tcx, id.def_id); + check_struct(tcx, id.owner_id.def_id); } DefKind::Union => { - check_union(tcx, id.def_id); + check_union(tcx, id.owner_id.def_id); } DefKind::OpaqueTy => { - let item = tcx.hir().item(id); - let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else { - return; - }; - // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting - // `async-std` (and `pub async fn` in general). - // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it! - // See https://github.com/rust-lang/rust/issues/75100 - if !tcx.sess.opts.actually_rustdoc { - let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id()); - check_opaque(tcx, item.def_id, substs, &origin); + check_opaque(tcx, id); + } + DefKind::ImplTraitPlaceholder => { + let parent = tcx.impl_trait_in_trait_parent(id.owner_id.to_def_id()); + // Only check the validity of this opaque type if the function has a default body + if let hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)), + .. + }) = tcx.hir().get_by_def_id(parent.expect_local()) + { + check_opaque(tcx, id); } } DefKind::TyAlias => { - let pty_ty = tcx.type_of(id.def_id); - let generics = tcx.generics_of(id.def_id); + let pty_ty = tcx.type_of(id.owner_id); + let generics = tcx.generics_of(id.owner_id); check_type_params_are_used(tcx, &generics, pty_ty); } DefKind::ForeignMod => { @@ -888,7 +604,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { } } else { for item in items { - let def_id = item.id.def_id; + let def_id = item.id.owner_id.def_id; let generics = tcx.generics_of(def_id); let own_counts = generics.own_counts(); if generics.params.len() - own_counts.lifetimes != 0 { @@ -943,7 +659,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) { pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { // an error would be reported if this fails. - let _ = traits::OnUnimplementedDirective::of_item(tcx, item.def_id.to_def_id()); + let _ = traits::OnUnimplementedDirective::of_item(tcx, item.owner_id.to_def_id()); } pub(super) fn check_specialization_validity<'tcx>( @@ -1030,7 +746,7 @@ fn check_impl_items_against_trait<'tcx>( let trait_def = tcx.trait_def(impl_trait_ref.def_id); for impl_item in impl_item_refs { - let ty_impl_item = tcx.associated_item(impl_item.id.def_id); + let ty_impl_item = tcx.associated_item(impl_item.id.owner_id); let ty_trait_item = if let Some(trait_item_id) = ty_impl_item.trait_item_def_id { tcx.associated_item(trait_item_id) } else { @@ -1041,14 +757,10 @@ fn check_impl_items_against_trait<'tcx>( let impl_item_full = tcx.hir().impl_item(impl_item.id); match impl_item_full.kind { hir::ImplItemKind::Const(..) => { - // Find associated const definition. - compare_const_impl( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - ); + let _ = tcx.compare_assoc_const_impl_item_with_trait_item(( + impl_item.id.owner_id.def_id, + ty_impl_item.trait_item_def_id.unwrap(), + )); } hir::ImplItemKind::Fn(..) => { let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); @@ -1060,7 +772,7 @@ fn check_impl_items_against_trait<'tcx>( opt_trait_span, ); } - hir::ImplItemKind::TyAlias(impl_ty) => { + hir::ImplItemKind::Type(impl_ty) => { let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id); compare_ty_impl( tcx, @@ -1148,27 +860,6 @@ fn check_impl_items_against_trait<'tcx>( } } -/// Checks whether a type can be represented in memory. In particular, it -/// identifies types that contain themselves without indirection through a -/// pointer, which would mean their size is unbounded. -pub(super) fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool { - let rty = tcx.type_of(item_def_id); - - // Check that it is possible to represent this type. This call identifies - // (1) types that contain themselves and (2) types that contain a different - // recursive type. It is only necessary to throw an error on those that - // contain themselves. For case 2, there must be an inner type that will be - // caught by case 1. - match representability::ty_is_representable(tcx, rty, sp, None) { - Representability::SelfRecursive(spans) => { - recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans); - return false; - } - Representability::Representable | Representability::ContainsRecursive => (), - } - true -} - pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { let t = tcx.type_of(def_id); if let ty::Adt(def, substs) = t.kind() @@ -1434,6 +1125,7 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()), span, + "zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types", |lint| { let note = if non_exhaustive { "is marked with `#[non_exhaustive]`" @@ -1441,10 +1133,9 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtD "contains private fields" }; let field_ty = tcx.def_path_str_with_substs(def_id, substs); - lint.build("zero-sized fields in repr(transparent) cannot contain external non-exhaustive types") + lint .note(format!("this {descr} contains `{field_ty}`, which {note}, \ and makes it not a breaking change to become non-zero-sized in the future.")) - .emit(); }, ) } @@ -1489,7 +1180,7 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L } } - if tcx.adt_def(def_id).repr().int.is_none() && tcx.features().arbitrary_enum_discriminant { + if tcx.adt_def(def_id).repr().int.is_none() { let is_unit = |var: &hir::Variant<'_>| matches!(var.data, hir::VariantData::Unit(..)); let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some(); @@ -1506,7 +1197,6 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp); - check_representable(tcx, sp, def_id); check_transparent(tcx, sp, def); } diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs index 59d591acd..32f66b06f 100644 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs @@ -1,6 +1,6 @@ use super::potentially_plural_count; use crate::errors::LifetimesOrBoundsMismatchOnTrait; -use hir::def_id::DefId; +use hir::def_id::{DefId, LocalDefId}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed}; use rustc_hir as hir; @@ -12,14 +12,14 @@ use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKi use rustc_infer::infer::{self, TyCtxtInferExt}; use rustc_infer::traits::util; use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; use rustc_middle::ty::util::ExplicitSelf; +use rustc_middle::ty::InternalSubsts; use rustc_middle::ty::{ self, AssocItem, DefIdTree, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, }; use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt}; use rustc_span::Span; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{ self, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal, @@ -215,224 +215,220 @@ fn compare_predicate_entailment<'tcx>( ); let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); - tcx.infer_ctxt().enter(|ref infcx| { - let ocx = ObligationCtxt::new(infcx); + let infcx = &tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(infcx); - debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds()); + debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds()); - let mut selcx = traits::SelectionContext::new(&infcx); - let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs); - for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) { - let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id); - let traits::Normalized { value: predicate, obligations } = - traits::normalize(&mut selcx, param_env, normalize_cause, predicate); + let mut selcx = traits::SelectionContext::new(&infcx); + let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs); + for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) { + let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id); + let traits::Normalized { value: predicate, obligations } = + traits::normalize(&mut selcx, param_env, normalize_cause, predicate); - ocx.register_obligations(obligations); - let cause = ObligationCause::new( - span, - impl_m_hir_id, - ObligationCauseCode::CompareImplItemObligation { - impl_item_def_id: impl_m.def_id.expect_local(), - trait_item_def_id: trait_m.def_id, - kind: impl_m.kind, - }, - ); - ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate)); - } - - // We now need to check that the signature of the impl method is - // compatible with that of the trait method. We do this by - // checking that `impl_fty <: trait_fty`. - // - // FIXME. Unfortunately, this doesn't quite work right now because - // associated type normalization is not integrated into subtype - // checks. For the comparison to be valid, we need to - // normalize the associated types in the impl/trait methods - // first. However, because function types bind regions, just - // calling `normalize_associated_types_in` would have no effect on - // any associated types appearing in the fn arguments or return - // type. - - // Compute placeholder form of impl and trait method tys. - let tcx = infcx.tcx; - - let mut wf_tys = FxHashSet::default(); - - let impl_sig = infcx.replace_bound_vars_with_fresh_vars( - impl_m_span, - infer::HigherRankedType, - tcx.fn_sig(impl_m.def_id), + ocx.register_obligations(obligations); + let cause = ObligationCause::new( + span, + impl_m_hir_id, + ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_m.def_id.expect_local(), + trait_item_def_id: trait_m.def_id, + kind: impl_m.kind, + }, ); + ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate)); + } - let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id); - let impl_sig = ocx.normalize(norm_cause.clone(), param_env, impl_sig); - let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)); - debug!("compare_impl_method: impl_fty={:?}", impl_fty); - - let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs); - let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig); - - // Next, add all inputs and output as well-formed tys. Importantly, - // we have to do this before normalization, since the normalized ty may - // not contain the input parameters. See issue #87748. - wf_tys.extend(trait_sig.inputs_and_output.iter()); - let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig); - // We also have to add the normalized trait signature - // as we don't normalize during implied bounds computation. - wf_tys.extend(trait_sig.inputs_and_output.iter()); - let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)); - - debug!("compare_impl_method: trait_fty={:?}", trait_fty); - - // FIXME: We'd want to keep more accurate spans than "the method signature" when - // processing the comparison between the trait and impl fn, but we sadly lose them - // and point at the whole signature when a trait bound or specific input or output - // type would be more appropriate. In other places we have a `Vec<Span>` - // corresponding to their `Vec<Predicate>`, but we don't have that here. - // Fixing this would improve the output of test `issue-83765.rs`. - let mut result = infcx - .at(&cause, param_env) - .sup(trait_fty, impl_fty) - .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok)); - - // HACK(RPITIT): #101614. When we are trying to infer the hidden types for - // RPITITs, we need to equate the output tys instead of just subtyping. If - // we just use `sup` above, we'll end up `&'static str <: _#1t`, which causes - // us to infer `_#1t = #'_#2r str`, where `'_#2r` is unconstrained, which gets - // fixed up to `ReEmpty`, and which is certainly not what we want. - if trait_fty.has_infer_types() { - result = result.and_then(|()| { - infcx - .at(&cause, param_env) - .eq(trait_sig.output(), impl_sig.output()) - .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok)) - }); - } + // We now need to check that the signature of the impl method is + // compatible with that of the trait method. We do this by + // checking that `impl_fty <: trait_fty`. + // + // FIXME. Unfortunately, this doesn't quite work right now because + // associated type normalization is not integrated into subtype + // checks. For the comparison to be valid, we need to + // normalize the associated types in the impl/trait methods + // first. However, because function types bind regions, just + // calling `normalize_associated_types_in` would have no effect on + // any associated types appearing in the fn arguments or return + // type. + + // Compute placeholder form of impl and trait method tys. + let tcx = infcx.tcx; - if let Err(terr) = result { - debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); + let mut wf_tys = FxHashSet::default(); - let (impl_err_span, trait_err_span) = - extract_spans_for_error_reporting(&infcx, terr, &cause, impl_m, trait_m); + let impl_sig = infcx.replace_bound_vars_with_fresh_vars( + impl_m_span, + infer::HigherRankedType, + tcx.fn_sig(impl_m.def_id), + ); - cause.span = impl_err_span; + let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id); + let impl_sig = ocx.normalize(norm_cause.clone(), param_env, impl_sig); + let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)); + debug!("compare_impl_method: impl_fty={:?}", impl_fty); + + let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs); + let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig); + + // Next, add all inputs and output as well-formed tys. Importantly, + // we have to do this before normalization, since the normalized ty may + // not contain the input parameters. See issue #87748. + wf_tys.extend(trait_sig.inputs_and_output.iter()); + let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig); + // We also have to add the normalized trait signature + // as we don't normalize during implied bounds computation. + wf_tys.extend(trait_sig.inputs_and_output.iter()); + let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)); + + debug!("compare_impl_method: trait_fty={:?}", trait_fty); + + // FIXME: We'd want to keep more accurate spans than "the method signature" when + // processing the comparison between the trait and impl fn, but we sadly lose them + // and point at the whole signature when a trait bound or specific input or output + // type would be more appropriate. In other places we have a `Vec<Span>` + // corresponding to their `Vec<Predicate>`, but we don't have that here. + // Fixing this would improve the output of test `issue-83765.rs`. + let mut result = infcx + .at(&cause, param_env) + .sup(trait_fty, impl_fty) + .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok)); + + // HACK(RPITIT): #101614. When we are trying to infer the hidden types for + // RPITITs, we need to equate the output tys instead of just subtyping. If + // we just use `sup` above, we'll end up `&'static str <: _#1t`, which causes + // us to infer `_#1t = #'_#2r str`, where `'_#2r` is unconstrained, which gets + // fixed up to `ReEmpty`, and which is certainly not what we want. + if trait_fty.has_infer_types() { + result = result.and_then(|()| { + infcx + .at(&cause, param_env) + .eq(trait_sig.output(), impl_sig.output()) + .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok)) + }); + } - let mut diag = struct_span_err!( - tcx.sess, - cause.span(), - E0053, - "method `{}` has an incompatible type for trait", - trait_m.name - ); - match &terr { - TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0) - if trait_m.fn_has_self_parameter => - { - let ty = trait_sig.inputs()[0]; - let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) - { - ExplicitSelf::ByValue => "self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Mut) => { - "&mut self".to_owned() - } - _ => format!("self: {ty}"), - }; + if let Err(terr) = result { + debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty); - // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the - // span points only at the type `Box<Self`>, but we want to cover the whole - // argument pattern and type. - let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { - ImplItemKind::Fn(ref sig, body) => tcx - .hir() - .body_param_names(body) - .zip(sig.decl.inputs.iter()) - .map(|(param, ty)| param.span.to(ty.span)) - .next() - .unwrap_or(impl_err_span), - _ => bug!("{:?} is not a method", impl_m), - }; + let (impl_err_span, trait_err_span) = + extract_spans_for_error_reporting(&infcx, terr, &cause, impl_m, trait_m); + cause.span = impl_err_span; + + let mut diag = struct_span_err!( + tcx.sess, + cause.span(), + E0053, + "method `{}` has an incompatible type for trait", + trait_m.name + ); + match &terr { + TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0) + if trait_m.fn_has_self_parameter => + { + let ty = trait_sig.inputs()[0]; + let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) { + ExplicitSelf::ByValue => "self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(), + _ => format!("self: {ty}"), + }; + + // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the + // span points only at the type `Box<Self`>, but we want to cover the whole + // argument pattern and type. + let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { + ImplItemKind::Fn(ref sig, body) => tcx + .hir() + .body_param_names(body) + .zip(sig.decl.inputs.iter()) + .map(|(param, ty)| param.span.to(ty.span)) + .next() + .unwrap_or(impl_err_span), + _ => bug!("{:?} is not a method", impl_m), + }; + + diag.span_suggestion( + span, + "change the self-receiver type to match the trait", + sugg, + Applicability::MachineApplicable, + ); + } + TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => { + if trait_sig.inputs().len() == *i { + // Suggestion to change output type. We do not suggest in `async` functions + // to avoid complex logic or incorrect output. + match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { + ImplItemKind::Fn(ref sig, _) + if sig.header.asyncness == hir::IsAsync::NotAsync => + { + let msg = "change the output type to match the trait"; + let ap = Applicability::MachineApplicable; + match sig.decl.output { + hir::FnRetTy::DefaultReturn(sp) => { + let sugg = format!("-> {} ", trait_sig.output()); + diag.span_suggestion_verbose(sp, msg, sugg, ap); + } + hir::FnRetTy::Return(hir_ty) => { + let sugg = trait_sig.output(); + diag.span_suggestion(hir_ty.span, msg, sugg, ap); + } + }; + } + _ => {} + }; + } else if let Some(trait_ty) = trait_sig.inputs().get(*i) { diag.span_suggestion( - span, - "change the self-receiver type to match the trait", - sugg, + impl_err_span, + "change the parameter type to match the trait", + trait_ty, Applicability::MachineApplicable, ); } - TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => { - if trait_sig.inputs().len() == *i { - // Suggestion to change output type. We do not suggest in `async` functions - // to avoid complex logic or incorrect output. - match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind { - ImplItemKind::Fn(ref sig, _) - if sig.header.asyncness == hir::IsAsync::NotAsync => - { - let msg = "change the output type to match the trait"; - let ap = Applicability::MachineApplicable; - match sig.decl.output { - hir::FnRetTy::DefaultReturn(sp) => { - let sugg = format!("-> {} ", trait_sig.output()); - diag.span_suggestion_verbose(sp, msg, sugg, ap); - } - hir::FnRetTy::Return(hir_ty) => { - let sugg = trait_sig.output(); - diag.span_suggestion(hir_ty.span, msg, sugg, ap); - } - }; - } - _ => {} - }; - } else if let Some(trait_ty) = trait_sig.inputs().get(*i) { - diag.span_suggestion( - impl_err_span, - "change the parameter type to match the trait", - trait_ty, - Applicability::MachineApplicable, - ); - } - } - _ => {} } + _ => {} + } - infcx.note_type_err( - &mut diag, - &cause, - trait_err_span.map(|sp| (sp, "type in trait".to_owned())), - Some(infer::ValuePairs::Terms(ExpectedFound { - expected: trait_fty.into(), - found: impl_fty.into(), - })), - terr, - false, - false, - ); + infcx.err_ctxt().note_type_err( + &mut diag, + &cause, + trait_err_span.map(|sp| (sp, "type in trait".to_owned())), + Some(infer::ValuePairs::Terms(ExpectedFound { + expected: trait_fty.into(), + found: impl_fty.into(), + })), + terr, + false, + false, + ); - return Err(diag.emit()); - } + return Err(diag.emit()); + } - // Check that all obligations are satisfied by the implementation's - // version. - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - let reported = infcx.report_fulfillment_errors(&errors, None, false); - return Err(reported); - } + // Check that all obligations are satisfied by the implementation's + // version. + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + return Err(reported); + } - // Finally, resolve all regions. This catches wily misuses of - // lifetime parameters. - let outlives_environment = OutlivesEnvironment::with_bounds( - param_env, - Some(infcx), - infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys), - ); - infcx.check_region_obligations_and_report_errors( - impl_m.def_id.expect_local(), - &outlives_environment, - ); + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let outlives_environment = OutlivesEnvironment::with_bounds( + param_env, + Some(infcx), + infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys), + ); + infcx.check_region_obligations_and_report_errors( + impl_m.def_id.expect_local(), + &outlives_environment, + ); - Ok(()) - }) + Ok(()) } pub fn collect_trait_impl_trait_tys<'tcx>( @@ -465,125 +461,173 @@ pub fn collect_trait_impl_trait_tys<'tcx>( let trait_to_placeholder_substs = impl_to_placeholder_substs.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_substs); - tcx.infer_ctxt().enter(|ref infcx| { - let ocx = ObligationCtxt::new(infcx); + let infcx = &tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(infcx); - let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id); - let impl_return_ty = ocx.normalize( - norm_cause.clone(), - param_env, - infcx - .replace_bound_vars_with_fresh_vars( - return_span, - infer::HigherRankedType, - tcx.fn_sig(impl_m.def_id), - ) - .output(), - ); + let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id); + let impl_sig = ocx.normalize( + norm_cause.clone(), + param_env, + infcx.replace_bound_vars_with_fresh_vars( + return_span, + infer::HigherRankedType, + tcx.fn_sig(impl_m.def_id), + ), + ); + let impl_return_ty = impl_sig.output(); - let mut collector = - ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_hir_id); - let unnormalized_trait_return_ty = tcx - .liberate_late_bound_regions( - impl_m.def_id, - tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs), - ) - .output() - .fold_with(&mut collector); - let trait_return_ty = - ocx.normalize(norm_cause.clone(), param_env, unnormalized_trait_return_ty); + let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_hir_id); + let unnormalized_trait_sig = tcx + .liberate_late_bound_regions( + impl_m.def_id, + tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs), + ) + .fold_with(&mut collector); + let trait_sig = ocx.normalize(norm_cause.clone(), param_env, unnormalized_trait_sig); + let trait_return_ty = trait_sig.output(); - let wf_tys = FxHashSet::from_iter([unnormalized_trait_return_ty, trait_return_ty]); + let wf_tys = FxHashSet::from_iter( + unnormalized_trait_sig.inputs_and_output.iter().chain(trait_sig.inputs_and_output.iter()), + ); - match infcx.at(&cause, param_env).eq(trait_return_ty, impl_return_ty) { - Ok(infer::InferOk { value: (), obligations }) => { - ocx.register_obligations(obligations); - } - Err(terr) => { - let mut diag = struct_span_err!( - tcx.sess, - cause.span(), - E0053, - "method `{}` has an incompatible return type for trait", - trait_m.name - ); - let hir = tcx.hir(); - infcx.note_type_err( - &mut diag, - &cause, - hir.get_if_local(impl_m.def_id) - .and_then(|node| node.fn_decl()) - .map(|decl| (decl.output.span(), "return type in trait".to_owned())), - Some(infer::ValuePairs::Terms(ExpectedFound { - expected: trait_return_ty.into(), - found: impl_return_ty.into(), - })), - terr, - false, - false, - ); - return Err(diag.emit()); - } + match infcx.at(&cause, param_env).eq(trait_return_ty, impl_return_ty) { + Ok(infer::InferOk { value: (), obligations }) => { + ocx.register_obligations(obligations); } + Err(terr) => { + let mut diag = struct_span_err!( + tcx.sess, + cause.span(), + E0053, + "method `{}` has an incompatible return type for trait", + trait_m.name + ); + let hir = tcx.hir(); + infcx.err_ctxt().note_type_err( + &mut diag, + &cause, + hir.get_if_local(impl_m.def_id) + .and_then(|node| node.fn_decl()) + .map(|decl| (decl.output.span(), "return type in trait".to_owned())), + Some(infer::ValuePairs::Terms(ExpectedFound { + expected: trait_return_ty.into(), + found: impl_return_ty.into(), + })), + terr, + false, + false, + ); + return Err(diag.emit()); + } + } - // Check that all obligations are satisfied by the implementation's - // RPITs. - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - let reported = infcx.report_fulfillment_errors(&errors, None, false); - return Err(reported); + // Unify the whole function signature. We need to do this to fully infer + // the lifetimes of the return type, but do this after unifying just the + // return types, since we want to avoid duplicating errors from + // `compare_predicate_entailment`. + match infcx + .at(&cause, param_env) + .eq(tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)), tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig))) + { + Ok(infer::InferOk { value: (), obligations }) => { + ocx.register_obligations(obligations); } + Err(terr) => { + let guar = tcx.sess.delay_span_bug( + return_span, + format!("could not unify `{trait_sig}` and `{impl_sig}`: {terr:?}"), + ); + return Err(guar); + } + } - // Finally, resolve all regions. This catches wily misuses of - // lifetime parameters. - let outlives_environment = OutlivesEnvironment::with_bounds( - param_env, - Some(infcx), - infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys), - ); - infcx.check_region_obligations_and_report_errors( - impl_m.def_id.expect_local(), - &outlives_environment, - ); + // Check that all obligations are satisfied by the implementation's + // RPITs. + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + return Err(reported); + } - let mut collected_tys = FxHashMap::default(); - for (def_id, (ty, substs)) in collector.types { - match infcx.fully_resolve(ty) { - Ok(ty) => { - // `ty` contains free regions that we created earlier while liberating the - // trait fn signature. However, projection normalization expects `ty` to - // contains `def_id`'s early-bound regions. - let id_substs = InternalSubsts::identity_for_item(tcx, def_id); - debug!(?id_substs, ?substs); - let map: FxHashMap<ty::GenericArg<'tcx>, ty::GenericArg<'tcx>> = substs - .iter() - .enumerate() - .map(|(index, arg)| (arg, id_substs[index])) - .collect(); - debug!(?map); - - let ty = tcx.fold_regions(ty, |region, _| { - if let ty::ReFree(_) = region.kind() { - map[®ion.into()].expect_region() - } else { - region - } - }); - debug!(%ty); - collected_tys.insert(def_id, ty); - } - Err(err) => { - tcx.sess.delay_span_bug( - return_span, - format!("could not fully resolve: {ty} => {err:?}"), - ); - collected_tys.insert(def_id, tcx.ty_error()); - } + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let outlives_environment = OutlivesEnvironment::with_bounds( + param_env, + Some(infcx), + infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys), + ); + infcx.check_region_obligations_and_report_errors( + impl_m.def_id.expect_local(), + &outlives_environment, + ); + + let mut collected_tys = FxHashMap::default(); + for (def_id, (ty, substs)) in collector.types { + match infcx.fully_resolve(ty) { + Ok(ty) => { + // `ty` contains free regions that we created earlier while liberating the + // trait fn signature. However, projection normalization expects `ty` to + // contains `def_id`'s early-bound regions. + let id_substs = InternalSubsts::identity_for_item(tcx, def_id); + debug!(?id_substs, ?substs); + let map: FxHashMap<ty::GenericArg<'tcx>, ty::GenericArg<'tcx>> = + std::iter::zip(substs, id_substs).collect(); + debug!(?map); + + // NOTE(compiler-errors): RPITITs, like all other RPITs, have early-bound + // region substs that are synthesized during AST lowering. These are substs + // that are appended to the parent substs (trait and trait method). However, + // we're trying to infer the unsubstituted type value of the RPITIT inside + // the *impl*, so we can later use the impl's method substs to normalize + // an RPITIT to a concrete type (`confirm_impl_trait_in_trait_candidate`). + // + // Due to the design of RPITITs, during AST lowering, we have no idea that + // an impl method corresponds to a trait method with RPITITs in it. Therefore, + // we don't have a list of early-bound region substs for the RPITIT in the impl. + // Since early region parameters are index-based, we can't just rebase these + // (trait method) early-bound region substs onto the impl, and there's no + // guarantee that the indices from the trait substs and impl substs line up. + // So to fix this, we subtract the number of trait substs and add the number of + // impl substs to *renumber* these early-bound regions to their corresponding + // indices in the impl's substitutions list. + // + // Also, we only need to account for a difference in trait and impl substs, + // since we previously enforce that the trait method and impl method have the + // same generics. + let num_trait_substs = trait_to_impl_substs.len(); + let num_impl_substs = tcx.generics_of(impl_m.container_id(tcx)).params.len(); + let ty = tcx.fold_regions(ty, |region, _| { + let (ty::ReFree(_) | ty::ReEarlyBound(_)) = region.kind() else { return region; }; + let Some(ty::ReEarlyBound(e)) = map.get(®ion.into()).map(|r| r.expect_region().kind()) + else { + tcx + .sess + .delay_span_bug( + return_span, + "expected ReFree to map to ReEarlyBound" + ); + return tcx.lifetimes.re_static; + }; + tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { + def_id: e.def_id, + name: e.name, + index: (e.index as usize - num_trait_substs + num_impl_substs) as u32, + })) + }); + debug!(%ty); + collected_tys.insert(def_id, ty); + } + Err(err) => { + tcx.sess.delay_span_bug( + return_span, + format!("could not fully resolve: {ty} => {err:?}"), + ); + collected_tys.insert(def_id, tcx.ty_error()); } } + } - Ok(&*tcx.arena.alloc(collected_tys)) - }) + Ok(&*tcx.arena.alloc(collected_tys)) } struct ImplTraitInTraitCollector<'a, 'tcx> { @@ -628,10 +672,7 @@ impl<'tcx> TypeFolder<'tcx> for ImplTraitInTraitCollector<'_, 'tcx> { }); self.types.insert(proj.item_def_id, (infer_ty, proj.substs)); // Recurse into bounds - for pred in self.tcx().bound_explicit_item_bounds(proj.item_def_id).transpose_iter() { - let pred_span = pred.0.1; - - let pred = pred.map_bound(|(pred, _)| *pred).subst(self.tcx(), proj.substs); + for (pred, pred_span) in self.tcx().bound_explicit_item_bounds(proj.item_def_id).subst_iter_copied(self.tcx(), proj.substs) { let pred = pred.fold_with(self); let pred = self.ocx.normalize( ObligationCause::misc(self.span, self.body_id), @@ -712,8 +753,8 @@ fn check_region_bounds_on_impl_item<'tcx>( } #[instrument(level = "debug", skip(infcx))] -fn extract_spans_for_error_reporting<'a, 'tcx>( - infcx: &infer::InferCtxt<'a, 'tcx>, +fn extract_spans_for_error_reporting<'tcx>( + infcx: &infer::InferCtxt<'tcx>, terr: TypeError<'_>, cause: &ObligationCause<'tcx>, impl_m: &ty::AssocItem, @@ -768,16 +809,15 @@ fn compare_self_type<'tcx>( let self_arg_ty = tcx.fn_sig(method.def_id).input(0); let param_env = ty::ParamEnv::reveal_all(); - tcx.infer_ctxt().enter(|infcx| { - let self_arg_ty = tcx.liberate_late_bound_regions(method.def_id, self_arg_ty); - let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty).is_ok(); - match ExplicitSelf::determine(self_arg_ty, can_eq_self) { - ExplicitSelf::ByValue => "self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), - ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(), - _ => format!("self: {self_arg_ty}"), - } - }) + let infcx = tcx.infer_ctxt().build(); + let self_arg_ty = tcx.liberate_late_bound_regions(method.def_id, self_arg_ty); + let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty).is_ok(); + match ExplicitSelf::determine(self_arg_ty, can_eq_self) { + ExplicitSelf::ByValue => "self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(), + ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(), + _ => format!("self: {self_arg_ty}"), + } }; match (trait_m.fn_has_self_parameter, impl_m.fn_has_self_parameter) { @@ -1300,114 +1340,114 @@ fn compare_generic_param_kinds<'tcx>( Ok(()) } -pub(crate) fn compare_const_impl<'tcx>( +/// Use `tcx.compare_assoc_const_impl_item_with_trait_item` instead +pub(crate) fn raw_compare_const_impl<'tcx>( tcx: TyCtxt<'tcx>, - impl_c: &ty::AssocItem, - impl_c_span: Span, - trait_c: &ty::AssocItem, - impl_trait_ref: ty::TraitRef<'tcx>, -) { + (impl_const_item_def, trait_const_item_def): (LocalDefId, DefId), +) -> Result<(), ErrorGuaranteed> { + let impl_const_item = tcx.associated_item(impl_const_item_def); + let trait_const_item = tcx.associated_item(trait_const_item_def); + let impl_trait_ref = tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap(); debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref); - tcx.infer_ctxt().enter(|infcx| { - let param_env = tcx.param_env(impl_c.def_id); - let ocx = ObligationCtxt::new(&infcx); - - // The below is for the most part highly similar to the procedure - // for methods above. It is simpler in many respects, especially - // because we shouldn't really have to deal with lifetimes or - // predicates. In fact some of this should probably be put into - // shared functions because of DRY violations... - let trait_to_impl_substs = impl_trait_ref.substs; - - // Create a parameter environment that represents the implementation's - // method. - let impl_c_hir_id = tcx.hir().local_def_id_to_hir_id(impl_c.def_id.expect_local()); - - // Compute placeholder form of impl and trait const tys. - let impl_ty = tcx.type_of(impl_c.def_id); - let trait_ty = tcx.bound_type_of(trait_c.def_id).subst(tcx, trait_to_impl_substs); - let mut cause = ObligationCause::new( - impl_c_span, - impl_c_hir_id, - ObligationCauseCode::CompareImplItemObligation { - impl_item_def_id: impl_c.def_id.expect_local(), - trait_item_def_id: trait_c.def_id, - kind: impl_c.kind, - }, - ); + let impl_c_span = tcx.def_span(impl_const_item_def.to_def_id()); - // There is no "body" here, so just pass dummy id. - let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty); + let infcx = tcx.infer_ctxt().build(); + let param_env = tcx.param_env(impl_const_item_def.to_def_id()); + let ocx = ObligationCtxt::new(&infcx); - debug!("compare_const_impl: impl_ty={:?}", impl_ty); + // The below is for the most part highly similar to the procedure + // for methods above. It is simpler in many respects, especially + // because we shouldn't really have to deal with lifetimes or + // predicates. In fact some of this should probably be put into + // shared functions because of DRY violations... + let trait_to_impl_substs = impl_trait_ref.substs; - let trait_ty = ocx.normalize(cause.clone(), param_env, trait_ty); + // Create a parameter environment that represents the implementation's + // method. + let impl_c_hir_id = tcx.hir().local_def_id_to_hir_id(impl_const_item_def); - debug!("compare_const_impl: trait_ty={:?}", trait_ty); + // Compute placeholder form of impl and trait const tys. + let impl_ty = tcx.type_of(impl_const_item_def.to_def_id()); + let trait_ty = tcx.bound_type_of(trait_const_item_def).subst(tcx, trait_to_impl_substs); + let mut cause = ObligationCause::new( + impl_c_span, + impl_c_hir_id, + ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_const_item_def, + trait_item_def_id: trait_const_item_def, + kind: impl_const_item.kind, + }, + ); - let err = infcx - .at(&cause, param_env) - .sup(trait_ty, impl_ty) - .map(|ok| ocx.register_infer_ok_obligations(ok)); + // There is no "body" here, so just pass dummy id. + let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty); - if let Err(terr) = err { - debug!( - "checking associated const for compatibility: impl ty {:?}, trait ty {:?}", - impl_ty, trait_ty - ); + debug!("compare_const_impl: impl_ty={:?}", impl_ty); - // Locate the Span containing just the type of the offending impl - match tcx.hir().expect_impl_item(impl_c.def_id.expect_local()).kind { - ImplItemKind::Const(ref ty, _) => cause.span = ty.span, - _ => bug!("{:?} is not a impl const", impl_c), - } + let trait_ty = ocx.normalize(cause.clone(), param_env, trait_ty); - let mut diag = struct_span_err!( - tcx.sess, - cause.span, - E0326, - "implemented const `{}` has an incompatible type for trait", - trait_c.name - ); + debug!("compare_const_impl: trait_ty={:?}", trait_ty); - let trait_c_span = trait_c.def_id.as_local().map(|trait_c_def_id| { - // Add a label to the Span containing just the type of the const - match tcx.hir().expect_trait_item(trait_c_def_id).kind { - TraitItemKind::Const(ref ty, _) => ty.span, - _ => bug!("{:?} is not a trait const", trait_c), - } - }); + let err = infcx + .at(&cause, param_env) + .sup(trait_ty, impl_ty) + .map(|ok| ocx.register_infer_ok_obligations(ok)); - infcx.note_type_err( - &mut diag, - &cause, - trait_c_span.map(|span| (span, "type in trait".to_owned())), - Some(infer::ValuePairs::Terms(ExpectedFound { - expected: trait_ty.into(), - found: impl_ty.into(), - })), - terr, - false, - false, - ); - diag.emit(); - } + if let Err(terr) = err { + debug!( + "checking associated const for compatibility: impl ty {:?}, trait ty {:?}", + impl_ty, trait_ty + ); - // Check that all obligations are satisfied by the implementation's - // version. - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - infcx.report_fulfillment_errors(&errors, None, false); - return; + // Locate the Span containing just the type of the offending impl + match tcx.hir().expect_impl_item(impl_const_item_def).kind { + ImplItemKind::Const(ref ty, _) => cause.span = ty.span, + _ => bug!("{:?} is not a impl const", impl_const_item), } - let outlives_environment = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors( - impl_c.def_id.expect_local(), - &outlives_environment, + let mut diag = struct_span_err!( + tcx.sess, + cause.span, + E0326, + "implemented const `{}` has an incompatible type for trait", + trait_const_item.name ); - }); + + let trait_c_span = trait_const_item_def.as_local().map(|trait_c_def_id| { + // Add a label to the Span containing just the type of the const + match tcx.hir().expect_trait_item(trait_c_def_id).kind { + TraitItemKind::Const(ref ty, _) => ty.span, + _ => bug!("{:?} is not a trait const", trait_const_item), + } + }); + + infcx.err_ctxt().note_type_err( + &mut diag, + &cause, + trait_c_span.map(|span| (span, "type in trait".to_owned())), + Some(infer::ValuePairs::Terms(ExpectedFound { + expected: trait_ty.into(), + found: impl_ty.into(), + })), + terr, + false, + false, + ); + return Err(diag.emit()); + }; + + // Check that all obligations are satisfied by the implementation's + // version. + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None, false)); + } + + // FIXME return `ErrorReported` if region obligations error? + let outlives_environment = OutlivesEnvironment::new(param_env); + infcx.check_region_obligations_and_report_errors(impl_const_item_def, &outlives_environment); + Ok(()) } pub(crate) fn compare_ty_impl<'tcx>( @@ -1488,52 +1528,50 @@ fn compare_type_predicate_entailment<'tcx>( hir::Constness::NotConst, ); let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause); - tcx.infer_ctxt().enter(|infcx| { - let ocx = ObligationCtxt::new(&infcx); + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); - debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds()); + debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds()); - let mut selcx = traits::SelectionContext::new(&infcx); - - assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len()); - for (span, predicate) in - std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates) - { - let cause = ObligationCause::misc(span, impl_ty_hir_id); - let traits::Normalized { value: predicate, obligations } = - traits::normalize(&mut selcx, param_env, cause, predicate); - - let cause = ObligationCause::new( - span, - impl_ty_hir_id, - ObligationCauseCode::CompareImplItemObligation { - impl_item_def_id: impl_ty.def_id.expect_local(), - trait_item_def_id: trait_ty.def_id, - kind: impl_ty.kind, - }, - ); - ocx.register_obligations(obligations); - ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate)); - } + let mut selcx = traits::SelectionContext::new(&infcx); - // Check that all obligations are satisfied by the implementation's - // version. - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - let reported = infcx.report_fulfillment_errors(&errors, None, false); - return Err(reported); - } + assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len()); + for (span, predicate) in std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates) + { + let cause = ObligationCause::misc(span, impl_ty_hir_id); + let traits::Normalized { value: predicate, obligations } = + traits::normalize(&mut selcx, param_env, cause, predicate); - // Finally, resolve all regions. This catches wily misuses of - // lifetime parameters. - let outlives_environment = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors( - impl_ty.def_id.expect_local(), - &outlives_environment, + let cause = ObligationCause::new( + span, + impl_ty_hir_id, + ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id: impl_ty.def_id.expect_local(), + trait_item_def_id: trait_ty.def_id, + kind: impl_ty.kind, + }, ); + ocx.register_obligations(obligations); + ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate)); + } + + // Check that all obligations are satisfied by the implementation's + // version. + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + return Err(reported); + } + + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let outlives_environment = OutlivesEnvironment::new(param_env); + infcx.check_region_obligations_and_report_errors( + impl_ty.def_id.expect_local(), + &outlives_environment, + ); - Ok(()) - }) + Ok(()) } /// Validate that `ProjectionCandidate`s created for this associated type will @@ -1693,94 +1731,89 @@ pub fn check_type_bounds<'tcx>( let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id); let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs); - tcx.infer_ctxt().enter(move |infcx| { - let ocx = ObligationCtxt::new(&infcx); + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); - let assumed_wf_types = - ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty.def_id.expect_local()); + let assumed_wf_types = + ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty.def_id.expect_local()); - let mut selcx = traits::SelectionContext::new(&infcx); - let normalize_cause = ObligationCause::new( - impl_ty_span, - impl_ty_hir_id, - ObligationCauseCode::CheckAssociatedTypeBounds { - impl_item_def_id: impl_ty.def_id.expect_local(), - trait_item_def_id: trait_ty.def_id, - }, - ); - let mk_cause = |span: Span| { - let code = if span.is_dummy() { - traits::ItemObligation(trait_ty.def_id) - } else { - traits::BindingObligation(trait_ty.def_id, span) - }; - ObligationCause::new(impl_ty_span, impl_ty_hir_id, code) + let mut selcx = traits::SelectionContext::new(&infcx); + let normalize_cause = ObligationCause::new( + impl_ty_span, + impl_ty_hir_id, + ObligationCauseCode::CheckAssociatedTypeBounds { + impl_item_def_id: impl_ty.def_id.expect_local(), + trait_item_def_id: trait_ty.def_id, + }, + ); + let mk_cause = |span: Span| { + let code = if span.is_dummy() { + traits::ItemObligation(trait_ty.def_id) + } else { + traits::BindingObligation(trait_ty.def_id, span) }; + ObligationCause::new(impl_ty_span, impl_ty_hir_id, code) + }; - let obligations = tcx - .bound_explicit_item_bounds(trait_ty.def_id) - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .map(|(bound, span)| { - debug!(?bound); - // this is where opaque type is found - let concrete_ty_bound = bound.subst(tcx, rebased_substs); - debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound); - - traits::Obligation::new(mk_cause(span.0), param_env, concrete_ty_bound) - }) - .collect(); - debug!("check_type_bounds: item_bounds={:?}", obligations); - - for mut obligation in util::elaborate_obligations(tcx, obligations) { - let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize( - &mut selcx, - normalize_param_env, - normalize_cause.clone(), - obligation.predicate, - ); - debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate); - obligation.predicate = normalized_predicate; + let obligations = tcx + .bound_explicit_item_bounds(trait_ty.def_id) + .subst_iter_copied(tcx, rebased_substs) + .map(|(concrete_ty_bound, span)| { + debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound); + traits::Obligation::new(mk_cause(span), param_env, concrete_ty_bound) + }) + .collect(); + debug!("check_type_bounds: item_bounds={:?}", obligations); + + for mut obligation in util::elaborate_obligations(tcx, obligations) { + let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize( + &mut selcx, + normalize_param_env, + normalize_cause.clone(), + obligation.predicate, + ); + debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate); + obligation.predicate = normalized_predicate; - ocx.register_obligations(obligations); - ocx.register_obligation(obligation); - } - // Check that all obligations are satisfied by the implementation's - // version. - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - let reported = infcx.report_fulfillment_errors(&errors, None, false); - return Err(reported); - } + ocx.register_obligations(obligations); + ocx.register_obligation(obligation); + } + // Check that all obligations are satisfied by the implementation's + // version. + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + return Err(reported); + } - // Finally, resolve all regions. This catches wily misuses of - // lifetime parameters. - let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_hir_id, assumed_wf_types); - let outlives_environment = - OutlivesEnvironment::with_bounds(param_env, Some(&infcx), implied_bounds); + // Finally, resolve all regions. This catches wily misuses of + // lifetime parameters. + let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_hir_id, assumed_wf_types); + let outlives_environment = + OutlivesEnvironment::with_bounds(param_env, Some(&infcx), implied_bounds); - infcx.check_region_obligations_and_report_errors( - impl_ty.def_id.expect_local(), - &outlives_environment, - ); + infcx.check_region_obligations_and_report_errors( + impl_ty.def_id.expect_local(), + &outlives_environment, + ); - let constraints = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - for (key, value) in constraints { - infcx - .report_mismatched_types( - &ObligationCause::misc( - value.hidden_type.span, - tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()), - ), - tcx.mk_opaque(key.def_id.to_def_id(), key.substs), - value.hidden_type.ty, - TypeError::Mismatch, - ) - .emit(); - } + let constraints = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); + for (key, value) in constraints { + infcx + .err_ctxt() + .report_mismatched_types( + &ObligationCause::misc( + value.hidden_type.span, + tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()), + ), + tcx.mk_opaque(key.def_id.to_def_id(), key.substs), + value.hidden_type.ty, + TypeError::Mismatch, + ) + .emit(); + } - Ok(()) - }) + Ok(()) } fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str { diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs index ab143c059..a74016e22 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_hir_analysis/src/check/dropck.rs @@ -1,4 +1,4 @@ -// FIXME(@lcnr): Move this module out of `rustc_typeck`. +// FIXME(@lcnr): Move this module out of `rustc_hir_analysis`. // // We don't do any drop checking during hir typeck. use crate::hir::def_id::{DefId, LocalDefId}; @@ -184,13 +184,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( let p = p.kind(); match (predicate.skip_binder(), p.skip_binder()) { (ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => { - // Since struct predicates cannot have ~const, project the impl predicate - // onto one that ignores the constness. This is equivalent to saying that - // we match a `Trait` bound on the struct with a `Trait` or `~const Trait` - // in the impl. - let non_const_a = - ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..a }; - relator.relate(predicate.rebind(non_const_a), p.rebind(b)).is_ok() + relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() } (ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => { relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() @@ -198,7 +192,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( ( ty::PredicateKind::ConstEvaluatable(a), ty::PredicateKind::ConstEvaluatable(b), - ) => tcx.try_unify_abstract_consts(self_param_env.and((a, b))), + ) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(), ( ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)), ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)), diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index c7425ff78..609095c9c 100644 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -7,10 +7,10 @@ use crate::errors::{ }; use crate::require_same_types; -use rustc_errors::struct_span_err; +use hir::def_id::DefId; +use rustc_errors::{struct_span_err, DiagnosticMessage}; use rustc_hir as hir; use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; -use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::symbol::{kw, sym, Symbol}; use rustc_target::spec::abi::Abi; @@ -26,7 +26,7 @@ fn equate_intrinsic_type<'tcx>( ) { let (own_counts, span) = match &it.kind { hir::ForeignItemKind::Fn(.., generics) => { - let own_counts = tcx.generics_of(it.def_id.to_def_id()).own_counts(); + let own_counts = tcx.generics_of(it.owner_id.to_def_id()).own_counts(); (own_counts, generics.span) } _ => { @@ -57,13 +57,17 @@ fn equate_intrinsic_type<'tcx>( { let fty = tcx.mk_fn_ptr(sig); let cause = ObligationCause::new(it.span, it.hir_id(), ObligationCauseCode::IntrinsicType); - require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(it.def_id)), fty); + require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(it.owner_id)), fty); } } /// Returns the unsafety of the given intrinsic. -pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { - match intrinsic { +pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir::Unsafety { + let has_safe_attr = match tcx.has_attr(intrinsic_id, sym::rustc_safe_intrinsic) { + true => hir::Unsafety::Normal, + false => hir::Unsafety::Unsafe, + }; + let is_in_list = match tcx.item_name(intrinsic_id) { // When adding a new intrinsic to this list, // it's usually worth updating that intrinsic's documentation // to note that it's safe to call, since @@ -107,14 +111,26 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { | sym::variant_count | sym::ptr_mask => hir::Unsafety::Normal, _ => hir::Unsafety::Unsafe, + }; + + if has_safe_attr != is_in_list { + tcx.sess.struct_span_err( + tcx.def_span(intrinsic_id), + DiagnosticMessage::Str(format!( + "intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `{}`", + tcx.item_name(intrinsic_id) + ))).emit(); } + + is_in_list } /// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`, /// and in `library/core/src/intrinsics.rs`. pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { let param = |n| tcx.mk_ty_param(n, Symbol::intern(&format!("P{}", n))); - let intrinsic_name = tcx.item_name(it.def_id.to_def_id()); + let intrinsic_id = it.owner_id.to_def_id(); + let intrinsic_name = tcx.item_name(intrinsic_id); let name_str = intrinsic_name.as_str(); let bound_vars = tcx.mk_bound_variable_kinds( @@ -161,7 +177,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { }; (n_tps, 0, inputs, output, hir::Unsafety::Unsafe) } else { - let unsafety = intrinsic_operation_unsafety(intrinsic_name); + let unsafety = intrinsic_operation_unsafety(tcx, intrinsic_id); let (n_tps, inputs, output) = match intrinsic_name { sym::abort => (0, Vec::new(), tcx.types.never), sym::unreachable => (0, Vec::new(), tcx.types.never), diff --git a/compiler/rustc_typeck/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index d8fe63dbf..17c4d0d48 100644 --- a/compiler/rustc_typeck/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -1,104 +1,11 @@ use rustc_ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::struct_span_err; use rustc_hir as hir; -use rustc_index::vec::Idx; -use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitable, UintTy}; use rustc_session::lint; -use rustc_span::{Span, Symbol, DUMMY_SP}; -use rustc_target::abi::{Pointer, VariantIdx}; +use rustc_span::{Symbol, DUMMY_SP}; use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType}; -use super::FnCtxt; - -/// If the type is `Option<T>`, it will return `T`, otherwise -/// the type itself. Works on most `Option`-like types. -fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty::Adt(def, substs) = *ty.kind() else { return ty }; - - if def.variants().len() == 2 && !def.repr().c() && def.repr().int.is_none() { - let data_idx; - - let one = VariantIdx::new(1); - let zero = VariantIdx::new(0); - - if def.variant(zero).fields.is_empty() { - data_idx = one; - } else if def.variant(one).fields.is_empty() { - data_idx = zero; - } else { - return ty; - } - - if def.variant(data_idx).fields.len() == 1 { - return def.variant(data_idx).fields[0].ty(tcx, substs); - } - } - - ty -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) { - let convert = |ty: Ty<'tcx>| { - let ty = self.resolve_vars_if_possible(ty); - let ty = self.tcx.normalize_erasing_regions(self.param_env, ty); - (SizeSkeleton::compute(ty, self.tcx, self.param_env), ty) - }; - let (sk_from, from) = convert(from); - let (sk_to, to) = convert(to); - - // Check for same size using the skeletons. - if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) { - if sk_from.same_size(sk_to) { - return; - } - - // Special-case transmuting from `typeof(function)` and - // `Option<typeof(function)>` to present a clearer error. - let from = unpack_option_like(self.tcx, from); - if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer.size(&self.tcx) { - struct_span_err!(self.tcx.sess, span, E0591, "can't transmute zero-sized type") - .note(&format!("source type: {from}")) - .note(&format!("target type: {to}")) - .help("cast with `as` to a pointer instead") - .emit(); - return; - } - } - - // Try to display a sensible error with as much information as possible. - let skeleton_string = |ty: Ty<'tcx>, sk| match sk { - Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()), - Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"), - Err(LayoutError::Unknown(bad)) => { - if bad == ty { - "this type does not have a fixed size".to_owned() - } else { - format!("size can vary because of {bad}") - } - } - Err(err) => err.to_string(), - }; - - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0512, - "cannot transmute between types of different sizes, \ - or dependently-sized types" - ); - if from == to { - err.note(&format!("`{from}` does not have a fixed size")); - } else { - err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))) - .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); - } - err.emit(); - } -} - pub struct InlineAsmCtxt<'a, 'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -126,7 +33,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool { // Type still may have region variables, but `Sized` does not depend // on those, so just erase them before querying. - if ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) { + if ty.is_sized(self.tcx, self.param_env) { return true; } if let ty::Foreign(..) = ty.kind() { @@ -146,7 +53,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { target_features: &FxHashSet<Symbol>, ) -> Option<InlineAsmType> { let ty = (self.get_operand_ty)(expr); - if ty.has_infer_types_or_consts() { + if ty.has_non_region_infer() { bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty); } let asm_ty_isize = match self.tcx.sess.target.pointer_width { @@ -221,7 +128,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { // Check that the type implements Copy. The only case where this can // possibly fail is for SIMD types which don't #[derive(Copy)]. - if !ty.is_copy_modulo_regions(self.tcx.at(expr.span), self.param_env) { + if !ty.is_copy_modulo_regions(self.tcx, self.param_env) { let msg = "arguments for inline assembly must be copyable"; let mut err = self.tcx.sess.struct_span_err(expr.span, msg); err.note(&format!("`{ty}` does not implement the Copy trait")); @@ -328,17 +235,16 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { lint::builtin::ASM_SUB_REGISTER, expr.hir_id, spans, + "formatting may not be suitable for sub-register argument", |lint| { - let msg = "formatting may not be suitable for sub-register argument"; - let mut err = lint.build(msg); - err.span_label(expr.span, "for this argument"); - err.help(&format!( + lint.span_label(expr.span, "for this argument"); + lint.help(&format!( "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}`", )); - err.help(&format!( + lint.help(&format!( "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}`", )); - err.emit(); + lint }, ); } diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index cfae63e4a..2e7b10257 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -62,195 +62,49 @@ a type parameter). */ -pub mod _match; -mod autoderef; -mod callee; -pub mod cast; mod check; -mod closure; -pub mod coercion; mod compare_method; -pub mod demand; -mod diverges; pub mod dropck; -mod expectation; -mod expr; -mod fallback; -mod fn_ctxt; -mod gather_locals; -mod generator_interior; -mod inherited; pub mod intrinsic; -mod intrinsicck; -pub mod method; -mod op; -mod pat; -mod place_op; +pub mod intrinsicck; mod region; -pub mod rvalue_scopes; -mod upvar; pub mod wfcheck; -pub mod writeback; -use check::{check_abi, check_fn, check_mod_item_types}; -pub use diverges::Diverges; -pub use expectation::Expectation; -pub use fn_ctxt::*; -pub use inherited::{Inherited, InheritedBuilder}; +pub use check::check_abi; -use crate::astconv::AstConv; -use crate::check::gather_locals::GatherLocalsVisitor; +use check::check_mod_item_types; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{ - pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan, -}; +use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder}; use rustc_hir as hir; -use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{HirIdMap, ImplicitSelfKind, Node}; use rustc_index::bit_set::BitSet; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, Ty, TyCtxt, UserType}; -use rustc_session::config; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::parse::feature_err; -use rustc_session::Session; use rustc_span::source_map::DUMMY_SP; use rustc_span::symbol::{kw, Ident}; use rustc_span::{self, BytePos, Span, Symbol}; use rustc_target::abi::VariantIdx; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits; -use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error; use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; -use std::cell::RefCell; use std::num::NonZeroU32; use crate::require_c_abi_if_c_variadic; use crate::util::common::indenter; -use self::coercion::DynamicCoerceMany; use self::compare_method::collect_trait_impl_trait_tys; use self::region::region_scope_tree; -pub use self::Expectation::*; - -#[macro_export] -macro_rules! type_error_struct { - ($session:expr, $span:expr, $typ:expr, $code:ident, $($message:tt)*) => ({ - let mut err = rustc_errors::struct_span_err!($session, $span, $code, $($message)*); - - if $typ.references_error() { - err.downgrade_to_delayed_bug(); - } - - err - }) -} - -/// The type of a local binding, including the revealed type for anon types. -#[derive(Copy, Clone, Debug)] -pub struct LocalTy<'tcx> { - decl_ty: Ty<'tcx>, - revealed_ty: Ty<'tcx>, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Needs { - MutPlace, - None, -} - -impl Needs { - fn maybe_mut_place(m: hir::Mutability) -> Self { - match m { - hir::Mutability::Mut => Needs::MutPlace, - hir::Mutability::Not => Needs::None, - } - } -} - -#[derive(Copy, Clone)] -pub struct UnsafetyState { - pub def: hir::HirId, - pub unsafety: hir::Unsafety, - from_fn: bool, -} - -impl UnsafetyState { - pub fn function(unsafety: hir::Unsafety, def: hir::HirId) -> UnsafetyState { - UnsafetyState { def, unsafety, from_fn: true } - } - - pub fn recurse(self, blk: &hir::Block<'_>) -> UnsafetyState { - use hir::BlockCheckMode; - match self.unsafety { - // If this unsafe, then if the outer function was already marked as - // unsafe we shouldn't attribute the unsafe'ness to the block. This - // way the block can be warned about instead of ignoring this - // extraneous block (functions are never warned about). - hir::Unsafety::Unsafe if self.from_fn => self, - - unsafety => { - let (unsafety, def) = match blk.rules { - BlockCheckMode::UnsafeBlock(..) => (hir::Unsafety::Unsafe, blk.hir_id), - BlockCheckMode::DefaultBlock => (unsafety, self.def), - }; - UnsafetyState { def, unsafety, from_fn: false } - } - } - } -} - -#[derive(Debug, Copy, Clone)] -pub enum PlaceOp { - Deref, - Index, -} - -pub struct BreakableCtxt<'tcx> { - may_break: bool, - - // this is `null` for loops where break with a value is illegal, - // such as `while`, `for`, and `while let` - coerce: Option<DynamicCoerceMany<'tcx>>, -} - -pub struct EnclosingBreakables<'tcx> { - stack: Vec<BreakableCtxt<'tcx>>, - by_id: HirIdMap<usize>, -} - -impl<'tcx> EnclosingBreakables<'tcx> { - fn find_breakable(&mut self, target_id: hir::HirId) -> &mut BreakableCtxt<'tcx> { - self.opt_find_breakable(target_id).unwrap_or_else(|| { - bug!("could not find enclosing breakable with id {}", target_id); - }) - } - - fn opt_find_breakable(&mut self, target_id: hir::HirId) -> Option<&mut BreakableCtxt<'tcx>> { - match self.by_id.get(&target_id) { - Some(ix) => Some(&mut self.stack[*ix]), - None => None, - } - } -} pub fn provide(providers: &mut Providers) { - method::provide(providers); wfcheck::provide(providers); *providers = Providers { - typeck_item_bodies, - typeck_const_arg, - typeck, - diagnostic_only_typeck, - has_typeck_results, adt_destructor, - used_trait_imports, check_mod_item_types, region_scope_tree, collect_trait_impl_trait_tys, + compare_assoc_const_impl_item_with_trait_item: compare_method::raw_compare_const_impl, ..*providers }; } @@ -259,255 +113,6 @@ fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> { tcx.calculate_dtor(def_id, dropck::check_drop_impl) } -/// If this `DefId` is a "primary tables entry", returns -/// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`. -/// -/// If this function returns `Some`, then `typeck_results(def_id)` will -/// succeed; if it returns `None`, then `typeck_results(def_id)` may or -/// may not succeed. In some cases where this function returns `None` -/// (notably closures), `typeck_results(def_id)` would wind up -/// redirecting to the owning function. -fn primary_body_of( - tcx: TyCtxt<'_>, - id: hir::HirId, -) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> { - match tcx.hir().get(id) { - Node::Item(item) => match item.kind { - hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => { - Some((body, Some(ty), None)) - } - hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))), - _ => None, - }, - Node::TraitItem(item) => match item.kind { - hir::TraitItemKind::Const(ty, Some(body)) => Some((body, Some(ty), None)), - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - Some((body, None, Some(sig))) - } - _ => None, - }, - Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Const(ty, body) => Some((body, Some(ty), None)), - hir::ImplItemKind::Fn(ref sig, body) => Some((body, None, Some(sig))), - _ => None, - }, - Node::AnonConst(constant) => Some((constant.body, None, None)), - _ => None, - } -} - -fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - // Closures' typeck results come from their outermost function, - // as they are part of the same "inference environment". - let typeck_root_def_id = tcx.typeck_root_def_id(def_id); - if typeck_root_def_id != def_id { - return tcx.has_typeck_results(typeck_root_def_id); - } - - if let Some(def_id) = def_id.as_local() { - let id = tcx.hir().local_def_id_to_hir_id(def_id); - primary_body_of(tcx, id).is_some() - } else { - false - } -} - -fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &FxHashSet<LocalDefId> { - &*tcx.typeck(def_id).used_trait_imports -} - -fn typeck_const_arg<'tcx>( - tcx: TyCtxt<'tcx>, - (did, param_did): (LocalDefId, DefId), -) -> &ty::TypeckResults<'tcx> { - let fallback = move || tcx.type_of(param_did); - typeck_with_fallback(tcx, did, fallback) -} - -fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { - if let Some(param_did) = tcx.opt_const_param_of(def_id) { - tcx.typeck_const_arg((def_id, param_did)) - } else { - let fallback = move || tcx.type_of(def_id.to_def_id()); - typeck_with_fallback(tcx, def_id, fallback) - } -} - -/// Used only to get `TypeckResults` for type inference during error recovery. -/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors. -fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { - let fallback = move || { - let span = tcx.hir().span(tcx.hir().local_def_id_to_hir_id(def_id)); - tcx.ty_error_with_message(span, "diagnostic only typeck table used") - }; - typeck_with_fallback(tcx, def_id, fallback) -} - -fn typeck_with_fallback<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - fallback: impl Fn() -> Ty<'tcx> + 'tcx, -) -> &'tcx ty::TypeckResults<'tcx> { - // Closures' typeck results come from their outermost function, - // as they are part of the same "inference environment". - let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(); - if typeck_root_def_id != def_id { - return tcx.typeck(typeck_root_def_id); - } - - let id = tcx.hir().local_def_id_to_hir_id(def_id); - let span = tcx.hir().span(id); - - // Figure out what primary body this item has. - let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| { - span_bug!(span, "can't type-check body of {:?}", def_id); - }); - let body = tcx.hir().body(body_id); - - let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { - let param_env = tcx.param_env(def_id); - let fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig { - let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); - <dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None) - } else { - tcx.fn_sig(def_id) - }; - - check_abi(tcx, id, span, fn_sig.abi()); - - // Compute the function signature from point of view of inside the fn. - let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); - let fn_sig = inh.normalize_associated_types_in( - body.value.span, - body_id.hir_id, - param_env, - fn_sig, - ); - check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0 - } else { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); - let expected_type = body_ty - .and_then(|ty| match ty.kind { - hir::TyKind::Infer => Some(<dyn AstConv<'_>>::ast_ty_to_ty(&fcx, ty)), - _ => None, - }) - .unwrap_or_else(|| match tcx.hir().get(id) { - Node::AnonConst(_) => match tcx.hir().get(tcx.hir().get_parent_node(id)) { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::ConstBlock(ref anon_const), - .. - }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }), - Node::Ty(&hir::Ty { - kind: hir::TyKind::Typeof(ref anon_const), .. - }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }), - Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. }) - | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => { - let operand_ty = asm - .operands - .iter() - .filter_map(|(op, _op_sp)| match op { - hir::InlineAsmOperand::Const { anon_const } - if anon_const.hir_id == id => - { - // Inline assembly constants must be integers. - Some(fcx.next_int_var()) - } - hir::InlineAsmOperand::SymFn { anon_const } - if anon_const.hir_id == id => - { - Some(fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span, - })) - } - _ => None, - }) - .next(); - operand_ty.unwrap_or_else(fallback) - } - _ => fallback(), - }, - _ => fallback(), - }); - - let expected_type = fcx.normalize_associated_types_in(body.value.span, expected_type); - fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); - - // Gather locals in statics (because of block expressions). - GatherLocalsVisitor::new(&fcx).visit_body(body); - - fcx.check_expr_coercable_to_type(&body.value, expected_type, None); - - fcx.write_ty(id, expected_type); - - fcx - }; - - let fallback_has_occurred = fcx.type_inference_fallback(); - - // Even though coercion casts provide type hints, we check casts after fallback for - // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - fcx.check_casts(); - fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); - - // Closure and generator analysis may run after fallback - // because they don't constrain other type variables. - fcx.closure_analyze(body); - assert!(fcx.deferred_call_resolutions.borrow().is_empty()); - // Before the generator analysis, temporary scopes shall be marked to provide more - // precise information on types to be captured. - fcx.resolve_rvalue_scopes(def_id.to_def_id()); - fcx.resolve_generator_interiors(def_id.to_def_id()); - - for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { - let ty = fcx.normalize_ty(span, ty); - fcx.require_type_is_sized(ty, span, code); - } - - fcx.select_all_obligations_or_error(); - - if !fcx.infcx.is_tainted_by_errors() { - fcx.check_transmutes(); - } - - fcx.check_asms(); - - fcx.infcx.skip_region_resolution(); - - fcx.resolve_type_vars_in_body(body) - }); - - // Consistency check our TypeckResults instance can hold all ItemLocalIds - // it will need to hold. - assert_eq!(typeck_results.hir_owner, id.owner); - - typeck_results -} - -/// When `check_fn` is invoked on a generator (i.e., a body that -/// includes yield), it returns back some information about the yield -/// points. -struct GeneratorTypes<'tcx> { - /// Type of generator argument / values returned by `yield`. - resume_ty: Ty<'tcx>, - - /// Type of value that is yielded. - yield_ty: Ty<'tcx>, - - /// Types that are captured (see `GeneratorInterior` for more). - interior: Ty<'tcx>, - - /// Indicates if the generator is movable or static (immovable). - movability: hir::Movability, -} - /// Given a `DefId` for an opaque type in return position, find its parent item's return /// expressions. fn get_owner_return_paths<'tcx>( @@ -515,7 +120,7 @@ fn get_owner_return_paths<'tcx>( def_id: LocalDefId, ) -> Option<(LocalDefId, ReturnsVisitor<'tcx>)> { let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let parent_id = tcx.hir().get_parent_item(hir_id); + let parent_id = tcx.hir().get_parent_item(hir_id).def_id; tcx.hir().find_by_def_id(parent_id).and_then(|node| node.body_id()).map(|body_id| { let body = tcx.hir().body(body_id); let mut visitor = ReturnsVisitor::default(); @@ -524,9 +129,10 @@ fn get_owner_return_paths<'tcx>( }) } -// Forbid defining intrinsics in Rust code, -// as they must always be defined by the compiler. -fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) { +/// Forbid defining intrinsics in Rust code, +/// as they must always be defined by the compiler. +// FIXME: Move this to a more appropriate place. +pub fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) { if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi { tcx.sess.span_err(sp, "intrinsic must be in `extern \"rust-intrinsic\" { ... }` block"); } @@ -820,6 +426,17 @@ fn fn_sig_suggestion<'tcx>( format!("{unsafety}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}") } +pub fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { + Some(match ty.kind() { + ty::Bool => "true", + ty::Char => "'a'", + ty::Int(_) | ty::Uint(_) => "42", + ty::Float(_) => "3.14159", + ty::Error(_) | ty::Never => return None, + _ => "value", + }) +} + /// Return placeholder code for the given associated item. /// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a /// structured suggestion. @@ -841,7 +458,7 @@ fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String { ty::AssocKind::Type => format!("type {} = Type;", assoc.name), ty::AssocKind::Const => { let ty = tcx.type_of(assoc.def_id); - let val = expr::ty_kind_suggestion(ty).unwrap_or("value"); + let val = ty_kind_suggestion(ty).unwrap_or("value"); format!("const {}: {} = {};", assoc.name, ty, val) } } @@ -892,76 +509,7 @@ fn bad_non_zero_sized_fields<'tcx>( err.emit(); } -fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, qpath: &hir::QPath<'_>, span: Span) { - struct_span_err!( - tcx.sess, - span, - E0533, - "expected unit struct, unit variant or constant, found {} `{}`", - res.descr(), - rustc_hir_pretty::qpath_to_string(qpath), - ) - .emit(); -} - -/// Controls whether the arguments are tupled. This is used for the call -/// operator. -/// -/// Tupling means that all call-side arguments are packed into a tuple and -/// passed as a single parameter. For example, if tupling is enabled, this -/// function: -/// ``` -/// fn f(x: (isize, isize)) {} -/// ``` -/// Can be called as: -/// ```ignore UNSOLVED (can this be done in user code?) -/// # fn f(x: (isize, isize)) {} -/// f(1, 2); -/// ``` -/// Instead of: -/// ``` -/// # fn f(x: (isize, isize)) {} -/// f((1, 2)); -/// ``` -#[derive(Clone, Eq, PartialEq)] -enum TupleArgumentsFlag { - DontTupleArguments, - TupleArguments, -} - -fn typeck_item_bodies(tcx: TyCtxt<'_>, (): ()) { - tcx.hir().par_body_owners(|body_owner_def_id| tcx.ensure().typeck(body_owner_def_id)); -} - -fn fatally_break_rust(sess: &Session) { - let handler = sess.diagnostic(); - handler.span_bug_no_panic( - MultiSpan::new(), - "It looks like you're trying to break rust; would you like some ICE?", - ); - handler.note_without_error("the compiler expectedly panicked. this is a feature."); - handler.note_without_error( - "we would appreciate a joke overview: \ - https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675", - ); - handler.note_without_error(&format!( - "rustc {} running on {}", - option_env!("CFG_VERSION").unwrap_or("unknown_version"), - config::host_triple(), - )); -} - -fn potentially_plural_count(count: usize, word: &str) -> String { +// FIXME: Consider moving this method to a more fitting place. +pub fn potentially_plural_count(count: usize, word: &str) -> String { format!("{} {}{}", count, word, pluralize!(count)) } - -fn has_expected_num_generic_args<'tcx>( - tcx: TyCtxt<'tcx>, - trait_did: Option<DefId>, - expected: usize, -) -> bool { - trait_did.map_or(true, |trait_did| { - let generics = tcx.generics_of(trait_did); - generics.count() == expected + if generics.has_self { 1 } else { 0 } - }) -} diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index b89db79be..ff32329e4 100644 --- a/compiler/rustc_typeck/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -252,9 +252,13 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h ) => { // For shortcircuiting operators, mark the RHS as a terminating // scope since it only executes conditionally. - terminating(r.hir_id.local_id); - } + // `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries + // should live beyond the immediate expression + if !matches!(r.kind, hir::ExprKind::Let(_)) { + terminating(r.hir_id.local_id); + } + } hir::ExprKind::If(_, ref then, Some(ref otherwise)) => { terminating(then.hir_id.local_id); terminating(otherwise.hir_id.local_id); diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 27b3da8ab..a23575004 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -12,17 +12,17 @@ use rustc_infer::infer::outlives::obligations::TypeOutlives; use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst}; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{ self, AdtKind, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, }; +use rustc_middle::ty::{GenericArgKind, InternalSubsts}; use rustc_session::parse::feature_err; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::autoderef::Autoderef; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ @@ -91,32 +91,31 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>( { let param_env = tcx.param_env(body_def_id); let body_id = tcx.hir().local_def_id_to_hir_id(body_def_id); - tcx.infer_ctxt().enter(|ref infcx| { - let ocx = ObligationCtxt::new(infcx); + let infcx = &tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(infcx); - let assumed_wf_types = ocx.assumed_wf_types(param_env, span, body_def_id); + let assumed_wf_types = ocx.assumed_wf_types(param_env, span, body_def_id); - let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env }; + let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env }; - if !tcx.features().trivial_bounds { - wfcx.check_false_global_bounds() - } - f(&mut wfcx); - let errors = wfcx.select_all_or_error(); - if !errors.is_empty() { - infcx.report_fulfillment_errors(&errors, None, false); - return; - } + if !tcx.features().trivial_bounds { + wfcx.check_false_global_bounds() + } + f(&mut wfcx); + let errors = wfcx.select_all_or_error(); + if !errors.is_empty() { + infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + return; + } - let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types); - let outlives_environment = - OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds); + let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types); + let outlives_environment = + OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds); - infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment); - }) + infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment); } -fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { +fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) { let node = tcx.hir().expect_owner(def_id); match node { hir::OwnerNode::Crate(_) => {} @@ -148,10 +147,10 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) { /// the types first. #[instrument(skip(tcx), level = "debug")] fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { - let def_id = item.def_id; + let def_id = item.owner_id.def_id; debug!( - ?item.def_id, + ?item.owner_id, item.name = ? tcx.def_path_str(def_id.to_def_id()) ); @@ -175,7 +174,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { // for `T` hir::ItemKind::Impl(ref impl_) => { let is_auto = tcx - .impl_trait_ref(item.def_id) + .impl_trait_ref(def_id) .map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.def_id)); if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) { let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span); @@ -211,13 +210,13 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { } } hir::ItemKind::Fn(ref sig, ..) => { - check_item_fn(tcx, item.def_id, item.ident, item.span, sig.decl); + check_item_fn(tcx, def_id, item.ident, item.span, sig.decl); } hir::ItemKind::Static(ty, ..) => { - check_item_type(tcx, item.def_id, ty.span, false); + check_item_type(tcx, def_id, ty.span, false); } hir::ItemKind::Const(ty, ..) => { - check_item_type(tcx, item.def_id, ty.span, false); + check_item_type(tcx, def_id, ty.span, false); } hir::ItemKind::Struct(ref struct_def, ref ast_generics) => { check_type_defn(tcx, item, false, |wfcx| vec![wfcx.non_enum_variant(struct_def)]); @@ -247,24 +246,24 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { } fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) { - let def_id = item.def_id; + let def_id = item.owner_id.def_id; debug!( - ?item.def_id, + ?item.owner_id, item.name = ? tcx.def_path_str(def_id.to_def_id()) ); match item.kind { hir::ForeignItemKind::Fn(decl, ..) => { - check_item_fn(tcx, item.def_id, item.ident, item.span, decl) + check_item_fn(tcx, def_id, item.ident, item.span, decl) } - hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, item.def_id, ty.span, true), + hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, def_id, ty.span, true), hir::ForeignItemKind::Type => (), } } fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) { - let def_id = trait_item.def_id; + let def_id = trait_item.owner_id.def_id; let (method_sig, span) = match trait_item.kind { hir::TraitItemKind::Fn(ref sig, _) => (Some(sig), trait_item.span), @@ -272,11 +271,11 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) { _ => (None, trait_item.span), }; check_object_unsafe_self_trait_by_name(tcx, trait_item); - check_associated_item(tcx, trait_item.def_id, span, method_sig); + check_associated_item(tcx, def_id, span, method_sig); let encl_trait_def_id = tcx.local_parent(def_id); let encl_trait = tcx.hir().expect_item(encl_trait_def_id); - let encl_trait_def_id = encl_trait.def_id.to_def_id(); + let encl_trait_def_id = encl_trait.owner_id.to_def_id(); let fn_lang_item_name = if Some(encl_trait_def_id) == tcx.lang_items().fn_trait() { Some("fn") } else if Some(encl_trait_def_id) == tcx.lang_items().fn_mut_trait() { @@ -349,7 +348,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe loop { let mut should_continue = false; for gat_item in associated_items { - let gat_def_id = gat_item.id.def_id; + let gat_def_id = gat_item.id.owner_id; let gat_item = tcx.associated_item(gat_def_id); // If this item is not an assoc ty, or has no substs, then it's not a GAT if gat_item.kind != ty::AssocKind::Type { @@ -366,7 +365,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe // constrains the GAT with individually. let mut new_required_bounds: Option<FxHashSet<ty::Predicate<'_>>> = None; for item in associated_items { - let item_def_id = item.id.def_id; + let item_def_id = item.id.owner_id; // Skip our own GAT, since it does not constrain itself at all. if item_def_id == gat_def_id { continue; @@ -393,7 +392,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe // We also assume that all of the function signature's parameter types // are well formed. &sig.inputs().iter().copied().collect(), - gat_def_id, + gat_def_id.def_id, gat_generics, ) } @@ -416,7 +415,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe .copied() .collect::<Vec<_>>(), &FxHashSet::default(), - gat_def_id, + gat_def_id.def_id, gat_generics, ) } @@ -456,7 +455,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe } for (gat_def_id, required_bounds) in required_bounds_by_item { - let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id); + let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id.def_id); debug!(?required_bounds); let param_env = tcx.param_env(gat_def_id); let gat_hir = gat_item_hir.hir_id(); @@ -699,29 +698,32 @@ fn resolve_regions_with_wf_tys<'tcx>( id: hir::HirId, param_env: ty::ParamEnv<'tcx>, wf_tys: &FxHashSet<Ty<'tcx>>, - add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'a, 'tcx>, &'a RegionBoundPairs<'tcx>), + add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'tcx>, &'a RegionBoundPairs<'tcx>), ) -> bool { // Unfortunately, we have to use a new `InferCtxt` each call, because // region constraints get added and solved there and we need to test each // call individually. - tcx.infer_ctxt().enter(|infcx| { - let outlives_environment = OutlivesEnvironment::with_bounds( - param_env, - Some(&infcx), - infcx.implied_bounds_tys(param_env, id, wf_tys.clone()), - ); - let region_bound_pairs = outlives_environment.region_bound_pairs(); + let infcx = tcx.infer_ctxt().build(); + let outlives_environment = OutlivesEnvironment::with_bounds( + param_env, + Some(&infcx), + infcx.implied_bounds_tys(param_env, id, wf_tys.clone()), + ); + let region_bound_pairs = outlives_environment.region_bound_pairs(); - add_constraints(&infcx, region_bound_pairs); + add_constraints(&infcx, region_bound_pairs); - let errors = infcx.resolve_regions(&outlives_environment); + infcx.process_registered_region_obligations( + outlives_environment.region_bound_pairs(), + param_env, + ); + let errors = infcx.resolve_regions(&outlives_environment); - debug!(?errors, "errors"); + debug!(?errors, "errors"); - // If we were able to prove that the type outlives the region without - // an error, it must be because of the implied or explicit bounds... - errors.is_empty() - }) + // If we were able to prove that the type outlives the region without + // an error, it must be because of the implied or explicit bounds... + errors.is_empty() } /// TypeVisitor that looks for uses of GATs like @@ -786,9 +788,9 @@ fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { /// When this is done, suggest using `Self` instead. fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem<'_>) { let (trait_name, trait_def_id) = - match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(item.hir_id())) { + match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(item.hir_id()).def_id) { hir::Node::Item(item) => match item.kind { - hir::ItemKind::Trait(..) => (item.ident, item.def_id), + hir::ItemKind::Trait(..) => (item.ident, item.owner_id), _ => return, }, _ => return, @@ -796,18 +798,18 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem let mut trait_should_be_self = vec![]; match &item.kind { hir::TraitItemKind::Const(ty, _) | hir::TraitItemKind::Type(_, Some(ty)) - if could_be_self(trait_def_id, ty) => + if could_be_self(trait_def_id.def_id, ty) => { trait_should_be_self.push(ty.span) } hir::TraitItemKind::Fn(sig, _) => { for ty in sig.decl.inputs { - if could_be_self(trait_def_id, ty) { + if could_be_self(trait_def_id.def_id, ty) { trait_should_be_self.push(ty.span); } } match sig.decl.output { - hir::FnRetTy::Return(ty) if could_be_self(trait_def_id, ty) => { + hir::FnRetTy::Return(ty) if could_be_self(trait_def_id.def_id, ty) => { trait_should_be_self.push(ty.span); } _ => {} @@ -836,16 +838,14 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem } fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) { - let def_id = impl_item.def_id; - let (method_sig, span) = match impl_item.kind { hir::ImplItemKind::Fn(ref sig, _) => (Some(sig), impl_item.span), // Constrain binding and overflow error spans to `<Ty>` in `type foo = <Ty>`. - hir::ImplItemKind::TyAlias(ty) if ty.span != DUMMY_SP => (None, ty.span), + hir::ImplItemKind::Type(ty) if ty.span != DUMMY_SP => (None, ty.span), _ => (None, impl_item.span), }; - check_associated_item(tcx, def_id, span, method_sig); + check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig); } fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { @@ -1045,9 +1045,11 @@ fn check_type_defn<'tcx, F>( ) where F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>, { - enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { + let _ = tcx.representability(item.owner_id.def_id); + + enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| { let variants = lookup_fields(wfcx); - let packed = tcx.adt_def(item.def_id).repr().packed(); + let packed = tcx.adt_def(item.owner_id).repr().packed(); for variant in &variants { // All field types must be well-formed. @@ -1071,7 +1073,7 @@ fn check_type_defn<'tcx, F>( // Just treat unresolved type expression as if it needs drop. true } else { - ty.needs_drop(tcx, tcx.param_env(item.def_id)) + ty.needs_drop(tcx, tcx.param_env(item.owner_id)) } } }; @@ -1103,8 +1105,6 @@ fn check_type_defn<'tcx, F>( // Explicit `enum` discriminant values must const-evaluate successfully. if let Some(discr_def_id) = variant.explicit_discr { - let discr_substs = InternalSubsts::identity_for_item(tcx, discr_def_id.to_def_id()); - let cause = traits::ObligationCause::new( tcx.def_span(discr_def_id), wfcx.body_id, @@ -1113,28 +1113,28 @@ fn check_type_defn<'tcx, F>( wfcx.register_obligation(traits::Obligation::new( cause, wfcx.param_env, - ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ty::Unevaluated::new( - ty::WithOptConstParam::unknown(discr_def_id.to_def_id()), - discr_substs, - ))) + ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable( + ty::Const::from_anon_const(tcx, discr_def_id), + )) .to_predicate(tcx), )); } } - check_where_clauses(wfcx, item.span, item.def_id); + check_where_clauses(wfcx, item.span, item.owner_id.def_id); }); } #[instrument(skip(tcx, item))] fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { - debug!(?item.def_id); + debug!(?item.owner_id); - let trait_def = tcx.trait_def(item.def_id); + let def_id = item.owner_id.def_id; + let trait_def = tcx.trait_def(def_id); if trait_def.is_marker || matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker) { - for associated_def_id in &*tcx.associated_item_def_ids(item.def_id) { + for associated_def_id in &*tcx.associated_item_def_ids(def_id) { struct_span_err!( tcx.sess, tcx.def_span(*associated_def_id), @@ -1145,8 +1145,8 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { } } - enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { - check_where_clauses(wfcx, item.span, item.def_id) + enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| { + check_where_clauses(wfcx, item.span, def_id) }); // Only check traits, don't check trait aliases @@ -1240,13 +1240,13 @@ fn check_impl<'tcx>( ast_trait_ref: &Option<hir::TraitRef<'_>>, constness: hir::Constness, ) { - enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { + enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| { match *ast_trait_ref { Some(ref ast_trait_ref) => { // `#[rustc_reservation_impl]` impls are not real impls and // therefore don't need to be WF (the trait's `Self: Trait` predicate // won't hold). - let trait_ref = tcx.impl_trait_ref(item.def_id).unwrap(); + let trait_ref = tcx.impl_trait_ref(item.owner_id).unwrap(); let trait_ref = wfcx.normalize(ast_trait_ref.path.span, None, trait_ref); let trait_pred = ty::TraitPredicate { trait_ref, @@ -1268,21 +1268,21 @@ fn check_impl<'tcx>( wfcx.register_obligations(obligations); } None => { - let self_ty = tcx.type_of(item.def_id); + let self_ty = tcx.type_of(item.owner_id); let self_ty = wfcx.normalize( item.span, - Some(WellFormedLoc::Ty(item.hir_id().expect_owner())), + Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)), self_ty, ); wfcx.register_wf_obligation( ast_self_ty.span, - Some(WellFormedLoc::Ty(item.hir_id().expect_owner())), + Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)), self_ty.into(), ); } } - check_where_clauses(wfcx, item.span, item.def_id); + check_where_clauses(wfcx, item.span, item.owner_id.def_id); }); } @@ -1427,9 +1427,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id let substituted_pred = predicates.rebind(pred).subst(tcx, substs); // Don't check non-defaulted params, dependent defaults (including lifetimes) // or preds with multiple params. - if substituted_pred.has_param_types_or_consts() - || param_count.params.len() > 1 - || has_region + if substituted_pred.has_non_region_param() || param_count.params.len() > 1 || has_region { None } else if predicates.0.predicates.iter().any(|&(p, _)| p == substituted_pred) { @@ -1678,7 +1676,7 @@ fn receiver_is_valid<'tcx>( // `self: Self` is always valid. if can_eq_self(receiver_ty) { if let Err(err) = wfcx.equate_types(&cause, wfcx.param_env, self_ty, receiver_ty) { - infcx.report_mismatched_types(&cause, self_ty, receiver_ty, err).emit(); + infcx.err_ctxt().report_mismatched_types(&cause, self_ty, receiver_ty, err).emit(); } return true; } @@ -1710,7 +1708,10 @@ fn receiver_is_valid<'tcx>( if let Err(err) = wfcx.equate_types(&cause, wfcx.param_env, self_ty, potential_self_ty) { - infcx.report_mismatched_types(&cause, self_ty, potential_self_ty, err).emit(); + infcx + .err_ctxt() + .report_mismatched_types(&cause, self_ty, potential_self_ty, err) + .emit(); } break; @@ -1777,14 +1778,14 @@ fn check_variances_for_type_defn<'tcx>( item: &hir::Item<'tcx>, hir_generics: &hir::Generics<'_>, ) { - let ty = tcx.type_of(item.def_id); + let ty = tcx.type_of(item.owner_id); if tcx.has_error_field(ty) { return; } - let ty_predicates = tcx.predicates_of(item.def_id); + let ty_predicates = tcx.predicates_of(item.owner_id); assert_eq!(ty_predicates.parent, None); - let variances = tcx.variances_of(item.def_id); + let variances = tcx.variances_of(item.owner_id); let mut constrained_parameters: FxHashSet<_> = variances .iter() @@ -1797,7 +1798,7 @@ fn check_variances_for_type_defn<'tcx>( // Lazily calculated because it is only needed in case of an error. let explicitly_bounded_params = LazyCell::new(|| { - let icx = crate::collect::ItemCtxt::new(tcx, item.def_id.to_def_id()); + let icx = crate::collect::ItemCtxt::new(tcx, item.owner_id.to_def_id()); hir_generics .predicates .iter() @@ -1918,10 +1919,10 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalDefId) { let items = tcx.hir_module_items(module); - items.par_items(|item| tcx.ensure().check_well_formed(item.def_id)); - items.par_impl_items(|item| tcx.ensure().check_well_formed(item.def_id)); - items.par_trait_items(|item| tcx.ensure().check_well_formed(item.def_id)); - items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.def_id)); + items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id)); + items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id)); + items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id)); + items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id)); } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_typeck/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs index 1d23ed929..d0c317334 100644 --- a/compiler/rustc_typeck/src/check_unused.rs +++ b/compiler/rustc_hir_analysis/src/check_unused.rs @@ -1,5 +1,6 @@ use crate::errors::{ExternCrateNotIdiomatic, UnusedExternCrate}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::unord::UnordSet; use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -8,12 +9,12 @@ use rustc_session::lint; use rustc_span::{Span, Symbol}; pub fn check_crate(tcx: TyCtxt<'_>) { - let mut used_trait_imports: FxHashSet<LocalDefId> = FxHashSet::default(); + let mut used_trait_imports: UnordSet<LocalDefId> = Default::default(); for item_def_id in tcx.hir().body_owners() { let imports = tcx.used_trait_imports(item_def_id); debug!("GatherVisitor: item_def_id={:?} with imports {:#?}", item_def_id, imports); - used_trait_imports.extend(imports.iter()); + used_trait_imports.extend(imports.items().copied()); } for &id in tcx.maybe_unused_trait_imports(()) { @@ -29,14 +30,18 @@ pub fn check_crate(tcx: TyCtxt<'_>) { continue; } let hir::ItemKind::Use(path, _) = item.kind else { unreachable!() }; - tcx.struct_span_lint_hir(lint::builtin::UNUSED_IMPORTS, item.hir_id(), path.span, |lint| { - let msg = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) { - format!("unused import: `{}`", snippet) - } else { - "unused import".to_owned() - }; - lint.build(&msg).emit(); - }); + let msg = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) { + format!("unused import: `{}`", snippet) + } else { + "unused import".to_owned() + }; + tcx.struct_span_lint_hir( + lint::builtin::UNUSED_IMPORTS, + item.hir_id(), + path.span, + msg, + |lint| lint, + ); } unused_crates_lint(tcx); @@ -85,11 +90,11 @@ fn unused_crates_lint(tcx: TyCtxt<'_>) { let mut crates_to_lint = vec![]; for id in tcx.hir().items() { - if matches!(tcx.def_kind(id.def_id), DefKind::ExternCrate) { + if matches!(tcx.def_kind(id.owner_id), DefKind::ExternCrate) { let item = tcx.hir().item(id); if let hir::ItemKind::ExternCrate(orig_name) = item.kind { crates_to_lint.push(ExternCrateToLint { - def_id: item.def_id.to_def_id(), + def_id: item.owner_id.to_def_id(), span: item.span, orig_name, warn_if_unused: !item.ident.as_str().starts_with('_'), diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs index 52aad636f..2890c149b 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs @@ -58,7 +58,7 @@ const ADD_ATTR: &str = impl<'tcx> InherentCollect<'tcx> { fn check_def_id(&mut self, item: &hir::Item<'_>, self_ty: Ty<'tcx>, def_id: DefId) { - let impl_def_id = item.def_id; + let impl_def_id = item.owner_id; if let Some(def_id) = def_id.as_local() { // Add the implementation to the mapping from implementation to base // type def ID, if there is a base type for this implementation and @@ -89,7 +89,7 @@ impl<'tcx> InherentCollect<'tcx> { for impl_item in items { if !self .tcx - .has_attr(impl_item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl) + .has_attr(impl_item.id.owner_id.to_def_id(), sym::rustc_allow_incoherent_impl) { struct_span_err!( self.tcx.sess, @@ -105,7 +105,7 @@ impl<'tcx> InherentCollect<'tcx> { } if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) { - self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id); + self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id.def_id); } else { bug!("unexpected self type: {:?}", self_ty); } @@ -135,7 +135,7 @@ impl<'tcx> InherentCollect<'tcx> { for item in items { if !self .tcx - .has_attr(item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl) + .has_attr(item.id.owner_id.to_def_id(), sym::rustc_allow_incoherent_impl) { struct_span_err!( self.tcx.sess, @@ -177,7 +177,7 @@ impl<'tcx> InherentCollect<'tcx> { } fn check_item(&mut self, id: hir::ItemId) { - if !matches!(self.tcx.def_kind(id.def_id), DefKind::Impl) { + if !matches!(self.tcx.def_kind(id.owner_id), DefKind::Impl) { return; } @@ -186,7 +186,7 @@ impl<'tcx> InherentCollect<'tcx> { return; }; - let self_ty = self.tcx.type_of(item.def_id); + let self_ty = self.tcx.type_of(item.owner_id); match *self_ty.kind() { ty::Adt(def, _) => { self.check_def_id(item, self_ty, def.did()); @@ -220,7 +220,9 @@ impl<'tcx> InherentCollect<'tcx> { | ty::Ref(..) | ty::Never | ty::FnPtr(_) - | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span), + | ty::Tuple(..) => { + self.check_primitive_impl(item.owner_id.def_id, self_ty, items, ty.span) + } ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => { let mut err = struct_span_err!( self.tcx.sess, @@ -241,7 +243,7 @@ impl<'tcx> InherentCollect<'tcx> { | ty::Bound(..) | ty::Placeholder(_) | ty::Infer(_) => { - bug!("unexpected impl self type of impl: {:?} {:?}", item.def_id, self_ty); + bug!("unexpected impl self type of impl: {:?} {:?}", item.owner_id, self_ty); } ty::Error(_) => {} } diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs index 03e076bf5..972769eb1 100644 --- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs +++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs @@ -58,6 +58,37 @@ impl<'tcx> InherentOverlapChecker<'tcx> { == item2.ident(self.tcx).normalize_to_macros_2_0() } + fn check_for_duplicate_items_in_impl(&self, impl_: DefId) { + let impl_items = self.tcx.associated_items(impl_); + + let mut seen_items = FxHashMap::default(); + for impl_item in impl_items.in_definition_order() { + let span = self.tcx.def_span(impl_item.def_id); + let ident = impl_item.ident(self.tcx); + + let norm_ident = ident.normalize_to_macros_2_0(); + match seen_items.entry(norm_ident) { + Entry::Occupied(entry) => { + let former = entry.get(); + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0592, + "duplicate definitions with name `{}`", + ident, + ); + err.span_label(span, format!("duplicate definitions for `{}`", ident)); + err.span_label(*former, format!("other definition for `{}`", ident)); + + err.emit(); + } + Entry::Vacant(entry) => { + entry.insert(span); + } + } + } + } + fn check_for_common_items_in_impls( &self, impl1: DefId, @@ -117,29 +148,22 @@ impl<'tcx> InherentOverlapChecker<'tcx> { // inherent impls without warning. SkipLeakCheck::Yes, overlap_mode, - |overlap| { - self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap); - false - }, - || true, - ); + ) + .map_or(true, |overlap| { + self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap); + false + }); } fn check_item(&mut self, id: hir::ItemId) { - let def_kind = self.tcx.def_kind(id.def_id); + let def_kind = self.tcx.def_kind(id.owner_id); if !matches!(def_kind, DefKind::Enum | DefKind::Struct | DefKind::Trait | DefKind::Union) { return; } - let impls = self.tcx.inherent_impls(id.def_id); + let impls = self.tcx.inherent_impls(id.owner_id); - // If there is only one inherent impl block, - // there is nothing to overlap check it with - if impls.len() <= 1 { - return; - } - - let overlap_mode = OverlapMode::get(self.tcx, id.def_id.to_def_id()); + let overlap_mode = OverlapMode::get(self.tcx, id.owner_id.to_def_id()); let impls_items = impls .iter() @@ -152,6 +176,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> { const ALLOCATING_ALGO_THRESHOLD: usize = 500; if impls.len() < ALLOCATING_ALGO_THRESHOLD { for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() { + self.check_for_duplicate_items_in_impl(impl1_def_id); + for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] { if self.impls_have_common_items(impl_items1, impl_items2) { self.check_for_overlapping_inherent_impls( @@ -290,6 +316,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> { impl_blocks.sort_unstable(); for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() { let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx]; + self.check_for_duplicate_items_in_impl(impl1_def_id); + for &impl2_items_idx in impl_blocks[(i + 1)..].iter() { let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx]; if self.impls_have_common_items(impl_items1, impl_items2) { diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs index ae9ebe590..ae9ebe590 100644 --- a/compiler/rustc_typeck/src/coherence/mod.rs +++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index 1608550aa..bb45c3823 100644 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -2,10 +2,9 @@ //! crate or pertains to a type defined in this crate. use rustc_data_structures::fx::FxHashSet; -use rustc_errors::struct_span_err; +use rustc_errors::{struct_span_err, DelayDm}; use rustc_errors::{Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::ty::subst::GenericArgKind; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::IgnoreRegions; @@ -43,7 +42,7 @@ fn do_orphan_check_impl<'tcx>( ) -> Result<(), ErrorGuaranteed> { let trait_def_id = trait_ref.def_id; - let item = tcx.hir().item(hir::ItemId { def_id }); + let item = tcx.hir().expect_item(def_id); let hir::ItemKind::Impl(ref impl_) = item.kind else { bug!("{:?} is not an impl: {:?}", def_id, item); }; @@ -102,7 +101,7 @@ fn do_orphan_check_impl<'tcx>( span_bug!(sp, "opaque type not found, but `has_opaque_types` is set") } - match traits::orphan_check(tcx, item.def_id.to_def_id()) { + match traits::orphan_check(tcx, item.owner_id.to_def_id()) { Ok(()) => {} Err(err) => emit_orphan_check_error( tcx, @@ -229,12 +228,8 @@ fn emit_orphan_check_error<'tcx>( "only traits defined in the current crate {msg}" ); err.span_label(sp, "impl doesn't use only types from inside the current crate"); - for (ty, is_target_ty) in &tys { - let mut ty = *ty; - tcx.infer_ctxt().enter(|infcx| { - // Remove the lifetimes unnecessary for this error. - ty = infcx.freshen(ty); - }); + for &(mut ty, is_target_ty) in &tys { + ty = tcx.erase_regions(ty); ty = match ty.kind() { // Remove the type arguments from the output, as they are not relevant. // You can think of this as the reverse of `resolve_vars_if_possible`. @@ -264,7 +259,7 @@ fn emit_orphan_check_error<'tcx>( }; let msg = format!("{} is not defined in the current crate{}", ty, postfix); - if *is_target_ty { + if is_target_ty { // Point at `D<A>` in `impl<A, B> for C<B> in D<A>` err.span_label(self_ty_span, &msg); } else { @@ -417,30 +412,31 @@ fn lint_auto_trait_impl<'tcx>( lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS, tcx.hir().local_def_id_to_hir_id(impl_def_id), tcx.def_span(impl_def_id), - |err| { - let item_span = tcx.def_span(self_type_did); - let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); - let mut err = err.build(&format!( + DelayDm(|| { + format!( "cross-crate traits with a default impl, like `{}`, \ should not be specialized", tcx.def_path_str(trait_ref.def_id), - )); + ) + }), + |lint| { + let item_span = tcx.def_span(self_type_did); + let self_descr = tcx.def_kind(self_type_did).descr(self_type_did); match arg { ty::util::NotUniqueParam::DuplicateParam(arg) => { - err.note(&format!("`{}` is mentioned multiple times", arg)); + lint.note(&format!("`{}` is mentioned multiple times", arg)); } ty::util::NotUniqueParam::NotParam(arg) => { - err.note(&format!("`{}` is not a generic parameter", arg)); + lint.note(&format!("`{}` is not a generic parameter", arg)); } } - err.span_note( + lint.span_note( item_span, &format!( "try using the same sequence of generic parameters as the {} definition", self_descr, ), - ); - err.emit(); + ) }, ); } diff --git a/compiler/rustc_typeck/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs index e45fb5fe4..a34815b45 100644 --- a/compiler/rustc_typeck/src/coherence/unsafety.rs +++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs @@ -13,7 +13,7 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { let item = tcx.hir().expect_item(def_id); let hir::ItemKind::Impl(ref impl_) = item.kind else { bug!() }; - if let Some(trait_ref) = tcx.impl_trait_ref(item.def_id) { + if let Some(trait_ref) = tcx.impl_trait_ref(item.owner_id) { let trait_def = tcx.trait_def(trait_ref.def_id); let unsafe_attr = impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle"); @@ -26,6 +26,12 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "implementing the trait `{}` is not unsafe", trait_ref.print_only_trait_path() ) + .span_suggestion_verbose( + item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)), + "remove `unsafe` from this trait implementation", + "", + rustc_errors::Applicability::MachineApplicable, + ) .emit(); } @@ -37,6 +43,18 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "the trait `{}` requires an `unsafe impl` declaration", trait_ref.print_only_trait_path() ) + .note(format!( + "the trait `{}` enforces invariants that the compiler can't check. \ + Review the trait documentation and make sure this implementation \ + upholds those invariants before adding the `unsafe` keyword", + trait_ref.print_only_trait_path() + )) + .span_suggestion_verbose( + item.span.shrink_to_lo(), + "add `unsafe` to this trait implementation", + "unsafe ", + rustc_errors::Applicability::MaybeIncorrect, + ) .emit(); } @@ -48,6 +66,18 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) { "requires an `unsafe impl` declaration due to `#[{}]` attribute", attr_name ) + .note(format!( + "the trait `{}` enforces invariants that the compiler can't check. \ + Review the trait documentation and make sure this implementation \ + upholds those invariants before adding the `unsafe` keyword", + trait_ref.print_only_trait_path() + )) + .span_suggestion_verbose( + item.span.shrink_to_lo(), + "add `unsafe` to this trait implementation", + "unsafe ", + rustc_errors::Applicability::MaybeIncorrect, + ) .emit(); } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 45a5eca70..346d2e2fc 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -15,46 +15,41 @@ //! crate as a kind of pass. This should eventually be factored away. use crate::astconv::AstConv; -use crate::bounds::Bounds; use crate::check::intrinsic::intrinsic_operation_unsafety; -use crate::constrained_generic_params as cgp; use crate::errors; -use crate::middle::resolve_lifetime as rl; use rustc_ast as ast; use rustc_ast::{MetaItemKind, NestedMetaItem}; use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr}; use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey}; use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind}; -use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; +use rustc_hir::def::CtorKind; +use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::weak_lang_items; -use rustc_hir::{GenericParamKind, HirId, Node}; +use rustc_hir::{GenericParamKind, Node}; use rustc_middle::hir::nested_filter; use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc_middle::mir::mono::Linkage; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::util::Discr; -use rustc_middle::ty::util::IntTypeExt; +use rustc_middle::ty::util::{Discr, IntTypeExt}; +use rustc_middle::ty::ReprOptions; use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, IsSuggestable, Ty, TyCtxt}; -use rustc_middle::ty::{ReprOptions, ToPredicate}; use rustc_session::lint; use rustc_session::parse::feature_err; use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::Span; use rustc_target::spec::{abi, SanitizerSet}; use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; use std::iter; +mod generics_of; mod item_bounds; +mod lifetimes; +mod predicates_of; mod type_of; -#[derive(Debug)] -struct OnlySelfBounds(bool); - /////////////////////////////////////////////////////////////////////////// // Main entry point @@ -63,19 +58,21 @@ fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { } pub fn provide(providers: &mut Providers) { + lifetimes::provide(providers); *providers = Providers { opt_const_param_of: type_of::opt_const_param_of, type_of: type_of::type_of, item_bounds: item_bounds::item_bounds, explicit_item_bounds: item_bounds::explicit_item_bounds, - generics_of, - predicates_of, + generics_of: generics_of::generics_of, + predicates_of: predicates_of::predicates_of, predicates_defined_on, - explicit_predicates_of, - super_predicates_of, - super_predicates_that_define_assoc_type, - trait_explicit_predicates_and_bounds, - type_param_predicates, + explicit_predicates_of: predicates_of::explicit_predicates_of, + super_predicates_of: predicates_of::super_predicates_of, + super_predicates_that_define_assoc_type: + predicates_of::super_predicates_that_define_assoc_type, + trait_explicit_predicates_and_bounds: predicates_of::trait_explicit_predicates_and_bounds, + type_param_predicates: predicates_of::type_param_predicates, trait_def, adt_def, fn_sig, @@ -103,13 +100,12 @@ pub fn provide(providers: &mut Providers) { /// It's also used for the bodies of items like structs where the body (the fields) /// are just signatures. /// -/// This is in contrast to [`FnCtxt`], which is used to type-check bodies of +/// This is in contrast to `FnCtxt`, which is used to type-check bodies of /// functions, closures, and `const`s -- anywhere that expressions and statements show up. /// /// An important thing to note is that `ItemCtxt` does no inference -- it has no [`InferCtxt`] -- /// while `FnCtxt` does do inference. /// -/// [`FnCtxt`]: crate::check::FnCtxt /// [`InferCtxt`]: rustc_infer::infer::InferCtxt /// /// # Trait predicates @@ -430,7 +426,6 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { if let Some(trait_ref) = poly_trait_ref.no_bound_vars() { let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item( self, - self.tcx, span, item_def_id, item_segment, @@ -449,8 +444,10 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> { match self.node() { hir::Node::Field(_) | hir::Node::Ctor(_) | hir::Node::Variant(_) => { - let item = - self.tcx.hir().expect_item(self.tcx.hir().get_parent_item(self.hir_id())); + let item = self + .tcx + .hir() + .expect_item(self.tcx.hir().get_parent_item(self.hir_id()).def_id); match &item.kind { hir::ItemKind::Enum(_, generics) | hir::ItemKind::Struct(_, generics) @@ -571,164 +568,10 @@ fn get_new_lifetime_name<'tcx>( (1..).flat_map(a_to_z_repeat_n).find(|lt| !existing_lifetimes.contains(lt.as_str())).unwrap() } -/// Returns the predicates defined on `item_def_id` of the form -/// `X: Foo` where `X` is the type parameter `def_id`. -#[instrument(level = "trace", skip(tcx))] -fn type_param_predicates( - tcx: TyCtxt<'_>, - (item_def_id, def_id, assoc_name): (DefId, LocalDefId, Ident), -) -> ty::GenericPredicates<'_> { - use rustc_hir::*; - - // In the AST, bounds can derive from two places. Either - // written inline like `<T: Foo>` or in a where-clause like - // `where T: Foo`. - - let param_id = tcx.hir().local_def_id_to_hir_id(def_id); - let param_owner = tcx.hir().ty_param_owner(def_id); - let generics = tcx.generics_of(param_owner); - let index = generics.param_def_id_to_index[&def_id.to_def_id()]; - let ty = tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id)); - - // Don't look for bounds where the type parameter isn't in scope. - let parent = if item_def_id == param_owner.to_def_id() { - None - } else { - tcx.generics_of(item_def_id).parent - }; - - let mut result = parent - .map(|parent| { - let icx = ItemCtxt::new(tcx, parent); - icx.get_type_parameter_bounds(DUMMY_SP, def_id.to_def_id(), assoc_name) - }) - .unwrap_or_default(); - let mut extend = None; - - let item_hir_id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()); - let ast_generics = match tcx.hir().get(item_hir_id) { - Node::TraitItem(item) => &item.generics, - - Node::ImplItem(item) => &item.generics, - - Node::Item(item) => { - match item.kind { - ItemKind::Fn(.., ref generics, _) - | ItemKind::Impl(hir::Impl { ref generics, .. }) - | ItemKind::TyAlias(_, ref generics) - | ItemKind::OpaqueTy(OpaqueTy { - ref generics, - origin: hir::OpaqueTyOrigin::TyAlias, - .. - }) - | ItemKind::Enum(_, ref generics) - | ItemKind::Struct(_, ref generics) - | ItemKind::Union(_, ref generics) => generics, - ItemKind::Trait(_, _, ref generics, ..) => { - // Implied `Self: Trait` and supertrait bounds. - if param_id == item_hir_id { - let identity_trait_ref = ty::TraitRef::identity(tcx, item_def_id); - extend = - Some((identity_trait_ref.without_const().to_predicate(tcx), item.span)); - } - generics - } - _ => return result, - } - } - - Node::ForeignItem(item) => match item.kind { - ForeignItemKind::Fn(_, _, ref generics) => generics, - _ => return result, - }, - - _ => return result, - }; - - let icx = ItemCtxt::new(tcx, item_def_id); - let extra_predicates = extend.into_iter().chain( - icx.type_parameter_bounds_in_generics( - ast_generics, - param_id, - ty, - OnlySelfBounds(true), - Some(assoc_name), - ) - .into_iter() - .filter(|(predicate, _)| match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(data) => data.self_ty().is_param(index), - _ => false, - }), - ); - result.predicates = - tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(extra_predicates)); - result -} - -impl<'tcx> ItemCtxt<'tcx> { - /// Finds bounds from `hir::Generics`. This requires scanning through the - /// AST. We do this to avoid having to convert *all* the bounds, which - /// would create artificial cycles. Instead, we can only convert the - /// bounds for a type parameter `X` if `X::Foo` is used. - #[instrument(level = "trace", skip(self, ast_generics))] - fn type_parameter_bounds_in_generics( - &self, - ast_generics: &'tcx hir::Generics<'tcx>, - param_id: hir::HirId, - ty: Ty<'tcx>, - only_self_bounds: OnlySelfBounds, - assoc_name: Option<Ident>, - ) -> Vec<(ty::Predicate<'tcx>, Span)> { - let param_def_id = self.tcx.hir().local_def_id(param_id).to_def_id(); - trace!(?param_def_id); - ast_generics - .predicates - .iter() - .filter_map(|wp| match *wp { - hir::WherePredicate::BoundPredicate(ref bp) => Some(bp), - _ => None, - }) - .flat_map(|bp| { - let bt = if bp.is_param_bound(param_def_id) { - Some(ty) - } else if !only_self_bounds.0 { - Some(self.to_ty(bp.bounded_ty)) - } else { - None - }; - let bvars = self.tcx.late_bound_vars(bp.bounded_ty.hir_id); - - bp.bounds.iter().filter_map(move |b| bt.map(|bt| (bt, b, bvars))).filter( - |(_, b, _)| match assoc_name { - Some(assoc_name) => self.bound_defines_assoc_item(b, assoc_name), - None => true, - }, - ) - }) - .flat_map(|(bt, b, bvars)| predicates_from_bound(self, bt, b, bvars)) - .collect() - } - - #[instrument(level = "trace", skip(self))] - fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Ident) -> bool { - match b { - hir::GenericBound::Trait(poly_trait_ref, _) => { - let trait_ref = &poly_trait_ref.trait_ref; - if let Some(trait_did) = trait_ref.trait_def_id() { - self.tcx.trait_may_define_assoc_type(trait_did, assoc_name) - } else { - false - } - } - _ => false, - } - } -} - fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { let it = tcx.hir().item(item_id); debug!("convert: item {} with id {}", it.ident, it.hir_id()); - let def_id = item_id.def_id; + let def_id = item_id.owner_id.def_id; match it.kind { // These don't define types. @@ -740,11 +583,11 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { hir::ItemKind::ForeignMod { items, .. } => { for item in items { let item = tcx.hir().foreign_item(item.id); - tcx.ensure().generics_of(item.def_id); - tcx.ensure().type_of(item.def_id); - tcx.ensure().predicates_of(item.def_id); + tcx.ensure().generics_of(item.owner_id); + tcx.ensure().type_of(item.owner_id); + tcx.ensure().predicates_of(item.owner_id); match item.kind { - hir::ForeignItemKind::Fn(..) => tcx.ensure().fn_sig(item.def_id), + hir::ForeignItemKind::Fn(..) => tcx.ensure().fn_sig(item.owner_id), hir::ForeignItemKind::Static(..) => { let mut visitor = HirPlaceholderCollector::default(); visitor.visit_foreign_item(item); @@ -840,20 +683,21 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) { fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { let trait_item = tcx.hir().trait_item(trait_item_id); - tcx.ensure().generics_of(trait_item_id.def_id); + let def_id = trait_item_id.owner_id; + tcx.ensure().generics_of(def_id); match trait_item.kind { hir::TraitItemKind::Fn(..) => { - tcx.ensure().type_of(trait_item_id.def_id); - tcx.ensure().fn_sig(trait_item_id.def_id); + tcx.ensure().type_of(def_id); + tcx.ensure().fn_sig(def_id); } hir::TraitItemKind::Const(.., Some(_)) => { - tcx.ensure().type_of(trait_item_id.def_id); + tcx.ensure().type_of(def_id); } hir::TraitItemKind::Const(hir_ty, _) => { - tcx.ensure().type_of(trait_item_id.def_id); + tcx.ensure().type_of(def_id); // Account for `const C: _;`. let mut visitor = HirPlaceholderCollector::default(); visitor.visit_trait_item(trait_item); @@ -863,8 +707,8 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { } hir::TraitItemKind::Type(_, Some(_)) => { - tcx.ensure().item_bounds(trait_item_id.def_id); - tcx.ensure().type_of(trait_item_id.def_id); + tcx.ensure().item_bounds(def_id); + tcx.ensure().type_of(def_id); // Account for `type T = _;`. let mut visitor = HirPlaceholderCollector::default(); visitor.visit_trait_item(trait_item); @@ -872,7 +716,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { } hir::TraitItemKind::Type(_, None) => { - tcx.ensure().item_bounds(trait_item_id.def_id); + tcx.ensure().item_bounds(def_id); // #74612: Visit and try to find bad placeholders // even if there is no concrete type. let mut visitor = HirPlaceholderCollector::default(); @@ -882,11 +726,11 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { } }; - tcx.ensure().predicates_of(trait_item_id.def_id); + tcx.ensure().predicates_of(def_id); } fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { - let def_id = impl_item_id.def_id; + let def_id = impl_item_id.owner_id; tcx.ensure().generics_of(def_id); tcx.ensure().type_of(def_id); tcx.ensure().predicates_of(def_id); @@ -895,7 +739,7 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { hir::ImplItemKind::Fn(..) => { tcx.ensure().fn_sig(def_id); } - hir::ImplItemKind::TyAlias(_) => { + hir::ImplItemKind::Type(_) => { // Account for `type T = _;` let mut visitor = HirPlaceholderCollector::default(); visitor.visit_impl_item(impl_item); @@ -1095,96 +939,6 @@ fn adt_def<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AdtDef<'tcx> { tcx.alloc_adt_def(def_id.to_def_id(), kind, variants, repr) } -/// Ensures that the super-predicates of the trait with a `DefId` -/// of `trait_def_id` are converted and stored. This also ensures that -/// the transitive super-predicates are converted. -fn super_predicates_of(tcx: TyCtxt<'_>, trait_def_id: DefId) -> ty::GenericPredicates<'_> { - debug!("super_predicates(trait_def_id={:?})", trait_def_id); - tcx.super_predicates_that_define_assoc_type((trait_def_id, None)) -} - -/// Ensures that the super-predicates of the trait with a `DefId` -/// of `trait_def_id` are converted and stored. This also ensures that -/// the transitive super-predicates are converted. -fn super_predicates_that_define_assoc_type( - tcx: TyCtxt<'_>, - (trait_def_id, assoc_name): (DefId, Option<Ident>), -) -> ty::GenericPredicates<'_> { - debug!( - "super_predicates_that_define_assoc_type(trait_def_id={:?}, assoc_name={:?})", - trait_def_id, assoc_name - ); - if trait_def_id.is_local() { - debug!("super_predicates_that_define_assoc_type: local trait_def_id={:?}", trait_def_id); - let trait_hir_id = tcx.hir().local_def_id_to_hir_id(trait_def_id.expect_local()); - - let Node::Item(item) = tcx.hir().get(trait_hir_id) else { - bug!("trait_node_id {} is not an item", trait_hir_id); - }; - - let (generics, bounds) = match item.kind { - hir::ItemKind::Trait(.., ref generics, ref supertraits, _) => (generics, supertraits), - hir::ItemKind::TraitAlias(ref generics, ref supertraits) => (generics, supertraits), - _ => span_bug!(item.span, "super_predicates invoked on non-trait"), - }; - - let icx = ItemCtxt::new(tcx, trait_def_id); - - // Convert the bounds that follow the colon, e.g., `Bar + Zed` in `trait Foo: Bar + Zed`. - let self_param_ty = tcx.types.self_param; - let superbounds1 = if let Some(assoc_name) = assoc_name { - <dyn AstConv<'_>>::compute_bounds_that_match_assoc_type( - &icx, - self_param_ty, - bounds, - assoc_name, - ) - } else { - <dyn AstConv<'_>>::compute_bounds(&icx, self_param_ty, bounds) - }; - - let superbounds1 = superbounds1.predicates(tcx, self_param_ty); - - // Convert any explicit superbounds in the where-clause, - // e.g., `trait Foo where Self: Bar`. - // In the case of trait aliases, however, we include all bounds in the where-clause, - // so e.g., `trait Foo = where u32: PartialEq<Self>` would include `u32: PartialEq<Self>` - // as one of its "superpredicates". - let is_trait_alias = tcx.is_trait_alias(trait_def_id); - let superbounds2 = icx.type_parameter_bounds_in_generics( - generics, - item.hir_id(), - self_param_ty, - OnlySelfBounds(!is_trait_alias), - assoc_name, - ); - - // Combine the two lists to form the complete set of superbounds: - let superbounds = &*tcx.arena.alloc_from_iter(superbounds1.into_iter().chain(superbounds2)); - debug!(?superbounds); - - // Now require that immediate supertraits are converted, - // which will, in turn, reach indirect supertraits. - if assoc_name.is_none() { - // Now require that immediate supertraits are converted, - // which will, in turn, reach indirect supertraits. - for &(pred, span) in superbounds { - debug!("superbound: {:?}", pred); - if let ty::PredicateKind::Trait(bound) = pred.kind().skip_binder() { - tcx.at(span).super_predicates_of(bound.def_id()); - } - } - } - - ty::GenericPredicates { parent: None, predicates: superbounds } - } else { - // if `assoc_name` is None, then the query should've been redirected to an - // external provider - assert!(assoc_name.is_some()); - tcx.super_predicates_of(trait_def_id) - } -} - fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef { let item = tcx.hir().expect_item(def_id.expect_local()); @@ -1256,7 +1010,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef { match item { Some(item) if matches!(item.kind, hir::AssocItemKind::Fn { .. }) => { - if !tcx.impl_defaultness(item.id.def_id).has_value() { + if !tcx.impl_defaultness(item.id.owner_id).has_value() { tcx.sess .struct_span_err( item.span, @@ -1326,475 +1080,6 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef { ) } -fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<Span> { - struct LateBoundRegionsDetector<'tcx> { - tcx: TyCtxt<'tcx>, - outer_index: ty::DebruijnIndex, - has_late_bound_regions: Option<Span>, - } - - impl<'tcx> Visitor<'tcx> for LateBoundRegionsDetector<'tcx> { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - if self.has_late_bound_regions.is_some() { - return; - } - match ty.kind { - hir::TyKind::BareFn(..) => { - self.outer_index.shift_in(1); - intravisit::walk_ty(self, ty); - self.outer_index.shift_out(1); - } - _ => intravisit::walk_ty(self, ty), - } - } - - fn visit_poly_trait_ref(&mut self, tr: &'tcx hir::PolyTraitRef<'tcx>) { - if self.has_late_bound_regions.is_some() { - return; - } - self.outer_index.shift_in(1); - intravisit::walk_poly_trait_ref(self, tr); - self.outer_index.shift_out(1); - } - - fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) { - if self.has_late_bound_regions.is_some() { - return; - } - - match self.tcx.named_region(lt.hir_id) { - Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {} - Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {} - Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => { - self.has_late_bound_regions = Some(lt.span); - } - } - } - } - - fn has_late_bound_regions<'tcx>( - tcx: TyCtxt<'tcx>, - generics: &'tcx hir::Generics<'tcx>, - decl: &'tcx hir::FnDecl<'tcx>, - ) -> Option<Span> { - let mut visitor = LateBoundRegionsDetector { - tcx, - outer_index: ty::INNERMOST, - has_late_bound_regions: None, - }; - for param in generics.params { - if let GenericParamKind::Lifetime { .. } = param.kind { - if tcx.is_late_bound(param.hir_id) { - return Some(param.span); - } - } - } - visitor.visit_fn_decl(decl); - visitor.has_late_bound_regions - } - - match node { - Node::TraitItem(item) => match item.kind { - hir::TraitItemKind::Fn(ref sig, _) => { - has_late_bound_regions(tcx, &item.generics, sig.decl) - } - _ => None, - }, - Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Fn(ref sig, _) => { - has_late_bound_regions(tcx, &item.generics, sig.decl) - } - _ => None, - }, - Node::ForeignItem(item) => match item.kind { - hir::ForeignItemKind::Fn(fn_decl, _, ref generics) => { - has_late_bound_regions(tcx, generics, fn_decl) - } - _ => None, - }, - Node::Item(item) => match item.kind { - hir::ItemKind::Fn(ref sig, .., ref generics, _) => { - has_late_bound_regions(tcx, generics, sig.decl) - } - _ => None, - }, - _ => None, - } -} - -struct AnonConstInParamTyDetector { - in_param_ty: bool, - found_anon_const_in_param_ty: bool, - ct: HirId, -} - -impl<'v> Visitor<'v> for AnonConstInParamTyDetector { - fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) { - if let GenericParamKind::Const { ty, default: _ } = p.kind { - let prev = self.in_param_ty; - self.in_param_ty = true; - self.visit_ty(ty); - self.in_param_ty = prev; - } - } - - fn visit_anon_const(&mut self, c: &'v hir::AnonConst) { - if self.in_param_ty && self.ct == c.hir_id { - self.found_anon_const_in_param_ty = true; - } else { - intravisit::walk_anon_const(self, c) - } - } -} - -fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { - use rustc_hir::*; - - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - - let node = tcx.hir().get(hir_id); - let parent_def_id = match node { - Node::ImplItem(_) - | Node::TraitItem(_) - | Node::Variant(_) - | Node::Ctor(..) - | Node::Field(_) => { - let parent_id = tcx.hir().get_parent_item(hir_id); - Some(parent_id.to_def_id()) - } - // FIXME(#43408) always enable this once `lazy_normalization` is - // stable enough and does not need a feature gate anymore. - Node::AnonConst(_) => { - let parent_def_id = tcx.hir().get_parent_item(hir_id); - - let mut in_param_ty = false; - for (_parent, node) in tcx.hir().parent_iter(hir_id) { - if let Some(generics) = node.generics() { - let mut visitor = AnonConstInParamTyDetector { - in_param_ty: false, - found_anon_const_in_param_ty: false, - ct: hir_id, - }; - - visitor.visit_generics(generics); - in_param_ty = visitor.found_anon_const_in_param_ty; - break; - } - } - - if in_param_ty { - // We do not allow generic parameters in anon consts if we are inside - // of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed. - None - } else if tcx.lazy_normalization() { - if let Some(param_id) = tcx.hir().opt_const_param_default_param_hir_id(hir_id) { - // If the def_id we are calling generics_of on is an anon ct default i.e: - // - // struct Foo<const N: usize = { .. }>; - // ^^^ ^ ^^^^^^ def id of this anon const - // ^ ^ param_id - // ^ parent_def_id - // - // then we only want to return generics for params to the left of `N`. If we don't do that we - // end up with that const looking like: `ty::ConstKind::Unevaluated(def_id, substs: [N#0])`. - // - // This causes ICEs (#86580) when building the substs for Foo in `fn foo() -> Foo { .. }` as - // we substitute the defaults with the partially built substs when we build the substs. Subst'ing - // the `N#0` on the unevaluated const indexes into the empty substs we're in the process of building. - // - // We fix this by having this function return the parent's generics ourselves and truncating the - // generics to only include non-forward declared params (with the exception of the `Self` ty) - // - // For the above code example that means we want `substs: []` - // For the following struct def we want `substs: [N#0]` when generics_of is called on - // the def id of the `{ N + 1 }` anon const - // struct Foo<const N: usize, const M: usize = { N + 1 }>; - // - // This has some implications for how we get the predicates available to the anon const - // see `explicit_predicates_of` for more information on this - let generics = tcx.generics_of(parent_def_id.to_def_id()); - let param_def = tcx.hir().local_def_id(param_id).to_def_id(); - let param_def_idx = generics.param_def_id_to_index[¶m_def]; - // In the above example this would be .params[..N#0] - let params = generics.params[..param_def_idx as usize].to_owned(); - let param_def_id_to_index = - params.iter().map(|param| (param.def_id, param.index)).collect(); - - return ty::Generics { - // we set the parent of these generics to be our parent's parent so that we - // dont end up with substs: [N, M, N] for the const default on a struct like this: - // struct Foo<const N: usize, const M: usize = { ... }>; - parent: generics.parent, - parent_count: generics.parent_count, - params, - param_def_id_to_index, - has_self: generics.has_self, - has_late_bound_regions: generics.has_late_bound_regions, - }; - } - - // HACK(eddyb) this provides the correct generics when - // `feature(generic_const_expressions)` is enabled, so that const expressions - // used with const generics, e.g. `Foo<{N+1}>`, can work at all. - // - // Note that we do not supply the parent generics when using - // `min_const_generics`. - Some(parent_def_id.to_def_id()) - } else { - let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); - match parent_node { - // HACK(eddyb) this provides the correct generics for repeat - // expressions' count (i.e. `N` in `[x; N]`), and explicit - // `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`), - // as they shouldn't be able to cause query cycle errors. - Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) - if constant.hir_id() == hir_id => - { - Some(parent_def_id.to_def_id()) - } - Node::Variant(Variant { disr_expr: Some(ref constant), .. }) - if constant.hir_id == hir_id => - { - Some(parent_def_id.to_def_id()) - } - Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => { - Some(tcx.typeck_root_def_id(def_id)) - } - // Exclude `GlobalAsm` here which cannot have generics. - Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) - if asm.operands.iter().any(|(op, _op_sp)| match op { - hir::InlineAsmOperand::Const { anon_const } - | hir::InlineAsmOperand::SymFn { anon_const } => { - anon_const.hir_id == hir_id - } - _ => false, - }) => - { - Some(parent_def_id.to_def_id()) - } - _ => None, - } - } - } - Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { - Some(tcx.typeck_root_def_id(def_id)) - } - Node::Item(item) => match item.kind { - ItemKind::OpaqueTy(hir::OpaqueTy { - origin: - hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id), - in_trait, - .. - }) => { - if in_trait { - assert!(matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn)) - } else { - assert!(matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn | DefKind::Fn)) - } - Some(fn_def_id.to_def_id()) - } - ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => { - let parent_id = tcx.hir().get_parent_item(hir_id); - assert_ne!(parent_id, CRATE_DEF_ID); - debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id); - // Opaque types are always nested within another item, and - // inherit the generics of the item. - Some(parent_id.to_def_id()) - } - _ => None, - }, - _ => None, - }; - - enum Defaults { - Allowed, - // See #36887 - FutureCompatDisallowed, - Deny, - } - - let no_generics = hir::Generics::empty(); - let ast_generics = node.generics().unwrap_or(&no_generics); - let (opt_self, allow_defaults) = match node { - Node::Item(item) => { - match item.kind { - ItemKind::Trait(..) | ItemKind::TraitAlias(..) => { - // Add in the self type parameter. - // - // Something of a hack: use the node id for the trait, also as - // the node id for the Self type parameter. - let opt_self = Some(ty::GenericParamDef { - index: 0, - name: kw::SelfUpper, - def_id, - pure_wrt_drop: false, - kind: ty::GenericParamDefKind::Type { - has_default: false, - synthetic: false, - }, - }); - - (opt_self, Defaults::Allowed) - } - ItemKind::TyAlias(..) - | ItemKind::Enum(..) - | ItemKind::Struct(..) - | ItemKind::OpaqueTy(..) - | ItemKind::Union(..) => (None, Defaults::Allowed), - _ => (None, Defaults::FutureCompatDisallowed), - } - } - - // GATs - Node::TraitItem(item) if matches!(item.kind, TraitItemKind::Type(..)) => { - (None, Defaults::Deny) - } - Node::ImplItem(item) if matches!(item.kind, ImplItemKind::TyAlias(..)) => { - (None, Defaults::Deny) - } - - _ => (None, Defaults::FutureCompatDisallowed), - }; - - let has_self = opt_self.is_some(); - let mut parent_has_self = false; - let mut own_start = has_self as u32; - let parent_count = parent_def_id.map_or(0, |def_id| { - let generics = tcx.generics_of(def_id); - assert!(!has_self); - parent_has_self = generics.has_self; - own_start = generics.count() as u32; - generics.parent_count + generics.params.len() - }); - - let mut params: Vec<_> = Vec::with_capacity(ast_generics.params.len() + has_self as usize); - - if let Some(opt_self) = opt_self { - params.push(opt_self); - } - - let early_lifetimes = early_bound_lifetimes_from_generics(tcx, ast_generics); - params.extend(early_lifetimes.enumerate().map(|(i, param)| ty::GenericParamDef { - name: param.name.ident().name, - index: own_start + i as u32, - def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), - pure_wrt_drop: param.pure_wrt_drop, - kind: ty::GenericParamDefKind::Lifetime, - })); - - // Now create the real type and const parameters. - let type_start = own_start - has_self as u32 + params.len() as u32; - let mut i = 0; - - const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \ - `struct`, `enum`, `type`, or `trait` definitions"; - - params.extend(ast_generics.params.iter().filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => None, - GenericParamKind::Type { ref default, synthetic, .. } => { - if default.is_some() { - match allow_defaults { - Defaults::Allowed => {} - Defaults::FutureCompatDisallowed - if tcx.features().default_type_parameter_fallback => {} - Defaults::FutureCompatDisallowed => { - tcx.struct_span_lint_hir( - lint::builtin::INVALID_TYPE_PARAM_DEFAULT, - param.hir_id, - param.span, - |lint| { - lint.build(TYPE_DEFAULT_NOT_ALLOWED).emit(); - }, - ); - } - Defaults::Deny => { - tcx.sess.span_err(param.span, TYPE_DEFAULT_NOT_ALLOWED); - } - } - } - - let kind = ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic }; - - let param_def = ty::GenericParamDef { - index: type_start + i as u32, - name: param.name.ident().name, - def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), - pure_wrt_drop: param.pure_wrt_drop, - kind, - }; - i += 1; - Some(param_def) - } - GenericParamKind::Const { default, .. } => { - if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() { - tcx.sess.span_err( - param.span, - "defaults for const parameters are only allowed in \ - `struct`, `enum`, `type`, or `trait` definitions", - ); - } - - let param_def = ty::GenericParamDef { - index: type_start + i as u32, - name: param.name.ident().name, - def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(), - pure_wrt_drop: param.pure_wrt_drop, - kind: ty::GenericParamDefKind::Const { has_default: default.is_some() }, - }; - i += 1; - Some(param_def) - } - })); - - // provide junk type parameter defs - the only place that - // cares about anything but the length is instantiation, - // and we don't do that for closures. - if let Node::Expr(&hir::Expr { - kind: hir::ExprKind::Closure(hir::Closure { movability: gen, .. }), - .. - }) = node - { - let dummy_args = if gen.is_some() { - &["<resume_ty>", "<yield_ty>", "<return_ty>", "<witness>", "<upvars>"][..] - } else { - &["<closure_kind>", "<closure_signature>", "<upvars>"][..] - }; - - params.extend(dummy_args.iter().enumerate().map(|(i, &arg)| ty::GenericParamDef { - index: type_start + i as u32, - name: Symbol::intern(arg), - def_id, - pure_wrt_drop: false, - kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false }, - })); - } - - // provide junk type parameter defs for const blocks. - if let Node::AnonConst(_) = node { - let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); - if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node { - params.push(ty::GenericParamDef { - index: type_start, - name: Symbol::intern("<const_ty>"), - def_id, - pure_wrt_drop: false, - kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false }, - }); - } - } - - let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect(); - - ty::Generics { - parent: parent_def_id, - parent_count, - params, - param_def_id_to_index, - has_self: has_self || parent_has_self, - has_late_bound_regions: has_late_bound_regions(tcx, node), - } -} - fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool { generic_args.iter().any(|arg| match arg { hir::GenericArg::Type(ty) => is_suggestable_infer_ty(ty), @@ -1858,7 +1143,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> { } ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => { - // Do not try to inference the return type for a impl method coming from a trait + // Do not try to infer the return type for a impl method coming from a trait if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) = tcx.hir().get(tcx.hir().get_parent_node(hir_id)) && i.of_trait.is_some() @@ -2001,15 +1286,46 @@ fn infer_return_ty_for_fn_sig<'tcx>( fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> { let icx = ItemCtxt::new(tcx, def_id); - match tcx.hir().expect_item(def_id.expect_local()).kind { + let item = tcx.hir().expect_item(def_id.expect_local()); + match item.kind { hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| { let selfty = tcx.type_of(def_id); - <dyn AstConv<'_>>::instantiate_mono_trait_ref(&icx, ast_trait_ref, selfty) + <dyn AstConv<'_>>::instantiate_mono_trait_ref( + &icx, + ast_trait_ref, + selfty, + check_impl_constness(tcx, impl_.constness, ast_trait_ref), + ) }), _ => bug!(), } } +fn check_impl_constness( + tcx: TyCtxt<'_>, + constness: hir::Constness, + ast_trait_ref: &hir::TraitRef<'_>, +) -> ty::BoundConstness { + match constness { + hir::Constness::Const => { + if let Some(trait_def_id) = ast_trait_ref.trait_def_id() && !tcx.has_attr(trait_def_id, sym::const_trait) { + let trait_name = tcx.item_name(trait_def_id).to_string(); + tcx.sess.emit_err(errors::ConstImplForNonConstTrait { + trait_ref_span: ast_trait_ref.path.span, + trait_name, + local_trait_span: trait_def_id.as_local().map(|_| tcx.def_span(trait_def_id).shrink_to_lo()), + marking: (), + adding: (), + }); + ty::BoundConstness::NotConst + } else { + ty::BoundConstness::ConstIfConst + } + }, + hir::Constness::NotConst => ty::BoundConstness::NotConst, + } +} + fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ImplPolarity { let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl); let item = tcx.hir().expect_item(def_id.expect_local()); @@ -2091,451 +1407,6 @@ fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicate result } -/// Returns a list of all type predicates (explicit and implicit) for the definition with -/// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus -/// `Self: Trait` predicates for traits. -fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { - let mut result = tcx.predicates_defined_on(def_id); - - if tcx.is_trait(def_id) { - // For traits, add `Self: Trait` predicate. This is - // not part of the predicates that a user writes, but it - // is something that one must prove in order to invoke a - // method or project an associated type. - // - // In the chalk setup, this predicate is not part of the - // "predicates" for a trait item. But it is useful in - // rustc because if you directly (e.g.) invoke a trait - // method like `Trait::method(...)`, you must naturally - // prove that the trait applies to the types that were - // used, and adding the predicate into this list ensures - // that this is done. - // - // We use a DUMMY_SP here as a way to signal trait bounds that come - // from the trait itself that *shouldn't* be shown as the source of - // an obligation and instead be skipped. Otherwise we'd use - // `tcx.def_span(def_id);` - - let constness = if tcx.has_attr(def_id, sym::const_trait) { - ty::BoundConstness::ConstIfConst - } else { - ty::BoundConstness::NotConst - }; - - let span = rustc_span::DUMMY_SP; - result.predicates = - tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once(( - ty::TraitRef::identity(tcx, def_id).with_constness(constness).to_predicate(tcx), - span, - )))); - } - debug!("predicates_of(def_id={:?}) = {:?}", def_id, result); - result -} - -/// Returns a list of user-specified type predicates for the definition with ID `def_id`. -/// N.B., this does not include any implied/inferred constraints. -#[instrument(level = "trace", skip(tcx), ret)] -fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> { - use rustc_hir::*; - - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - let node = tcx.hir().get(hir_id); - - let mut is_trait = None; - let mut is_default_impl_trait = None; - - let icx = ItemCtxt::new(tcx, def_id); - - const NO_GENERICS: &hir::Generics<'_> = hir::Generics::empty(); - - // We use an `IndexSet` to preserves order of insertion. - // Preserving the order of insertion is important here so as not to break UI tests. - let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default(); - - let ast_generics = match node { - Node::TraitItem(item) => item.generics, - - Node::ImplItem(item) => item.generics, - - Node::Item(item) => { - match item.kind { - ItemKind::Impl(ref impl_) => { - if impl_.defaultness.is_default() { - is_default_impl_trait = tcx.impl_trait_ref(def_id).map(ty::Binder::dummy); - } - &impl_.generics - } - ItemKind::Fn(.., ref generics, _) - | ItemKind::TyAlias(_, ref generics) - | ItemKind::Enum(_, ref generics) - | ItemKind::Struct(_, ref generics) - | ItemKind::Union(_, ref generics) => *generics, - - ItemKind::Trait(_, _, ref generics, ..) => { - is_trait = Some(ty::TraitRef::identity(tcx, def_id)); - *generics - } - ItemKind::TraitAlias(ref generics, _) => { - is_trait = Some(ty::TraitRef::identity(tcx, def_id)); - *generics - } - ItemKind::OpaqueTy(OpaqueTy { - origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..), - .. - }) => { - // return-position impl trait - // - // We don't inherit predicates from the parent here: - // If we have, say `fn f<'a, T: 'a>() -> impl Sized {}` - // then the return type is `f::<'static, T>::{{opaque}}`. - // - // If we inherited the predicates of `f` then we would - // require that `T: 'static` to show that the return - // type is well-formed. - // - // The only way to have something with this opaque type - // is from the return type of the containing function, - // which will ensure that the function's predicates - // hold. - return ty::GenericPredicates { parent: None, predicates: &[] }; - } - ItemKind::OpaqueTy(OpaqueTy { - ref generics, - origin: hir::OpaqueTyOrigin::TyAlias, - .. - }) => { - // type-alias impl trait - generics - } - - _ => NO_GENERICS, - } - } - - Node::ForeignItem(item) => match item.kind { - ForeignItemKind::Static(..) => NO_GENERICS, - ForeignItemKind::Fn(_, _, ref generics) => *generics, - ForeignItemKind::Type => NO_GENERICS, - }, - - _ => NO_GENERICS, - }; - - let generics = tcx.generics_of(def_id); - let parent_count = generics.parent_count as u32; - let has_own_self = generics.has_self && parent_count == 0; - - // Below we'll consider the bounds on the type parameters (including `Self`) - // and the explicit where-clauses, but to get the full set of predicates - // on a trait we need to add in the supertrait bounds and bounds found on - // associated types. - if let Some(_trait_ref) = is_trait { - predicates.extend(tcx.super_predicates_of(def_id).predicates.iter().cloned()); - } - - // In default impls, we can assume that the self type implements - // the trait. So in: - // - // default impl Foo for Bar { .. } - // - // we add a default where clause `Foo: Bar`. We do a similar thing for traits - // (see below). Recall that a default impl is not itself an impl, but rather a - // set of defaults that can be incorporated into another impl. - if let Some(trait_ref) = is_default_impl_trait { - predicates.insert((trait_ref.without_const().to_predicate(tcx), tcx.def_span(def_id))); - } - - // Collect the region predicates that were declared inline as - // well. In the case of parameters declared on a fn or method, we - // have to be careful to only iterate over early-bound regions. - let mut index = parent_count - + has_own_self as u32 - + early_bound_lifetimes_from_generics(tcx, ast_generics).count() as u32; - - trace!(?predicates); - trace!(?ast_generics); - - // Collect the predicates that were written inline by the user on each - // type parameter (e.g., `<T: Foo>`). - for param in ast_generics.params { - match param.kind { - // We already dealt with early bound lifetimes above. - GenericParamKind::Lifetime { .. } => (), - GenericParamKind::Type { .. } => { - let name = param.name.ident().name; - let param_ty = ty::ParamTy::new(index, name).to_ty(tcx); - index += 1; - - let mut bounds = Bounds::default(); - // Params are implicitly sized unless a `?Sized` bound is found - <dyn AstConv<'_>>::add_implicitly_sized( - &icx, - &mut bounds, - &[], - Some((param.hir_id, ast_generics.predicates)), - param.span, - ); - trace!(?bounds); - predicates.extend(bounds.predicates(tcx, param_ty)); - trace!(?predicates); - } - GenericParamKind::Const { .. } => { - // Bounds on const parameters are currently not possible. - index += 1; - } - } - } - - trace!(?predicates); - // Add in the bounds that appear in the where-clause. - for predicate in ast_generics.predicates { - match predicate { - hir::WherePredicate::BoundPredicate(bound_pred) => { - let ty = icx.to_ty(bound_pred.bounded_ty); - let bound_vars = icx.tcx.late_bound_vars(bound_pred.bounded_ty.hir_id); - - // Keep the type around in a dummy predicate, in case of no bounds. - // That way, `where Ty:` is not a complete noop (see #53696) and `Ty` - // is still checked for WF. - if bound_pred.bounds.is_empty() { - if let ty::Param(_) = ty.kind() { - // This is a `where T:`, which can be in the HIR from the - // transformation that moves `?Sized` to `T`'s declaration. - // We can skip the predicate because type parameters are - // trivially WF, but also we *should*, to avoid exposing - // users who never wrote `where Type:,` themselves, to - // compiler/tooling bugs from not handling WF predicates. - } else { - let span = bound_pred.bounded_ty.span; - let predicate = ty::Binder::bind_with_vars( - ty::PredicateKind::WellFormed(ty.into()), - bound_vars, - ); - predicates.insert((predicate.to_predicate(tcx), span)); - } - } - - let mut bounds = Bounds::default(); - <dyn AstConv<'_>>::add_bounds( - &icx, - ty, - bound_pred.bounds.iter(), - &mut bounds, - bound_vars, - ); - predicates.extend(bounds.predicates(tcx, ty)); - } - - hir::WherePredicate::RegionPredicate(region_pred) => { - let r1 = <dyn AstConv<'_>>::ast_region_to_region(&icx, ®ion_pred.lifetime, None); - predicates.extend(region_pred.bounds.iter().map(|bound| { - let (r2, span) = match bound { - hir::GenericBound::Outlives(lt) => { - (<dyn AstConv<'_>>::ast_region_to_region(&icx, lt, None), lt.span) - } - _ => bug!(), - }; - let pred = ty::Binder::dummy(ty::PredicateKind::RegionOutlives( - ty::OutlivesPredicate(r1, r2), - )) - .to_predicate(icx.tcx); - - (pred, span) - })) - } - - hir::WherePredicate::EqPredicate(..) => { - // FIXME(#20041) - } - } - } - - if tcx.features().generic_const_exprs { - predicates.extend(const_evaluatable_predicates_of(tcx, def_id.expect_local())); - } - - let mut predicates: Vec<_> = predicates.into_iter().collect(); - - // Subtle: before we store the predicates into the tcx, we - // sort them so that predicates like `T: Foo<Item=U>` come - // before uses of `U`. This avoids false ambiguity errors - // in trait checking. See `setup_constraining_predicates` - // for details. - if let Node::Item(&Item { kind: ItemKind::Impl { .. }, .. }) = node { - let self_ty = tcx.type_of(def_id); - let trait_ref = tcx.impl_trait_ref(def_id); - cgp::setup_constraining_predicates( - tcx, - &mut predicates, - trait_ref, - &mut cgp::parameters_for_impl(self_ty, trait_ref), - ); - } - - ty::GenericPredicates { - parent: generics.parent, - predicates: tcx.arena.alloc_from_iter(predicates), - } -} - -fn const_evaluatable_predicates_of<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, -) -> FxIndexSet<(ty::Predicate<'tcx>, Span)> { - struct ConstCollector<'tcx> { - tcx: TyCtxt<'tcx>, - preds: FxIndexSet<(ty::Predicate<'tcx>, Span)>, - } - - impl<'tcx> intravisit::Visitor<'tcx> for ConstCollector<'tcx> { - fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) { - let def_id = self.tcx.hir().local_def_id(c.hir_id); - let ct = ty::Const::from_anon_const(self.tcx, def_id); - if let ty::ConstKind::Unevaluated(uv) = ct.kind() { - assert_eq!(uv.promoted, ()); - let span = self.tcx.hir().span(c.hir_id); - self.preds.insert(( - ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(uv)) - .to_predicate(self.tcx), - span, - )); - } - } - - fn visit_const_param_default(&mut self, _param: HirId, _ct: &'tcx hir::AnonConst) { - // Do not look into const param defaults, - // these get checked when they are actually instantiated. - // - // We do not want the following to error: - // - // struct Foo<const N: usize, const M: usize = { N + 1 }>; - // struct Bar<const N: usize>(Foo<N, 3>); - } - } - - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let node = tcx.hir().get(hir_id); - - let mut collector = ConstCollector { tcx, preds: FxIndexSet::default() }; - if let hir::Node::Item(item) = node && let hir::ItemKind::Impl(ref impl_) = item.kind { - if let Some(of_trait) = &impl_.of_trait { - debug!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id); - collector.visit_trait_ref(of_trait); - } - - debug!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id); - collector.visit_ty(impl_.self_ty); - } - - if let Some(generics) = node.generics() { - debug!("const_evaluatable_predicates_of({:?}): visit_generics", def_id); - collector.visit_generics(generics); - } - - if let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(hir_id) { - debug!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id); - collector.visit_fn_decl(fn_sig.decl); - } - debug!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds); - - collector.preds -} - -fn trait_explicit_predicates_and_bounds( - tcx: TyCtxt<'_>, - def_id: LocalDefId, -) -> ty::GenericPredicates<'_> { - assert_eq!(tcx.def_kind(def_id), DefKind::Trait); - gather_explicit_predicates_of(tcx, def_id.to_def_id()) -} - -fn explicit_predicates_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::GenericPredicates<'tcx> { - let def_kind = tcx.def_kind(def_id); - if let DefKind::Trait = def_kind { - // Remove bounds on associated types from the predicates, they will be - // returned by `explicit_item_bounds`. - let predicates_and_bounds = tcx.trait_explicit_predicates_and_bounds(def_id.expect_local()); - let trait_identity_substs = InternalSubsts::identity_for_item(tcx, def_id); - - let is_assoc_item_ty = |ty: Ty<'tcx>| { - // For a predicate from a where clause to become a bound on an - // associated type: - // * It must use the identity substs of the item. - // * Since any generic parameters on the item are not in scope, - // this means that the item is not a GAT, and its identity - // substs are the same as the trait's. - // * It must be an associated type for this trait (*not* a - // supertrait). - if let ty::Projection(projection) = ty.kind() { - projection.substs == trait_identity_substs - && tcx.associated_item(projection.item_def_id).container_id(tcx) == def_id - } else { - false - } - }; - - let predicates: Vec<_> = predicates_and_bounds - .predicates - .iter() - .copied() - .filter(|(pred, _)| match pred.kind().skip_binder() { - ty::PredicateKind::Trait(tr) => !is_assoc_item_ty(tr.self_ty()), - ty::PredicateKind::Projection(proj) => { - !is_assoc_item_ty(proj.projection_ty.self_ty()) - } - ty::PredicateKind::TypeOutlives(outlives) => !is_assoc_item_ty(outlives.0), - _ => true, - }) - .collect(); - if predicates.len() == predicates_and_bounds.predicates.len() { - predicates_and_bounds - } else { - ty::GenericPredicates { - parent: predicates_and_bounds.parent, - predicates: tcx.arena.alloc_slice(&predicates), - } - } - } else { - if matches!(def_kind, DefKind::AnonConst) && tcx.lazy_normalization() { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - if tcx.hir().opt_const_param_default_param_hir_id(hir_id).is_some() { - // In `generics_of` we set the generics' parent to be our parent's parent which means that - // we lose out on the predicates of our actual parent if we dont return those predicates here. - // (See comment in `generics_of` for more information on why the parent shenanigans is necessary) - // - // struct Foo<T, const N: usize = { <T as Trait>::ASSOC }>(T) where T: Trait; - // ^^^ ^^^^^^^^^^^^^^^^^^^^^^^ the def id we are calling - // ^^^ explicit_predicates_of on - // parent item we dont have set as the - // parent of generics returned by `generics_of` - // - // In the above code we want the anon const to have predicates in its param env for `T: Trait` - let item_def_id = tcx.hir().get_parent_item(hir_id); - // In the above code example we would be calling `explicit_predicates_of(Foo)` here - return tcx.explicit_predicates_of(item_def_id); - } - } - gather_explicit_predicates_of(tcx, def_id) - } -} - -/// Converts a specific `GenericBound` from the AST into a set of -/// predicates that apply to the self type. A vector is returned -/// because this can be anywhere from zero predicates (`T: ?Sized` adds no -/// predicates) to one (`T: Foo`) to many (`T: Bar<X = i32>` adds `T: Bar` -/// and `<T as Bar>::X == i32`). -fn predicates_from_bound<'tcx>( - astconv: &dyn AstConv<'tcx>, - param_ty: Ty<'tcx>, - bound: &'tcx hir::GenericBound<'tcx>, - bound_vars: &'tcx ty::List<ty::BoundVariableKind>, -) -> Vec<(ty::Predicate<'tcx>, Span)> { - let mut bounds = Bounds::default(); - astconv.add_bounds(param_ty, [bound].into_iter(), &mut bounds, bound_vars); - bounds.predicates(astconv.tcx(), param_ty).collect() -} - fn compute_sig_of_foreign_fn_decl<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, @@ -2543,7 +1414,7 @@ fn compute_sig_of_foreign_fn_decl<'tcx>( abi: abi::Abi, ) -> ty::PolyFnSig<'tcx> { let unsafety = if abi == abi::Abi::RustIntrinsic { - intrinsic_operation_unsafety(tcx.item_name(def_id)) + intrinsic_operation_unsafety(tcx, def_id) } else { hir::Unsafety::Unsafe }; @@ -2741,13 +1612,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; } - // The panic_no_unwind function called by TerminatorKind::Abort will never - // unwind. If the panic handler that it invokes unwind then it will simply - // call the panic handler again. - if Some(did.to_def_id()) == tcx.lang_items().panic_no_unwind() { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; - } - let supported_target_features = tcx.supported_target_features(LOCAL_CRATE); let mut inline_span = None; @@ -2808,7 +1672,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { ) .emit(); } - } else if attr.has_name(sym::rustc_allocator_nounwind) { + } else if attr.has_name(sym::rustc_nounwind) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; } else if attr.has_name(sym::rustc_reallocator) { codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR; @@ -3154,6 +2018,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { E0535, "invalid argument" ) + .help("valid inline arguments are `always` and `never`") .emit(); InlineAttr::None @@ -3227,11 +2092,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs { lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, - |lint| { - lint.build("`no_sanitize` will have no effect after inlining") - .span_note(inline_span, "inlining requested here") - .emit(); - }, + "`no_sanitize` will have no effect after inlining", + |lint| lint.span_note(inline_span, "inlining requested here"), ) } } @@ -3386,7 +2248,7 @@ fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: let node = tcx.hir().get(hir_id); if let Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node { let parent_id = tcx.hir().get_parent_item(hir_id); - let parent_item = tcx.hir().expect_item(parent_id); + let parent_item = tcx.hir().expect_item(parent_id.def_id); if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = parent_item.kind { tcx.sess .struct_span_err( diff --git a/compiler/rustc_typeck/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index 0d34a8bfe..0d34a8bfe 100644 --- a/compiler/rustc_typeck/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index a1d1f125f..c29a645eb 100644 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -284,7 +284,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { icx.to_ty(ty) } } - ImplItemKind::TyAlias(ty) => { + ImplItemKind::Type(ty) => { if tcx.impl_trait_ref(tcx.hir().get_parent_item(hir_id)).is_none() { check_feature_inherent_assoc_ty(tcx, item.span); } @@ -319,7 +319,15 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } } ItemKind::TyAlias(self_ty, _) => icx.to_ty(self_ty), - ItemKind::Impl(hir::Impl { self_ty, .. }) => icx.to_ty(*self_ty), + ItemKind::Impl(hir::Impl { self_ty, .. }) => { + match self_ty.find_self_aliases() { + spans if spans.len() > 0 => { + tcx.sess.emit_err(crate::errors::SelfInImplSelf { span: spans.into(), note: (), }); + tcx.ty_error() + }, + _ => icx.to_ty(*self_ty), + } + }, ItemKind::Fn(..) => { let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); tcx.mk_fn_def(def_id.to_def_id(), substs) @@ -340,10 +348,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { .. }) => { if in_trait { - span_bug!(item.span, "impl-trait in trait has no default") - } else { - find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) + assert!(tcx.impl_defaultness(owner).has_value()); } + find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) } ItemKind::Trait(..) | ItemKind::TraitAlias(..) @@ -493,8 +500,10 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { }, def_id.to_def_id(), ); - if let Some(assoc_item) = assoc_item { - tcx.type_of(tcx.generics_of(assoc_item.def_id).params[idx].def_id) + if let Some(param) + = assoc_item.map(|item| &tcx.generics_of(item.def_id).params[idx]).filter(|param| param.kind.is_ty_or_const()) + { + tcx.type_of(param.def_id) } else { // FIXME(associated_const_equality): add a useful error message here. tcx.ty_error_with_message( @@ -564,6 +573,11 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T /// checked against it (we also carry the span of that first /// type). found: Option<ty::OpaqueHiddenType<'tcx>>, + + /// In the presence of dead code, typeck may figure out a hidden type + /// while borrowck will now. We collect these cases here and check at + /// the end that we actually found a type that matches (modulo regions). + typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>, } impl ConstraintLocator<'_> { @@ -590,18 +604,23 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error() }); return; } - if !tables.concrete_opaque_types.contains_key(&self.def_id) { + let Some(&typeck_hidden_ty) = tables.concrete_opaque_types.get(&self.def_id) else { debug!("no constraints in typeck results"); return; + }; + if self.typeck_types.iter().all(|prev| prev.ty != typeck_hidden_ty.ty) { + self.typeck_types.push(typeck_hidden_ty); } + // Use borrowck to get the type with unerased regions. let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types; debug!(?concrete_opaque_types); if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) { debug!(?concrete_type, "found constraint"); - if let Some(prev) = self.found { - if concrete_type.ty != prev.ty && !(concrete_type, prev).references_error() { + if let Some(prev) = &mut self.found { + if concrete_type.ty != prev.ty && !(concrete_type, prev.ty).references_error() { prev.report_mismatch(&concrete_type, self.tcx); + prev.ty = self.tcx.ty_error(); } } else { self.found = Some(concrete_type); @@ -624,31 +643,31 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T intravisit::walk_expr(self, ex); } fn visit_item(&mut self, it: &'tcx Item<'tcx>) { - trace!(?it.def_id); + trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. - if it.def_id != self.def_id { - self.check(it.def_id); + if it.owner_id.def_id != self.def_id { + self.check(it.owner_id.def_id); intravisit::walk_item(self, it); } } fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { - trace!(?it.def_id); + trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. - if it.def_id != self.def_id { - self.check(it.def_id); + if it.owner_id.def_id != self.def_id { + self.check(it.owner_id.def_id); intravisit::walk_impl_item(self, it); } } fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { - trace!(?it.def_id); - self.check(it.def_id); + trace!(?it.owner_id); + self.check(it.owner_id.def_id); intravisit::walk_trait_item(self, it); } } let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); let scope = tcx.hir().get_defining_scope(hir_id); - let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None }; + let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None, typeck_types: vec![] }; debug!(?scope); @@ -678,16 +697,32 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T } } - match locator.found { - Some(hidden) => hidden.ty, - None => { - tcx.sess.emit_err(UnconstrainedOpaqueType { - span: tcx.def_span(def_id), - name: tcx.item_name(tcx.local_parent(def_id).to_def_id()), - }); - tcx.ty_error() + let Some(hidden) = locator.found else { + tcx.sess.emit_err(UnconstrainedOpaqueType { + span: tcx.def_span(def_id), + name: tcx.item_name(tcx.local_parent(def_id).to_def_id()), + what: match tcx.hir().get(scope) { + _ if scope == hir::CRATE_HIR_ID => "module", + Node::Item(hir::Item { kind: hir::ItemKind::Mod(_), .. }) => "module", + Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => "impl", + _ => "item", + }, + }); + return tcx.ty_error(); + }; + + // Only check against typeck if we didn't already error + if !hidden.ty.references_error() { + for concrete_type in locator.typeck_types { + if tcx.erase_regions(concrete_type.ty) != tcx.erase_regions(hidden.ty) + && !(concrete_type, hidden).references_error() + { + hidden.report_mismatch(&concrete_type, tcx); + } } } + + hidden.ty } fn find_opaque_ty_constraints_for_rpit( @@ -743,24 +778,24 @@ fn find_opaque_ty_constraints_for_rpit( intravisit::walk_expr(self, ex); } fn visit_item(&mut self, it: &'tcx Item<'tcx>) { - trace!(?it.def_id); + trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. - if it.def_id != self.def_id { - self.check(it.def_id); + if it.owner_id.def_id != self.def_id { + self.check(it.owner_id.def_id); intravisit::walk_item(self, it); } } fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { - trace!(?it.def_id); + trace!(?it.owner_id); // The opaque type itself or its children are not within its reveal scope. - if it.def_id != self.def_id { - self.check(it.def_id); + if it.owner_id.def_id != self.def_id { + self.check(it.owner_id.def_id); intravisit::walk_impl_item(self, it); } } fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { - trace!(?it.def_id); - self.check(it.def_id); + trace!(?it.owner_id); + self.check(it.owner_id.def_id); intravisit::walk_trait_item(self, it); } } @@ -788,20 +823,15 @@ fn find_opaque_ty_constraints_for_rpit( // the `concrete_opaque_types` table. tcx.ty_error() } else { - table - .concrete_opaque_types - .get(&def_id) - .copied() - .unwrap_or_else(|| { - // We failed to resolve the opaque type or it - // resolves to itself. We interpret this as the - // no values of the hidden type ever being constructed, - // so we can just make the hidden type be `!`. - // For backwards compatibility reasons, we fall back to - // `()` until we the diverging default is changed. - Some(tcx.mk_diverging_default()) - }) - .expect("RPIT always have a hidden type from typeck") + table.concrete_opaque_types.get(&def_id).map(|ty| ty.ty).unwrap_or_else(|| { + // We failed to resolve the opaque type or it + // resolves to itself. We interpret this as the + // no values of the hidden type ever being constructed, + // so we can just make the hidden type be `!`. + // For backwards compatibility reasons, we fall back to + // `()` until we the diverging default is changed. + tcx.mk_diverging_default() + }) } }) } diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs index 8428e4664..213b89fc7 100644 --- a/compiler/rustc_typeck/src/constrained_generic_params.rs +++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs @@ -114,9 +114,9 @@ pub fn identify_constrained_generic_params<'tcx>( /// ``` /// The impl's predicates are collected from left to right. Ignoring /// the implicit `Sized` bounds, these are -/// * T: Debug -/// * U: Iterator -/// * <U as Iterator>::Item = T -- a desugared ProjectionPredicate +/// * `T: Debug` +/// * `U: Iterator` +/// * `<U as Iterator>::Item = T` -- a desugared ProjectionPredicate /// /// When we, for example, try to go over the trait-reference /// `IntoIter<u32> as Trait`, we substitute the impl parameters with fresh @@ -132,12 +132,16 @@ pub fn identify_constrained_generic_params<'tcx>( /// /// We *do* have to be somewhat careful when projection targets contain /// projections themselves, for example in +/// +/// ```ignore (illustrative) /// impl<S,U,V,W> Trait for U where /// /* 0 */ S: Iterator<Item = U>, /// /* - */ U: Iterator, /// /* 1 */ <U as Iterator>::Item: ToOwned<Owned=(W,<V as Iterator>::Item)> /// /* 2 */ W: Iterator<Item = V> /// /* 3 */ V: Debug +/// ``` +/// /// we have to evaluate the projections in the order I wrote them: /// `V: Debug` requires `V` to be evaluated. The only projection that /// *determines* `V` is 2 (1 contains it, but *does not determine it*, diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 0d2e66745..d5b1a7ce1 100644 --- a/compiler/rustc_typeck/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1,23 +1,13 @@ -//! Errors emitted by typeck. +//! Errors emitted by `rustc_hir_analysis`. + use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler}; -use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic}; +use rustc_errors::{IntoDiagnostic, MultiSpan}; +use rustc_macros::{Diagnostic, LintDiagnostic}; use rustc_middle::ty::Ty; -use rustc_session::SessionDiagnostic; use rustc_span::{symbol::Ident, Span, Symbol}; -#[derive(SessionDiagnostic)] -#[diag(typeck::field_multiply_specified_in_initializer, code = "E0062")] -pub struct FieldMultiplySpecifiedInInitializer { - #[primary_span] - #[label] - pub span: Span, - #[label(typeck::previous_use_label)] - pub prev_span: Span, - pub ident: Ident, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::unrecognized_atomic_operation, code = "E0092")] +#[derive(Diagnostic)] +#[diag(hir_analysis_unrecognized_atomic_operation, code = "E0092")] pub struct UnrecognizedAtomicOperation<'a> { #[primary_span] #[label] @@ -25,8 +15,8 @@ pub struct UnrecognizedAtomicOperation<'a> { pub op: &'a str, } -#[derive(SessionDiagnostic)] -#[diag(typeck::wrong_number_of_generic_arguments_to_intrinsic, code = "E0094")] +#[derive(Diagnostic)] +#[diag(hir_analysis_wrong_number_of_generic_arguments_to_intrinsic, code = "E0094")] pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> { #[primary_span] #[label] @@ -36,8 +26,8 @@ pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> { pub descr: &'a str, } -#[derive(SessionDiagnostic)] -#[diag(typeck::unrecognized_intrinsic_function, code = "E0093")] +#[derive(Diagnostic)] +#[diag(hir_analysis_unrecognized_intrinsic_function, code = "E0093")] pub struct UnrecognizedIntrinsicFunction { #[primary_span] #[label] @@ -45,93 +35,86 @@ pub struct UnrecognizedIntrinsicFunction { pub name: Symbol, } -#[derive(SessionDiagnostic)] -#[diag(typeck::lifetimes_or_bounds_mismatch_on_trait, code = "E0195")] +#[derive(Diagnostic)] +#[diag(hir_analysis_lifetimes_or_bounds_mismatch_on_trait, code = "E0195")] pub struct LifetimesOrBoundsMismatchOnTrait { #[primary_span] #[label] pub span: Span, - #[label(typeck::generics_label)] + #[label(generics_label)] pub generics_span: Option<Span>, pub item_kind: &'static str, pub ident: Ident, } -#[derive(SessionDiagnostic)] -#[diag(typeck::drop_impl_on_wrong_item, code = "E0120")] +#[derive(Diagnostic)] +#[diag(hir_analysis_drop_impl_on_wrong_item, code = "E0120")] pub struct DropImplOnWrongItem { #[primary_span] #[label] pub span: Span, } -#[derive(SessionDiagnostic)] -#[diag(typeck::field_already_declared, code = "E0124")] +#[derive(Diagnostic)] +#[diag(hir_analysis_field_already_declared, code = "E0124")] pub struct FieldAlreadyDeclared { pub field_name: Ident, #[primary_span] #[label] pub span: Span, - #[label(typeck::previous_decl_label)] + #[label(previous_decl_label)] pub prev_span: Span, } -#[derive(SessionDiagnostic)] -#[diag(typeck::copy_impl_on_type_with_dtor, code = "E0184")] +#[derive(Diagnostic)] +#[diag(hir_analysis_copy_impl_on_type_with_dtor, code = "E0184")] pub struct CopyImplOnTypeWithDtor { #[primary_span] #[label] pub span: Span, } -#[derive(SessionDiagnostic)] -#[diag(typeck::multiple_relaxed_default_bounds, code = "E0203")] +#[derive(Diagnostic)] +#[diag(hir_analysis_multiple_relaxed_default_bounds, code = "E0203")] pub struct MultipleRelaxedDefaultBounds { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[diag(typeck::copy_impl_on_non_adt, code = "E0206")] +#[derive(Diagnostic)] +#[diag(hir_analysis_copy_impl_on_non_adt, code = "E0206")] pub struct CopyImplOnNonAdt { #[primary_span] #[label] pub span: Span, } -#[derive(SessionDiagnostic)] -#[diag(typeck::trait_object_declared_with_no_traits, code = "E0224")] +#[derive(Diagnostic)] +#[diag(hir_analysis_trait_object_declared_with_no_traits, code = "E0224")] pub struct TraitObjectDeclaredWithNoTraits { #[primary_span] pub span: Span, - #[label(typeck::alias_span)] + #[label(alias_span)] pub trait_alias_span: Option<Span>, } -#[derive(SessionDiagnostic)] -#[diag(typeck::ambiguous_lifetime_bound, code = "E0227")] +#[derive(Diagnostic)] +#[diag(hir_analysis_ambiguous_lifetime_bound, code = "E0227")] pub struct AmbiguousLifetimeBound { #[primary_span] pub span: Span, } -#[derive(SessionDiagnostic)] -#[diag(typeck::assoc_type_binding_not_allowed, code = "E0229")] +#[derive(Diagnostic)] +#[diag(hir_analysis_assoc_type_binding_not_allowed, code = "E0229")] pub struct AssocTypeBindingNotAllowed { #[primary_span] #[label] pub span: Span, } -#[derive(SessionDiagnostic)] -#[diag(typeck::functional_record_update_on_non_struct, code = "E0436")] -pub struct FunctionalRecordUpdateOnNonStruct { - #[primary_span] - pub span: Span, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::typeof_reserved_keyword_used, code = "E0516")] +#[derive(Diagnostic)] +#[diag(hir_analysis_typeof_reserved_keyword_used, code = "E0516")] pub struct TypeofReservedKeywordUsed<'tcx> { pub ty: Ty<'tcx>, #[primary_span] @@ -141,104 +124,26 @@ pub struct TypeofReservedKeywordUsed<'tcx> { pub opt_sugg: Option<(Span, Applicability)>, } -#[derive(SessionDiagnostic)] -#[diag(typeck::return_stmt_outside_of_fn_body, code = "E0572")] -pub struct ReturnStmtOutsideOfFnBody { - #[primary_span] - pub span: Span, - #[label(typeck::encl_body_label)] - pub encl_body_span: Option<Span>, - #[label(typeck::encl_fn_label)] - pub encl_fn_span: Option<Span>, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::yield_expr_outside_of_generator, code = "E0627")] -pub struct YieldExprOutsideOfGenerator { - #[primary_span] - pub span: Span, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::struct_expr_non_exhaustive, code = "E0639")] -pub struct StructExprNonExhaustive { - #[primary_span] - pub span: Span, - pub what: &'static str, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::method_call_on_unknown_type, code = "E0699")] -pub struct MethodCallOnUnknownType { - #[primary_span] - pub span: Span, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::value_of_associated_struct_already_specified, code = "E0719")] +#[derive(Diagnostic)] +#[diag(hir_analysis_value_of_associated_struct_already_specified, code = "E0719")] pub struct ValueOfAssociatedStructAlreadySpecified { #[primary_span] #[label] pub span: Span, - #[label(typeck::previous_bound_label)] + #[label(previous_bound_label)] pub prev_span: Span, pub item_name: Ident, pub def_path: String, } -#[derive(SessionDiagnostic)] -#[diag(typeck::address_of_temporary_taken, code = "E0745")] -pub struct AddressOfTemporaryTaken { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(SessionSubdiagnostic)] -pub enum AddReturnTypeSuggestion { - #[suggestion( - typeck::add_return_type_add, - code = "-> {found} ", - applicability = "machine-applicable" - )] - Add { - #[primary_span] - span: Span, - found: String, - }, - #[suggestion( - typeck::add_return_type_missing_here, - code = "-> _ ", - applicability = "has-placeholders" - )] - MissingHere { - #[primary_span] - span: Span, - }, -} - -#[derive(SessionSubdiagnostic)] -pub enum ExpectedReturnTypeLabel<'tcx> { - #[label(typeck::expected_default_return_type)] - Unit { - #[primary_span] - span: Span, - }, - #[label(typeck::expected_return_type)] - Other { - #[primary_span] - span: Span, - expected: Ty<'tcx>, - }, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::unconstrained_opaque_type)] +#[derive(Diagnostic)] +#[diag(hir_analysis_unconstrained_opaque_type)] #[note] pub struct UnconstrainedOpaqueType { #[primary_span] pub span: Span, pub name: Symbol, + pub what: &'static str, } pub struct MissingTypeParams { @@ -249,12 +154,12 @@ pub struct MissingTypeParams { pub empty_generic_args: bool, } -// Manual implementation of `SessionDiagnostic` to be able to call `span_to_snippet`. -impl<'a> SessionDiagnostic<'a> for MissingTypeParams { +// Manual implementation of `IntoDiagnostic` to be able to call `span_to_snippet`. +impl<'a> IntoDiagnostic<'a> for MissingTypeParams { fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> { let mut err = handler.struct_span_err_with_code( self.span, - rustc_errors::fluent::typeck::missing_type_params, + rustc_errors::fluent::hir_analysis_missing_type_params, error_code!(E0393), ); err.set_arg("parameterCount", self.missing_type_params.len()); @@ -267,7 +172,7 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams { .join(", "), ); - err.span_label(self.def_span, rustc_errors::fluent::typeck::label); + err.span_label(self.def_span, rustc_errors::fluent::label); let mut suggested = false; // Don't suggest setting the type params if there are some already: the order is @@ -282,7 +187,7 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams { // least we can clue them to the correct syntax `Iterator<Type>`. err.span_suggestion( self.span, - rustc_errors::fluent::typeck::suggestion, + rustc_errors::fluent::suggestion, format!( "{}<{}>", snippet, @@ -298,16 +203,16 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams { } } if !suggested { - err.span_label(self.span, rustc_errors::fluent::typeck::no_suggestion_label); + err.span_label(self.span, rustc_errors::fluent::no_suggestion_label); } - err.note(rustc_errors::fluent::typeck::note); + err.note(rustc_errors::fluent::note); err } } -#[derive(SessionDiagnostic)] -#[diag(typeck::manual_implementation, code = "E0183")] +#[derive(Diagnostic)] +#[diag(hir_analysis_manual_implementation, code = "E0183")] #[help] pub struct ManualImplementation { #[primary_span] @@ -316,22 +221,22 @@ pub struct ManualImplementation { pub trait_name: String, } -#[derive(SessionDiagnostic)] -#[diag(typeck::substs_on_overridden_impl)] +#[derive(Diagnostic)] +#[diag(hir_analysis_substs_on_overridden_impl)] pub struct SubstsOnOverriddenImpl { #[primary_span] pub span: Span, } #[derive(LintDiagnostic)] -#[diag(typeck::unused_extern_crate)] +#[diag(hir_analysis_unused_extern_crate)] pub struct UnusedExternCrate { #[suggestion(applicability = "machine-applicable", code = "")] pub span: Span, } #[derive(LintDiagnostic)] -#[diag(typeck::extern_crate_not_idiomatic)] +#[diag(hir_analysis_extern_crate_not_idiomatic)] pub struct ExternCrateNotIdiomatic { #[suggestion_short(applicability = "machine-applicable", code = "{suggestion_code}")] pub span: Span, @@ -339,9 +244,39 @@ pub struct ExternCrateNotIdiomatic { pub suggestion_code: String, } -#[derive(SessionDiagnostic)] -#[diag(typeck::expected_used_symbol)] +#[derive(Diagnostic)] +#[diag(hir_analysis_expected_used_symbol)] pub struct ExpectedUsedSymbol { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_const_impl_for_non_const_trait)] +pub struct ConstImplForNonConstTrait { + #[primary_span] + pub trait_ref_span: Span, + pub trait_name: String, + #[suggestion(applicability = "machine-applicable", code = "#[const_trait]")] + pub local_trait_span: Option<Span>, + #[note] + pub marking: (), + #[note(adding)] + pub adding: (), +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_const_bound_for_non_const_trait)] +pub struct ConstBoundForNonConstTrait { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(hir_analysis_self_in_impl_self)] +pub struct SelfInImplSelf { + #[primary_span] + pub span: MultiSpan, + #[note] + pub note: (), +} diff --git a/compiler/rustc_typeck/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index 7b080dc29..b0fdfcf38 100644 --- a/compiler/rustc_typeck/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -64,38 +64,36 @@ fn diagnostic_hir_wf_check<'tcx>( impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> { fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - self.tcx.infer_ctxt().enter(|infcx| { - let tcx_ty = - self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx }); - let cause = traits::ObligationCause::new( - ty.span, - self.hir_id, - traits::ObligationCauseCode::WellFormed(None), - ); - let errors = traits::fully_solve_obligation( - &infcx, - traits::Obligation::new( - cause, - self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into())) - .to_predicate(self.tcx), - ), - ); - if !errors.is_empty() { - debug!("Wf-check got errors for {:?}: {:?}", ty, errors); - for error in errors { - if error.obligation.predicate == self.predicate { - // Save the cause from the greatest depth - this corresponds - // to picking more-specific types (e.g. `MyStruct<u8>`) - // over less-specific types (e.g. `Option<MyStruct<u8>>`) - if self.depth >= self.cause_depth { - self.cause = Some(error.obligation.cause); - self.cause_depth = self.depth - } + let infcx = self.tcx.infer_ctxt().build(); + let tcx_ty = self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx }); + let cause = traits::ObligationCause::new( + ty.span, + self.hir_id, + traits::ObligationCauseCode::WellFormed(None), + ); + let errors = traits::fully_solve_obligation( + &infcx, + traits::Obligation::new( + cause, + self.param_env, + ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into())) + .to_predicate(self.tcx), + ), + ); + if !errors.is_empty() { + debug!("Wf-check got errors for {:?}: {:?}", ty, errors); + for error in errors { + if error.obligation.predicate == self.predicate { + // Save the cause from the greatest depth - this corresponds + // to picking more-specific types (e.g. `MyStruct<u8>`) + // over less-specific types (e.g. `Option<MyStruct<u8>>`) + if self.depth >= self.cause_depth { + self.cause = Some(error.obligation.cause); + self.cause_depth = self.depth } } } - }); + } self.depth += 1; intravisit::walk_ty(self, ty); self.depth -= 1; @@ -119,7 +117,7 @@ fn diagnostic_hir_wf_check<'tcx>( let ty = match loc { WellFormedLoc::Ty(_) => match hir.get(hir_id) { hir::Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::TyAlias(ty) => Some(ty), + hir::ImplItemKind::Type(ty) => Some(ty), hir::ImplItemKind::Const(ty, _) => Some(ty), ref item => bug!("Unexpected ImplItem {:?}", item), }, diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 9fee1eaae..136f61999 100644 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -11,7 +11,7 @@ use crate::constrained_generic_params as cgp; use min_specialization::check_min_specialization; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; @@ -19,8 +19,6 @@ use rustc_middle::ty::query::Providers; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; use rustc_span::{Span, Symbol}; -use std::collections::hash_map::Entry::{Occupied, Vacant}; - mod min_specialization; /// Checks that all the type/lifetime parameters on an impl also @@ -57,11 +55,10 @@ fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { let min_specialization = tcx.features().min_specialization; let module = tcx.hir_module_items(module_def_id); for id in module.items() { - if matches!(tcx.def_kind(id.def_id), DefKind::Impl) { - enforce_impl_params_are_constrained(tcx, id.def_id); - enforce_impl_items_are_distinct(tcx, id.def_id); + if matches!(tcx.def_kind(id.owner_id), DefKind::Impl) { + enforce_impl_params_are_constrained(tcx, id.owner_id.def_id); if min_specialization { - check_min_specialization(tcx, id.def_id); + check_min_specialization(tcx, id.owner_id.def_id); } } } @@ -194,35 +191,3 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol } err.emit(); } - -/// Enforce that we do not have two items in an impl with the same name. -fn enforce_impl_items_are_distinct(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { - let mut seen_type_items = FxHashMap::default(); - let mut seen_value_items = FxHashMap::default(); - for &impl_item_ref in tcx.associated_item_def_ids(impl_def_id) { - let impl_item = tcx.associated_item(impl_item_ref); - let seen_items = match impl_item.kind { - ty::AssocKind::Type => &mut seen_type_items, - _ => &mut seen_value_items, - }; - let span = tcx.def_span(impl_item_ref); - let ident = impl_item.ident(tcx); - match seen_items.entry(ident.normalize_to_macros_2_0()) { - Occupied(entry) => { - let mut err = struct_span_err!( - tcx.sess, - span, - E0201, - "duplicate definitions with name `{}`:", - ident - ); - err.span_label(*entry.get(), format!("previous definition of `{}` here", ident)); - err.span_label(span, "duplicate definition"); - err.emit(); - } - Vacant(entry) => { - entry.insert(span); - } - } - } -} diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index 2741d9f77..e806e9487 100644 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -77,7 +77,7 @@ use rustc_middle::ty::subst::{GenericArg, InternalSubsts, SubstsRef}; use rustc_middle::ty::trait_def::TraitSpecializationKind; use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; use rustc_span::Span; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _; use rustc_trait_selection::traits::{self, translate_substs, wf, ObligationCtxt}; @@ -130,8 +130,10 @@ fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node /// /// Example /// +/// ```ignore (illustrative) /// impl<A, B> Foo<A> for B { /* impl2 */ } /// impl<C> Foo<Vec<C>> for C { /* impl1 */ } +/// ``` /// /// Would return `S1 = [C]` and `S2 = [Vec<C>, C]`. fn get_impl_substs<'tcx>( @@ -139,34 +141,33 @@ fn get_impl_substs<'tcx>( impl1_def_id: LocalDefId, impl2_node: Node, ) -> Option<(SubstsRef<'tcx>, SubstsRef<'tcx>)> { - tcx.infer_ctxt().enter(|ref infcx| { - let ocx = ObligationCtxt::new(infcx); - let param_env = tcx.param_env(impl1_def_id); - let impl1_hir_id = tcx.hir().local_def_id_to_hir_id(impl1_def_id); + let infcx = &tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(infcx); + let param_env = tcx.param_env(impl1_def_id); + let impl1_hir_id = tcx.hir().local_def_id_to_hir_id(impl1_def_id); - let assumed_wf_types = - ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id); + let assumed_wf_types = + ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id); - let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id.to_def_id()); - let impl2_substs = - translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node); + let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id.to_def_id()); + let impl2_substs = + translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node); - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - ocx.infcx.report_fulfillment_errors(&errors, None, false); - return None; - } + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + return None; + } - let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_hir_id, assumed_wf_types); - let outlives_env = OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds); - infcx.check_region_obligations_and_report_errors(impl1_def_id, &outlives_env); - let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else { - let span = tcx.def_span(impl1_def_id); - tcx.sess.emit_err(SubstsOnOverriddenImpl { span }); - return None; - }; - Some((impl1_substs, impl2_substs)) - }) + let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_hir_id, assumed_wf_types); + let outlives_env = OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds); + infcx.check_region_obligations_and_report_errors(impl1_def_id, &outlives_env); + let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else { + let span = tcx.def_span(impl1_def_id); + tcx.sess.emit_err(SubstsOnOverriddenImpl { span }); + return None; + }; + Some((impl1_substs, impl2_substs)) } /// Returns a list of all of the unconstrained subst of the given impl. @@ -226,13 +227,17 @@ fn unconstrained_parent_impl_substs<'tcx>( /// /// For example forbid the following: /// +/// ```ignore (illustrative) /// impl<A> Tr for A { } /// impl<B> Tr for (B, B) { } +/// ``` /// /// Note that only consider the unconstrained parameters of the base impl: /// +/// ```ignore (illustrative) /// impl<S, I: IntoIterator<Item = S>> Tr<S> for I { } /// impl<T> Tr<T> for Vec<T> { } +/// ``` /// /// The substs for the parent impl here are `[T, Vec<T>]`, which repeats `T`, /// but `S` is constrained in the parent impl, so `parent_substs` is only @@ -257,8 +262,10 @@ fn check_duplicate_params<'tcx>( /// /// For example forbid the following: /// +/// ```ignore (illustrative) /// impl<A> Tr for A { } /// impl Tr for &'static i32 { } +/// ``` fn check_static_lifetimes<'tcx>( tcx: TyCtxt<'tcx>, parent_substs: &Vec<GenericArg<'tcx>>, @@ -344,23 +351,21 @@ fn check_predicates<'tcx>( // Include the well-formed predicates of the type parameters of the impl. for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().substs { - tcx.infer_ctxt().enter(|ref infcx| { - let obligations = wf::obligations( - infcx, - tcx.param_env(impl1_def_id), - tcx.hir().local_def_id_to_hir_id(impl1_def_id), - 0, - arg, - span, - ) - .unwrap(); + let infcx = &tcx.infer_ctxt().build(); + let obligations = wf::obligations( + infcx, + tcx.param_env(impl1_def_id), + tcx.hir().local_def_id_to_hir_id(impl1_def_id), + 0, + arg, + span, + ) + .unwrap(); - assert!(!obligations.needs_infer()); - impl2_predicates.extend( - traits::elaborate_obligations(tcx, obligations) - .map(|obligation| obligation.predicate), - ) - }) + assert!(!obligations.needs_infer()); + impl2_predicates.extend( + traits::elaborate_obligations(tcx, obligations).map(|obligation| obligation.predicate), + ) } impl2_predicates.extend( traits::elaborate_predicates_with_span(tcx, always_applicable_traits) @@ -423,13 +428,10 @@ fn trait_predicate_kind<'tcx>( predicate: ty::Predicate<'tcx>, ) -> Option<TraitSpecializationKind> { match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: _, - }) => Some(tcx.trait_def(trait_ref.def_id).specialization_kind), - ty::PredicateKind::Trait(_) - | ty::PredicateKind::RegionOutlives(_) + ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity: _ }) => { + Some(tcx.trait_def(trait_ref.def_id).specialization_kind) + } + ty::PredicateKind::RegionOutlives(_) | ty::PredicateKind::TypeOutlives(_) | ty::PredicateKind::Projection(_) | ty::PredicateKind::WellFormed(_) diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index b1ce972e1..525cd2419 100644 --- a/compiler/rustc_typeck/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -64,15 +64,13 @@ This API is completely unstable and subject to change. #![feature(if_let_guard)] #![feature(is_sorted)] #![feature(iter_intersperse)] -#![cfg_attr(bootstrap, feature(label_break_value))] #![feature(let_chains)] -#![cfg_attr(bootstrap, feature(let_else))] #![feature(min_specialization)] #![feature(never_type)] #![feature(once_cell)] #![feature(slice_partition_dedup)] #![feature(try_blocks)] -#![feature(is_some_with)] +#![feature(is_some_and)] #![feature(type_alias_impl_trait)] #![recursion_limit = "256"] @@ -84,20 +82,19 @@ extern crate rustc_middle; // These are used by Clippy. pub mod check; -pub mod expr_use_visitor; -mod astconv; +pub mod astconv; mod bounds; mod check_unused; mod coherence; -mod collect; +// FIXME: This module shouldn't be public. +pub mod collect; mod constrained_generic_params; mod errors; pub mod hir_wf_check; mod impl_wf_check; -mod mem_categorization; mod outlives; -mod structured_errors; +pub mod structured_errors; mod variance; use rustc_errors::{struct_span_err, ErrorGuaranteed}; @@ -112,7 +109,7 @@ use rustc_middle::util; use rustc_session::config::EntryFnType; use rustc_span::{symbol::sym, Span, DUMMY_SP}; use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; use std::iter; @@ -143,24 +140,23 @@ fn require_same_types<'tcx>( expected: Ty<'tcx>, actual: Ty<'tcx>, ) -> bool { - tcx.infer_ctxt().enter(|ref infcx| { - let param_env = ty::ParamEnv::empty(); - let errors = match infcx.at(cause, param_env).eq(expected, actual) { - Ok(InferOk { obligations, .. }) => traits::fully_solve_obligations(infcx, obligations), - Err(err) => { - infcx.report_mismatched_types(cause, expected, actual, err).emit(); - return false; - } - }; + let infcx = &tcx.infer_ctxt().build(); + let param_env = ty::ParamEnv::empty(); + let errors = match infcx.at(cause, param_env).eq(expected, actual) { + Ok(InferOk { obligations, .. }) => traits::fully_solve_obligations(infcx, obligations), + Err(err) => { + infcx.err_ctxt().report_mismatched_types(cause, expected, actual, err).emit(); + return false; + } + }; - match &errors[..] { - [] => true, - errors => { - infcx.report_fulfillment_errors(errors, None, false); - false - } + match &errors[..] { + [] => true, + errors => { + infcx.err_ctxt().report_fulfillment_errors(errors, None, false); + false } - }) + } } fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { @@ -307,23 +303,22 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) { error = true; } let return_ty = return_ty.skip_binder(); - tcx.infer_ctxt().enter(|infcx| { - // Main should have no WC, so empty param env is OK here. - let param_env = ty::ParamEnv::empty(); - let cause = traits::ObligationCause::new( - return_ty_span, - main_diagnostics_hir_id, - ObligationCauseCode::MainFunctionType, - ); - let ocx = traits::ObligationCtxt::new(&infcx); - let norm_return_ty = ocx.normalize(cause.clone(), param_env, return_ty); - ocx.register_bound(cause, param_env, norm_return_ty, term_did); - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - infcx.report_fulfillment_errors(&errors, None, false); - error = true; - } - }); + let infcx = tcx.infer_ctxt().build(); + // Main should have no WC, so empty param env is OK here. + let param_env = ty::ParamEnv::empty(); + let cause = traits::ObligationCause::new( + return_ty_span, + main_diagnostics_hir_id, + ObligationCauseCode::MainFunctionType, + ); + let ocx = traits::ObligationCtxt::new(&infcx); + let norm_return_ty = ocx.normalize(cause.clone(), param_env, return_ty); + ocx.register_bound(cause, param_env, norm_return_ty, term_did); + let errors = ocx.select_all_or_error(); + if !errors.is_empty() { + infcx.err_ctxt().report_fulfillment_errors(&errors, None, false); + error = true; + } // now we can take the return type of the given main function expected_return_type = main_fnsig.output(); } else { @@ -386,7 +381,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) { error = true; } if let hir::IsAsync::Async = sig.header.asyncness { - let span = tcx.def_span(it.def_id); + let span = tcx.def_span(it.owner_id); struct_span_err!( tcx.sess, span, diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs index 7534482cc..7534482cc 100644 --- a/compiler/rustc_typeck/src/outlives/explicit.rs +++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs index 3b779280e..90c6edb65 100644 --- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs +++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs @@ -1,8 +1,8 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt}; +use rustc_middle::ty::{GenericArg, GenericArgKind}; use rustc_span::Span; use super::explicit::ExplicitPredicatesMap; @@ -29,7 +29,7 @@ pub(super) fn infer_predicates<'tcx>( // Visit all the crates and infer predicates for id in tcx.hir().items() { - let item_did = id.def_id; + let item_did = id.owner_id; debug!("InferVisitor::visit_item(item={:?})", item_did); diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs index e50c26765..e50c26765 100644 --- a/compiler/rustc_typeck/src/outlives/mod.rs +++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs diff --git a/compiler/rustc_typeck/src/outlives/test.rs b/compiler/rustc_hir_analysis/src/outlives/test.rs index eb0e12034..fa2ac5659 100644 --- a/compiler/rustc_typeck/src/outlives/test.rs +++ b/compiler/rustc_hir_analysis/src/outlives/test.rs @@ -6,11 +6,11 @@ pub fn test_inferred_outlives(tcx: TyCtxt<'_>) { for id in tcx.hir().items() { // For unit testing: check for a special "rustc_outlives" // attribute and report an error with various results if found. - if tcx.has_attr(id.def_id.to_def_id(), sym::rustc_outlives) { - let inferred_outlives_of = tcx.inferred_outlives_of(id.def_id); + if tcx.has_attr(id.owner_id.to_def_id(), sym::rustc_outlives) { + let inferred_outlives_of = tcx.inferred_outlives_of(id.owner_id); struct_span_err!( tcx.sess, - tcx.def_span(id.def_id), + tcx.def_span(id.owner_id), E0640, "{:?}", inferred_outlives_of diff --git a/compiler/rustc_typeck/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs index 3e8d023fb..0409c7081 100644 --- a/compiler/rustc_typeck/src/outlives/utils.rs +++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs @@ -96,6 +96,23 @@ pub(crate) fn insert_outlives_predicate<'tcx>( .or_insert(span); } + Component::Opaque(def_id, substs) => { + // This would arise from something like: + // + // ```rust + // type Opaque<T> = impl Sized; + // fn defining<T>() -> Opaque<T> {} + // struct Ss<'a, T>(&'a Opaque<T>); + // ``` + // + // Here we want to have an implied bound `Opaque<T>: 'a` + + let ty = tcx.mk_opaque(def_id, substs); + required_predicates + .entry(ty::OutlivesPredicate(ty.into(), outlived_region)) + .or_insert(span); + } + Component::EscapingProjection(_) => { // As above, but the projection involves // late-bound regions. Therefore, the WF diff --git a/compiler/rustc_typeck/src/structured_errors.rs b/compiler/rustc_hir_analysis/src/structured_errors.rs index 0b46fce17..0b46fce17 100644 --- a/compiler/rustc_typeck/src/structured_errors.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors.rs diff --git a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs index 324df313e..324df313e 100644 --- a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs diff --git a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs index bb6088054..bb6088054 100644 --- a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs index 435912464..435912464 100644 --- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index eaf0310d5..eaf0310d5 100644 --- a/compiler/rustc_typeck/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs diff --git a/compiler/rustc_typeck/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 82103c5a0..82103c5a0 100644 --- a/compiler/rustc_typeck/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs diff --git a/compiler/rustc_typeck/src/variance/solve.rs b/compiler/rustc_hir_analysis/src/variance/solve.rs index 97aca621a..97aca621a 100644 --- a/compiler/rustc_typeck/src/variance/solve.rs +++ b/compiler/rustc_hir_analysis/src/variance/solve.rs diff --git a/compiler/rustc_typeck/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index 1f763011e..1f763011e 100644 --- a/compiler/rustc_typeck/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs diff --git a/compiler/rustc_typeck/src/variance/test.rs b/compiler/rustc_hir_analysis/src/variance/test.rs index 2ba87db88..83ed3e44b 100644 --- a/compiler/rustc_typeck/src/variance/test.rs +++ b/compiler/rustc_hir_analysis/src/variance/test.rs @@ -6,9 +6,10 @@ pub fn test_variance(tcx: TyCtxt<'_>) { // For unit testing: check for a special "rustc_variance" // attribute and report an error with various results if found. for id in tcx.hir().items() { - if tcx.has_attr(id.def_id.to_def_id(), sym::rustc_variance) { - let variances_of = tcx.variances_of(id.def_id); - struct_span_err!(tcx.sess, tcx.def_span(id.def_id), E0208, "{:?}", variances_of).emit(); + if tcx.has_attr(id.owner_id.to_def_id(), sym::rustc_variance) { + let variances_of = tcx.variances_of(id.owner_id); + struct_span_err!(tcx.sess, tcx.def_span(id.owner_id), E0208, "{:?}", variances_of) + .emit(); } } } diff --git a/compiler/rustc_typeck/src/variance/xform.rs b/compiler/rustc_hir_analysis/src/variance/xform.rs index 027f0859f..027f0859f 100644 --- a/compiler/rustc_typeck/src/variance/xform.rs +++ b/compiler/rustc_hir_analysis/src/variance/xform.rs diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 20332e75c..2b15d4dcd 100644 --- a/compiler/rustc_typeck/src/check/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -1,10 +1,10 @@ -use crate::check::coercion::{AsCoercionSite, CoerceMany}; -use crate::check::{Diverges, Expectation, FnCtxt, Needs}; +use crate::coercion::{AsCoercionSite, CoerceMany}; +use crate::{Diverges, Expectation, FnCtxt, Needs}; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{self as hir, ExprKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::traits::Obligation; -use rustc_middle::ty::{self, Subst, ToPredicate, Ty}; +use rustc_middle::ty::{self, ToPredicate, Ty}; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; use rustc_trait_selection::traits::{ @@ -140,7 +140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some(ret) = self .tcx .hir() - .find_by_def_id(self.body_id.owner) + .find_by_def_id(self.body_id.owner.def_id) .and_then(|owner| owner.fn_decl()) .map(|decl| decl.output.span()) else { return; }; @@ -259,7 +259,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help("consider adding an `else` block that evaluates to the expected type"); error = true; }, - ret_reason.is_none(), + false, ); error } @@ -495,7 +495,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .typeck_results .borrow() .liberated_fn_sigs() - .get(hir::HirId::make_owner(self.body_id.owner))?; + .get(hir::HirId::make_owner(self.body_id.owner.def_id))?; let substs = sig.output().walk().find_map(|arg| { if let ty::GenericArgKind::Type(ty) = arg.unpack() @@ -514,8 +514,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } for ty in [first_ty, second_ty] { - for pred in self.tcx.bound_explicit_item_bounds(rpit_def_id).transpose_iter() { - let pred = pred.map_bound(|(pred, _)| *pred).subst(self.tcx, substs); + for (pred, _) in self + .tcx + .bound_explicit_item_bounds(rpit_def_id) + .subst_iter_copied(self.tcx, substs) + { let pred = match pred.kind().skip_binder() { ty::PredicateKind::Trait(mut trait_pred) => { assert_eq!(trait_pred.trait_ref.self_ty(), opaque_ty); diff --git a/compiler/rustc_typeck/src/check/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index 59c366ad7..59c366ad7 100644 --- a/compiler/rustc_typeck/src/check/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index 0d35c2479..1b33f2f02 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -1,8 +1,10 @@ +use super::method::probe::{IsSuggestion, Mode, ProbeScope}; use super::method::MethodCallee; -use super::{DefIdOrName, Expectation, FnCtxt, TupleArgumentsFlag}; -use crate::type_error_struct; +use super::{Expectation, FnCtxt, TupleArgumentsFlag}; -use rustc_errors::{struct_span_err, Applicability, Diagnostic}; +use crate::type_error_struct; +use rustc_ast::util::parser::PREC_POSTFIX; +use rustc_errors::{struct_span_err, Applicability, Diagnostic, StashKey}; use rustc_hir as hir; use rustc_hir::def::{self, Namespace, Res}; use rustc_hir::def_id::DefId; @@ -17,7 +19,7 @@ use rustc_infer::{ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; -use rustc_middle::ty::subst::{Subst, SubstsRef}; +use rustc_middle::ty::SubstsRef; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{sym, Ident}; @@ -25,6 +27,7 @@ use rustc_span::Span; use rustc_target::spec::abi; use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use std::iter; @@ -60,6 +63,7 @@ pub fn check_legal_trait_for_method_call( } } +#[derive(Debug)] enum CallStep<'tcx> { Builtin(Ty<'tcx>), DeferredClosure(LocalDefId, ty::FnSig<'tcx>), @@ -188,6 +192,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } + ty::Error(_) => { + return None; + } + _ => {} } @@ -394,140 +402,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ty::FnPtr(sig) => (sig, None), _ => { - let mut unit_variant = None; - if let hir::ExprKind::Path(qpath) = &callee_expr.kind - && let Res::Def(def::DefKind::Ctor(kind, def::CtorKind::Const), _) - = self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) - // Only suggest removing parens if there are no arguments - && arg_exprs.is_empty() + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind + && let [segment] = path.segments + && let Some(mut diag) = self + .tcx + .sess + .diagnostic() + .steal_diagnostic(segment.ident.span, StashKey::CallIntoMethod) { - let descr = match kind { - def::CtorOf::Struct => "struct", - def::CtorOf::Variant => "enum variant", - }; - let removal_span = - callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); - unit_variant = - Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(qpath))); - } - - let callee_ty = self.resolve_vars_if_possible(callee_ty); - let mut err = type_error_struct!( - self.tcx.sess, - callee_expr.span, - callee_ty, - E0618, - "expected function, found {}", - match &unit_variant { - Some((_, kind, path)) => format!("{kind} `{path}`"), - None => format!("`{callee_ty}`"), - } - ); - - self.identify_bad_closure_def_and_call( - &mut err, - call_expr.hir_id, - &callee_expr.kind, - callee_expr.span, - ); - - if let Some((removal_span, kind, path)) = &unit_variant { - err.span_suggestion_verbose( - *removal_span, - &format!( - "`{path}` is a unit {kind}, and does not take parentheses to be constructed", - ), - "", - Applicability::MachineApplicable, - ); - } - - let mut inner_callee_path = None; - let def = match callee_expr.kind { - hir::ExprKind::Path(ref qpath) => { - self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) - } - hir::ExprKind::Call(ref inner_callee, _) => { - // If the call spans more than one line and the callee kind is - // itself another `ExprCall`, that's a clue that we might just be - // missing a semicolon (Issue #51055) - let call_is_multiline = - self.tcx.sess.source_map().is_multiline(call_expr.span); - if call_is_multiline { - err.span_suggestion( - callee_expr.span.shrink_to_hi(), - "consider using a semicolon here", - ";", - Applicability::MaybeIncorrect, - ); - } - if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.kind { - inner_callee_path = Some(inner_qpath); - self.typeck_results.borrow().qpath_res(inner_qpath, inner_callee.hir_id) - } else { - Res::Err - } - } - _ => Res::Err, - }; - - if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) { - if let Some((maybe_def, output_ty, _)) = self.extract_callable_info(callee_expr, callee_ty) - && !self.type_is_sized_modulo_regions(self.param_env, output_ty, callee_expr.span) + // Try suggesting `foo(a)` -> `a.foo()` if possible. + if let Some(ty) = + self.suggest_call_as_method( + &mut diag, + segment, + arg_exprs, + call_expr, + expected + ) { - let descr = match maybe_def { - DefIdOrName::DefId(def_id) => self.tcx.def_kind(def_id).descr(def_id), - DefIdOrName::Name(name) => name, - }; - err.span_label( - callee_expr.span, - format!("this {descr} returns an unsized value `{output_ty}`, so it cannot be called") - ); - if let DefIdOrName::DefId(def_id) = maybe_def - && let Some(def_span) = self.tcx.hir().span_if_local(def_id) - { - err.span_label(def_span, "the callable type is defined here"); - } + diag.emit(); + return ty; } else { - err.span_label(call_expr.span, "call expression requires function"); + diag.emit(); } } - if let Some(span) = self.tcx.hir().res_span(def) { - let callee_ty = callee_ty.to_string(); - let label = match (unit_variant, inner_callee_path) { - (Some((_, kind, path)), _) => Some(format!("{kind} `{path}` defined here")), - (_, Some(hir::QPath::Resolved(_, path))) => self - .tcx - .sess - .source_map() - .span_to_snippet(path.span) - .ok() - .map(|p| format!("`{p}` defined here returns `{callee_ty}`")), - _ => { - match def { - // Emit a different diagnostic for local variables, as they are not - // type definitions themselves, but rather variables *of* that type. - Res::Local(hir_id) => Some(format!( - "`{}` has type `{}`", - self.tcx.hir().name(hir_id), - callee_ty - )), - Res::Def(kind, def_id) if kind.ns() == Some(Namespace::ValueNS) => { - Some(format!( - "`{}` defined here", - self.tcx.def_path_str(def_id), - )) - } - _ => Some(format!("`{callee_ty}` defined here")), - } - } - }; - if let Some(label) = label { - err.span_label(span, label); - } - } - err.emit(); + self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs); // This is the "default" function signature, used in case of error. // In that case, we check each argument against "error" in order to @@ -574,6 +474,243 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_sig.output() } + /// Attempts to reinterpret `method(rcvr, args...)` as `rcvr.method(args...)` + /// and suggesting the fix if the method probe is successful. + fn suggest_call_as_method( + &self, + diag: &mut Diagnostic, + segment: &'tcx hir::PathSegment<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + call_expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + ) -> Option<Ty<'tcx>> { + if let [callee_expr, rest @ ..] = arg_exprs { + let callee_ty = self.check_expr(callee_expr); + // First, do a probe with `IsSuggestion(true)` to avoid emitting + // any strange errors. If it's successful, then we'll do a true + // method lookup. + let Ok(pick) = self + .probe_for_name( + call_expr.span, + Mode::MethodCall, + segment.ident, + IsSuggestion(true), + callee_ty, + call_expr.hir_id, + // We didn't record the in scope traits during late resolution + // so we need to probe AllTraits unfortunately + ProbeScope::AllTraits, + ) else { + return None; + }; + + let pick = self.confirm_method( + call_expr.span, + callee_expr, + call_expr, + callee_ty, + pick, + segment, + ); + if pick.illegal_sized_bound.is_some() { + return None; + } + + let up_to_rcvr_span = segment.ident.span.until(callee_expr.span); + let rest_span = callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); + let rest_snippet = if let Some(first) = rest.first() { + self.tcx + .sess + .source_map() + .span_to_snippet(first.span.to(call_expr.span.shrink_to_hi())) + } else { + Ok(")".to_string()) + }; + + if let Ok(rest_snippet) = rest_snippet { + let sugg = if callee_expr.precedence().order() >= PREC_POSTFIX { + vec![ + (up_to_rcvr_span, "".to_string()), + (rest_span, format!(".{}({rest_snippet}", segment.ident)), + ] + } else { + vec![ + (up_to_rcvr_span, "(".to_string()), + (rest_span, format!(").{}({rest_snippet}", segment.ident)), + ] + }; + let self_ty = self.resolve_vars_if_possible(pick.callee.sig.inputs()[0]); + diag.multipart_suggestion( + format!( + "use the `.` operator to call the method `{}{}` on `{self_ty}`", + self.tcx + .associated_item(pick.callee.def_id) + .trait_container(self.tcx) + .map_or_else( + || String::new(), + |trait_def_id| self.tcx.def_path_str(trait_def_id) + "::" + ), + segment.ident + ), + sugg, + Applicability::MaybeIncorrect, + ); + + // Let's check the method fully now + let return_ty = self.check_method_argument_types( + segment.ident.span, + call_expr, + Ok(pick.callee), + rest, + TupleArgumentsFlag::DontTupleArguments, + expected, + ); + + return Some(return_ty); + } + } + + None + } + + fn report_invalid_callee( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + callee_expr: &'tcx hir::Expr<'tcx>, + callee_ty: Ty<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + ) { + let mut unit_variant = None; + if let hir::ExprKind::Path(qpath) = &callee_expr.kind + && let Res::Def(def::DefKind::Ctor(kind, def::CtorKind::Const), _) + = self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) + // Only suggest removing parens if there are no arguments + && arg_exprs.is_empty() + { + let descr = match kind { + def::CtorOf::Struct => "struct", + def::CtorOf::Variant => "enum variant", + }; + let removal_span = callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); + unit_variant = Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(qpath))); + } + + let callee_ty = self.resolve_vars_if_possible(callee_ty); + let mut err = type_error_struct!( + self.tcx.sess, + callee_expr.span, + callee_ty, + E0618, + "expected function, found {}", + match &unit_variant { + Some((_, kind, path)) => format!("{kind} `{path}`"), + None => format!("`{callee_ty}`"), + } + ); + + self.identify_bad_closure_def_and_call( + &mut err, + call_expr.hir_id, + &callee_expr.kind, + callee_expr.span, + ); + + if let Some((removal_span, kind, path)) = &unit_variant { + err.span_suggestion_verbose( + *removal_span, + &format!( + "`{path}` is a unit {kind}, and does not take parentheses to be constructed", + ), + "", + Applicability::MachineApplicable, + ); + } + + let mut inner_callee_path = None; + let def = match callee_expr.kind { + hir::ExprKind::Path(ref qpath) => { + self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) + } + hir::ExprKind::Call(ref inner_callee, _) => { + // If the call spans more than one line and the callee kind is + // itself another `ExprCall`, that's a clue that we might just be + // missing a semicolon (Issue #51055) + let call_is_multiline = self.tcx.sess.source_map().is_multiline(call_expr.span); + if call_is_multiline { + err.span_suggestion( + callee_expr.span.shrink_to_hi(), + "consider using a semicolon here", + ";", + Applicability::MaybeIncorrect, + ); + } + if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.kind { + inner_callee_path = Some(inner_qpath); + self.typeck_results.borrow().qpath_res(inner_qpath, inner_callee.hir_id) + } else { + Res::Err + } + } + _ => Res::Err, + }; + + if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) { + if let Some((maybe_def, output_ty, _)) = + self.extract_callable_info(callee_expr, callee_ty) + && !self.type_is_sized_modulo_regions(self.param_env, output_ty, callee_expr.span) + { + let descr = match maybe_def { + DefIdOrName::DefId(def_id) => self.tcx.def_kind(def_id).descr(def_id), + DefIdOrName::Name(name) => name, + }; + err.span_label( + callee_expr.span, + format!("this {descr} returns an unsized value `{output_ty}`, so it cannot be called") + ); + if let DefIdOrName::DefId(def_id) = maybe_def + && let Some(def_span) = self.tcx.hir().span_if_local(def_id) + { + err.span_label(def_span, "the callable type is defined here"); + } + } else { + err.span_label(call_expr.span, "call expression requires function"); + } + } + + if let Some(span) = self.tcx.hir().res_span(def) { + let callee_ty = callee_ty.to_string(); + let label = match (unit_variant, inner_callee_path) { + (Some((_, kind, path)), _) => Some(format!("{kind} `{path}` defined here")), + (_, Some(hir::QPath::Resolved(_, path))) => self + .tcx + .sess + .source_map() + .span_to_snippet(path.span) + .ok() + .map(|p| format!("`{p}` defined here returns `{callee_ty}`")), + _ => { + match def { + // Emit a different diagnostic for local variables, as they are not + // type definitions themselves, but rather variables *of* that type. + Res::Local(hir_id) => Some(format!( + "`{}` has type `{}`", + self.tcx.hir().name(hir_id), + callee_ty + )), + Res::Def(kind, def_id) if kind.ns() == Some(Namespace::ValueNS) => { + Some(format!("`{}` defined here", self.tcx.def_path_str(def_id),)) + } + _ => Some(format!("`{callee_ty}` defined here")), + } + } + }; + if let Some(label) = label { + err.span_label(span, label); + } + } + err.emit(); + } + fn confirm_deferred_closure_call( &self, call_expr: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 81a979865..d1dab0540 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -30,20 +30,18 @@ use super::FnCtxt; -use crate::hir::def_id::DefId; use crate::type_error_struct; -use hir::def_id::LOCAL_CRATE; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; -use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::cast::{CastKind, CastTy}; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Binder, Ty, TypeAndMut, TypeVisitable, VariantDef}; +use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitable, VariantDef}; use rustc_session::lint; use rustc_session::Session; +use rustc_span::def_id::{DefId, LOCAL_CRATE}; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; @@ -62,6 +60,8 @@ pub struct CastCheck<'tcx> { cast_ty: Ty<'tcx>, cast_span: Span, span: Span, + /// whether the cast is made in a const context or not. + pub constness: hir::Constness, } /// The kind of pointer and associated metadata (thin, length or vtable) - we @@ -204,85 +204,18 @@ fn make_invalid_casting_error<'a, 'tcx>( ) } -pub enum CastCheckResult<'tcx> { - Ok, - Deferred(CastCheck<'tcx>), - Err(ErrorGuaranteed), -} - -pub fn check_cast<'tcx>( - fcx: &FnCtxt<'_, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, - expr_ty: Ty<'tcx>, - cast_ty: Ty<'tcx>, - cast_span: Span, - span: Span, -) -> CastCheckResult<'tcx> { - if cast_ty.is_dyn_star() { - check_dyn_star_cast(fcx, expr, expr_ty, cast_ty) - } else { - match CastCheck::new(fcx, expr, expr_ty, cast_ty, cast_span, span) { - Ok(check) => CastCheckResult::Deferred(check), - Err(e) => CastCheckResult::Err(e), - } - } -} - -fn check_dyn_star_cast<'tcx>( - fcx: &FnCtxt<'_, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, - expr_ty: Ty<'tcx>, - cast_ty: Ty<'tcx>, -) -> CastCheckResult<'tcx> { - // Find the bounds in the dyn*. For eaxmple, if we have - // - // let x = 22_usize as dyn* (Clone + Debug + 'static) - // - // this would return `existential_predicates = [?Self: Clone, ?Self: Debug]` and `region = 'static`. - let (existential_predicates, region) = match cast_ty.kind() { - ty::Dynamic(predicates, region, ty::DynStar) => (predicates, region), - _ => panic!("Invalid dyn* cast_ty"), - }; - - let cause = ObligationCause::new( - expr.span, - fcx.body_id, - // FIXME(dyn-star): Use a better obligation cause code - ObligationCauseCode::MiscObligation, - ); - - // For each existential predicate (e.g., `?Self: Clone`) substitute - // the type of the expression (e.g., `usize` in our example above) - // and then require that the resulting predicate (e.g., `usize: Clone`) - // holds (it does). - for existential_predicate in existential_predicates.iter() { - let predicate = existential_predicate.with_self_ty(fcx.tcx, expr_ty); - fcx.register_predicate(Obligation::new(cause.clone(), fcx.param_env, predicate)); - } - - // Enforce the region bound `'static` (e.g., `usize: 'static`, in our example). - fcx.register_predicate(Obligation::new( - cause, - fcx.param_env, - fcx.tcx.mk_predicate(Binder::dummy(ty::PredicateKind::TypeOutlives( - ty::OutlivesPredicate(expr_ty, *region), - ))), - )); - - CastCheckResult::Ok -} - impl<'a, 'tcx> CastCheck<'tcx> { - fn new( + pub fn new( fcx: &FnCtxt<'a, 'tcx>, expr: &'tcx hir::Expr<'tcx>, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, cast_span: Span, span: Span, + constness: hir::Constness, ) -> Result<CastCheck<'tcx>, ErrorGuaranteed> { let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span); - let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span }; + let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span, constness }; // For better error messages, check for some obviously unsized // cases now. We do a more thorough check at the end, once @@ -596,7 +529,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { err.emit(); } CastError::SizedUnsizedCast => { - use crate::structured_errors::{SizedUnsizedCast, StructuredDiagnostic}; + use rustc_hir_analysis::structured_errors::{ + SizedUnsizedCast, StructuredDiagnostic, + }; SizedUnsizedCast { sess: &fcx.tcx.sess, @@ -754,19 +689,25 @@ impl<'a, 'tcx> CastCheck<'tcx> { } else { ("", lint::builtin::TRIVIAL_CASTS) }; - fcx.tcx.struct_span_lint_hir(lint, self.expr.hir_id, self.span, |err| { - err.build(&format!( - "trivial {}cast: `{}` as `{}`", - adjective, - fcx.ty_to_string(t_expr), - fcx.ty_to_string(t_cast) - )) - .help(&format!( - "cast can be replaced by coercion; this might \ - require {type_asc_or}a temporary variable" - )) - .emit(); - }); + fcx.tcx.struct_span_lint_hir( + lint, + self.expr.hir_id, + self.span, + DelayDm(|| { + format!( + "trivial {}cast: `{}` as `{}`", + adjective, + fcx.ty_to_string(t_expr), + fcx.ty_to_string(t_cast) + ) + }), + |lint| { + lint.help(format!( + "cast can be replaced by coercion; this might \ + require {type_asc_or}a temporary variable" + )) + }, + ); } #[instrument(skip(fcx), level = "debug")] @@ -928,11 +869,13 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), - // FIXME(dyn-star): this needs more conditions... - (_, DynStar) => Ok(CastKind::DynStarCast), - - // FIXME(dyn-star): do we want to allow dyn* upcasting or other casts? - (DynStar, _) => Err(CastError::IllegalCast), + (_, DynStar) | (DynStar, _) => { + if fcx.tcx.features().dyn_star { + bug!("should be handled by `try_coerce`") + } else { + Err(CastError::IllegalCast) + } + } } } @@ -1074,12 +1017,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { lint::builtin::CENUM_IMPL_DROP_CAST, self.expr.hir_id, self.span, - |err| { - err.build(&format!( - "cannot cast enum `{}` into integer `{}` because it implements `Drop`", - self.expr_ty, self.cast_ty - )) - .emit(); + DelayDm(|| format!( + "cannot cast enum `{}` into integer `{}` because it implements `Drop`", + self.expr_ty, self.cast_ty + )), + |lint| { + lint }, ); } @@ -1090,12 +1033,11 @@ impl<'a, 'tcx> CastCheck<'tcx> { lint::builtin::LOSSY_PROVENANCE_CASTS, self.expr.hir_id, self.span, - |err| { - let mut err = err.build(&format!( + DelayDm(|| format!( "under strict provenance it is considered bad style to cast pointer `{}` to integer `{}`", self.expr_ty, self.cast_ty - )); - + )), + |lint| { let msg = "use `.addr()` to obtain the address of a pointer"; let expr_prec = self.expr.precedence().order(); @@ -1114,9 +1056,9 @@ impl<'a, 'tcx> CastCheck<'tcx> { (cast_span, format!(").addr(){scalar_cast}")), ]; - err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); + lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); } else { - err.span_suggestion( + lint.span_suggestion( cast_span, msg, format!(".addr(){scalar_cast}"), @@ -1124,12 +1066,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { ); } - err.help( + lint.help( "if you can't comply with strict provenance and need to expose the pointer \ provenance you can use `.expose_addr()` instead" ); - err.emit(); + lint }, ); } @@ -1139,24 +1081,24 @@ impl<'a, 'tcx> CastCheck<'tcx> { lint::builtin::FUZZY_PROVENANCE_CASTS, self.expr.hir_id, self.span, - |err| { - let mut err = err.build(&format!( - "strict provenance disallows casting integer `{}` to pointer `{}`", - self.expr_ty, self.cast_ty - )); + DelayDm(|| format!( + "strict provenance disallows casting integer `{}` to pointer `{}`", + self.expr_ty, self.cast_ty + )), + |lint| { let msg = "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address"; let suggestions = vec![ (self.expr_span.shrink_to_lo(), String::from("(...).with_addr(")), (self.expr_span.shrink_to_hi().to(self.cast_span), String::from(")")), ]; - err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); - err.help( + lint.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); + lint.help( "if you can't comply with strict provenance and don't have a pointer with \ the correct provenance you can use `std::ptr::from_exposed_addr()` instead" ); - err.emit(); + lint }, ); } diff --git a/compiler/rustc_typeck/src/check/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 9b943b160..a5a45f75e 100644 --- a/compiler/rustc_typeck/src/check/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -2,12 +2,11 @@ use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; -use crate::astconv::AstConv; -use crate::rustc_middle::ty::subst::Subst; use hir::def::DefKind; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_infer::infer::{InferOk, InferResult}; @@ -177,24 +176,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *expected_ty.kind() { ty::Opaque(def_id, substs) => { let bounds = self.tcx.bound_explicit_item_bounds(def_id); - let sig = bounds - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .find_map(|(pred, span)| match pred.0.kind().skip_binder() { + let sig = + bounds.subst_iter_copied(self.tcx, substs).find_map(|(pred, span)| match pred + .kind() + .skip_binder() + { ty::PredicateKind::Projection(proj_predicate) => self .deduce_sig_from_projection( - Some(span.0), - pred.0 - .kind() - .rebind(pred.rebind(proj_predicate).subst(self.tcx, substs)), + Some(span), + pred.kind().rebind(proj_predicate), ), _ => None, }); let kind = bounds - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .filter_map(|(pred, _)| match pred.0.kind().skip_binder() { + .0 + .iter() + .filter_map(|(pred, _)| match pred.kind().skip_binder() { ty::PredicateKind::Trait(tp) => { self.tcx.fn_trait_kind_from_lang_item(tp.def_id()) } @@ -642,6 +640,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), bound_vars, ); + // Astconv can't normalize inputs or outputs with escaping bound vars, + // so normalize them here, after we've wrapped them in a binder. + let result = self.normalize_associated_types_in(self.tcx.hir().span(hir_id), result); let c_result = self.inh.infcx.canonicalize_response(result); self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result); @@ -695,18 +696,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Opaque(def_id, substs) => self .tcx .bound_explicit_item_bounds(def_id) - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .find_map(|(p, s)| get_future_output(p.subst(self.tcx, substs), s.0))?, + .subst_iter_copied(self.tcx, substs) + .find_map(|(p, s)| get_future_output(p, s))?, ty::Error(_) => return None, ty::Projection(proj) if self.tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder => { self.tcx .bound_explicit_item_bounds(proj.item_def_id) - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .find_map(|(p, s)| get_future_output(p.subst(self.tcx, proj.substs), s.0))? + .subst_iter_copied(self.tcx, proj.substs) + .find_map(|(p, s)| get_future_output(p, s))? } _ => span_bug!( self.tcx.def_span(expr_def_id), diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index def592c46..86597a703 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -35,8 +35,7 @@ //! // and are then unable to coerce `&7i32` to `&mut i32`. //! ``` -use crate::astconv::AstConv; -use crate::check::FnCtxt; +use crate::FnCtxt; use rustc_errors::{ struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, }; @@ -44,6 +43,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::Expr; +use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{Coercion, InferOk, InferResult}; use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt}; @@ -61,7 +61,7 @@ use rustc_span::symbol::sym; use rustc_span::{self, BytePos, DesugaringKind, Span}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; use smallvec::{smallvec, SmallVec}; @@ -216,6 +216,9 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { ty::Ref(r_b, _, mutbl_b) => { return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b); } + ty::Dynamic(predicates, region, ty::DynStar) if self.tcx.features().dyn_star => { + return self.coerce_dyn_star(a, b, predicates, region); + } _ => {} } @@ -702,7 +705,12 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Object safety violations or miscellaneous. Err(err) => { - self.report_selection_error(obligation.clone(), &obligation, &err, false); + self.err_ctxt().report_selection_error( + obligation.clone(), + &obligation, + &err, + false, + ); // Treat this like an obligation and follow through // with the unsizing - the lack of a coercion should // be silent, as it causes a type mismatch later. @@ -740,6 +748,63 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { Ok(coercion) } + fn coerce_dyn_star( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>, + b_region: ty::Region<'tcx>, + ) -> CoerceResult<'tcx> { + if !self.tcx.features().dyn_star { + return Err(TypeError::Mismatch); + } + + if let ty::Dynamic(a_data, _, _) = a.kind() + && let ty::Dynamic(b_data, _, _) = b.kind() + { + if a_data.principal_def_id() == b_data.principal_def_id() { + return self.unify_and(a, b, |_| vec![]); + } else if !self.tcx().features().trait_upcasting { + let mut err = feature_err( + &self.tcx.sess.parse_sess, + sym::trait_upcasting, + self.cause.span, + &format!( + "cannot cast `{a}` to `{b}`, trait upcasting coercion is experimental" + ), + ); + err.emit(); + } + } + + // Check the obligations of the cast -- for example, when casting + // `usize` to `dyn* Clone + 'static`: + let obligations = predicates + .iter() + .map(|predicate| { + // For each existential predicate (e.g., `?Self: Clone`) substitute + // the type of the expression (e.g., `usize` in our example above) + // and then require that the resulting predicate (e.g., `usize: Clone`) + // holds (it does). + let predicate = predicate.with_self_ty(self.tcx, a); + Obligation::new(self.cause.clone(), self.param_env, predicate) + }) + // Enforce the region bound (e.g., `usize: 'static`, in our example). + .chain([Obligation::new( + self.cause.clone(), + self.param_env, + self.tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::TypeOutlives( + ty::OutlivesPredicate(a, b_region), + ))), + )]) + .collect(); + + Ok(InferOk { + value: (vec![Adjustment { kind: Adjust::DynStar, target: b }], b), + obligations, + }) + } + fn coerce_from_safe_fn<F, G>( &self, a: Ty<'tcx>, @@ -1549,7 +1614,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } } _ => { - err = fcx.report_mismatched_types( + err = fcx.err_ctxt().report_mismatched_types( cause, expected, found, @@ -1629,7 +1694,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { expression: Option<&'tcx hir::Expr<'tcx>>, blk_id: Option<hir::HirId>, ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { - let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err); + let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err); let mut pointing_at_return_type = false; let mut fn_output = None; @@ -1683,7 +1748,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { expected, found, can_suggest, - fcx.tcx.hir().local_def_id_to_hir_id(fcx.tcx.hir().get_parent_item(id)), + fcx.tcx.hir().get_parent_item(id).into(), ); } if !pointing_at_return_type { @@ -1692,7 +1757,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } let parent_id = fcx.tcx.hir().get_parent_item(id); - let parent_item = fcx.tcx.hir().get_by_def_id(parent_id); + let parent_item = fcx.tcx.hir().get_by_def_id(parent_id.def_id); if let (Some(expr), Some(_), Some((fn_decl, _, _))) = (expression, blk_id, fcx.get_node_fn_decl(parent_item)) @@ -1704,7 +1769,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { expected, found, id, - fcx.tcx.hir().local_def_id_to_hir_id(parent_id), + parent_id.into(), ); } diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index e1d55ff82..16febfc46 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -1,21 +1,20 @@ -use crate::check::FnCtxt; -use rustc_infer::infer::InferOk; -use rustc_middle::middle::stability::EvalResult; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::ObligationCause; - +use crate::FnCtxt; use rustc_ast::util::parser::PREC_POSTFIX; use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::lang_items::LangItem; use rustc_hir::{is_range_literal, Node}; +use rustc_infer::infer::InferOk; use rustc_middle::lint::in_external_macro; +use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Span}; +use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::ObligationCause; use super::method::probe; @@ -32,17 +31,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: Option<TypeError<'tcx>>, ) { self.annotate_expected_due_to_let_ty(err, expr, error); - self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr); - self.suggest_compatible_variants(err, expr, expected, expr_ty); - self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty); - if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { - return; - } - self.suggest_no_capture_closure(err, expected, expr_ty); - self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty); - self.suggest_missing_parentheses(err, expr); - self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected); - self.suggest_copied_or_cloned(err, expr, expr_ty, expected); + + // Use `||` to give these suggestions a precedence + let _ = self.suggest_missing_parentheses(err, expr) + || self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr) + || self.suggest_compatible_variants(err, expr, expected, expr_ty) + || self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty) + || self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) + || self.suggest_no_capture_closure(err, expected, expr_ty) + || self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty) + || self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected) + || self.suggest_copied_or_cloned(err, expr, expr_ty, expected) + || self.suggest_into(err, expr, expr_ty, expected); + self.note_type_is_not_clone(err, expected, expr_ty, expr); self.note_need_for_fn_pointer(err, expected, expr_ty); self.note_internal_mutation_in_method(err, expr, expected, expr_ty); @@ -77,7 +78,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_predicates(obligations); None } - Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)), + Err(e) => Some(self.err_ctxt().report_mismatched_types(&cause, expected, actual, e)), } } @@ -107,7 +108,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.register_predicates(obligations); None } - Err(e) => Some(self.report_mismatched_types(cause, expected, actual, e)), + Err(e) => Some(self.err_ctxt().report_mismatched_types(cause, expected, actual, e)), } } @@ -151,7 +152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let expr = expr.peel_drop_temps(); let cause = self.misc(expr.span); let expr_ty = self.resolve_vars_with_obligations(checked_ty); - let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e.clone()); + let mut err = self.err_ctxt().report_mismatched_types(&cause, expected, expr_ty, e.clone()); let is_insufficiently_polymorphic = matches!(e, TypeError::RegionsInsufficientlyPolymorphic(..)); @@ -286,7 +287,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, expected: Ty<'tcx>, expr_ty: Ty<'tcx>, - ) { + ) -> bool { if let ty::Adt(expected_adt, substs) = expected.kind() { if let hir::ExprKind::Field(base, ident) = expr.kind { let base_ty = self.typeck_results.borrow().expr_ty(base); @@ -299,7 +300,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "", Applicability::MaybeIncorrect, ); - return + return true; } } @@ -338,7 +339,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) { vec!["None", "Some(())"] } else { - return; + return false; }; if let Some(indent) = self.tcx.sess.source_map().indentation_before(span.shrink_to_lo()) @@ -358,7 +359,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - return; + return true; } } } @@ -375,7 +376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let field_is_local = sole_field.did.is_local(); let field_is_accessible = - sole_field.vis.is_accessible_from(expr.hir_id.owner, self.tcx) + sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx) // Skip suggestions for unstable public fields (for example `Pin::pointer`) && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked); @@ -417,6 +418,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::def::CtorKind::Const => unreachable!(), }; + // Suggest constructor as deep into the block tree as possible. + // This fixes https://github.com/rust-lang/rust/issues/101065, + // and also just helps make the most minimal suggestions. + let mut expr = expr; + while let hir::ExprKind::Block(block, _) = &expr.kind + && let Some(expr_) = &block.expr + { + expr = expr_ + } + vec![ (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")), (expr.span.shrink_to_hi(), close.to_owned()), @@ -435,6 +446,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggestions_for(&**variant, *ctor_kind, *field_name), Applicability::MaybeIncorrect, ); + return true; } _ => { // More than one matching variant. @@ -450,9 +462,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), Applicability::MaybeIncorrect, ); + return true; } } } + + false } fn suggest_non_zero_new_unwrap( @@ -461,19 +476,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, expected: Ty<'tcx>, expr_ty: Ty<'tcx>, - ) { + ) -> bool { let tcx = self.tcx; let (adt, unwrap) = match expected.kind() { // In case Option<NonZero*> is wanted, but * is provided, suggest calling new ty::Adt(adt, substs) if tcx.is_diagnostic_item(sym::Option, adt.did()) => { // Unwrap option - let ty::Adt(adt, _) = substs.type_at(0).kind() else { return }; + let ty::Adt(adt, _) = substs.type_at(0).kind() else { return false; }; (adt, "") } // In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types ty::Adt(adt, _) => (adt, ".unwrap()"), - _ => return, + _ => return false, }; let map = [ @@ -492,7 +507,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let Some((s, _)) = map .iter() .find(|&&(s, t)| self.tcx.is_diagnostic_item(s, adt.did()) && self.can_coerce(expr_ty, t)) - else { return }; + else { return false; }; let path = self.tcx.def_path_str(adt.non_enum_variant().def_id); @@ -504,6 +519,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ], Applicability::MaybeIncorrect, ); + + true } pub fn get_conversion_methods( @@ -513,24 +530,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { checked_ty: Ty<'tcx>, hir_id: hir::HirId, ) -> Vec<AssocItem> { - let mut methods = - self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, hir_id); - methods.retain(|m| { - self.has_only_self_parameter(m) - && self - .tcx - // This special internal attribute is used to permit - // "identity-like" conversion methods to be suggested here. - // - // FIXME (#46459 and #46460): ideally - // `std::convert::Into::into` and `std::borrow:ToOwned` would - // also be `#[rustc_conversion_suggestion]`, if not for - // method-probing false-positives and -negatives (respectively). - // - // FIXME? Other potential candidate methods: `as_ref` and - // `as_mut`? - .has_attr(m.def_id, sym::rustc_conversion_suggestion) - }); + let methods = self.probe_for_return_type( + span, + probe::Mode::MethodCall, + expected, + checked_ty, + hir_id, + |m| { + self.has_only_self_parameter(m) + && self + .tcx + // This special internal attribute is used to permit + // "identity-like" conversion methods to be suggested here. + // + // FIXME (#46459 and #46460): ideally + // `std::convert::Into::into` and `std::borrow:ToOwned` would + // also be `#[rustc_conversion_suggestion]`, if not for + // method-probing false-positives and -negatives (respectively). + // + // FIXME? Other potential candidate methods: `as_ref` and + // `as_mut`? + .has_attr(m.def_id, sym::rustc_conversion_suggestion) + }, + ); methods } @@ -697,7 +719,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, - ) -> Option<(Span, String, String, Applicability, bool /* verbose */)> { + ) -> Option<( + Span, + String, + String, + Applicability, + bool, /* verbose */ + bool, /* suggest `&` or `&mut` type annotation */ + )> { let sess = self.sess(); let sp = expr.span; @@ -729,6 +758,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { String::new(), Applicability::MachineApplicable, true, + false, )); } } @@ -743,6 +773,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "b".to_string(), Applicability::MachineApplicable, true, + false, )); } } @@ -800,6 +831,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg.2, Applicability::MachineApplicable, false, + false, )); } @@ -827,6 +859,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("{prefix}&mut {sugg_expr}"), Applicability::MachineApplicable, false, + false, ), hir::Mutability::Not => ( sp, @@ -834,6 +867,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("{prefix}&{sugg_expr}"), Applicability::MachineApplicable, false, + false, ), }); } @@ -863,6 +897,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { String::new(), Applicability::MachineApplicable, true, + true )); } return None; @@ -876,6 +911,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { String::new(), Applicability::MachineApplicable, true, + true, )); } } @@ -942,6 +978,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { src, applicability, true, + false, )); } } @@ -982,6 +1019,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable }, true, + false, )); } @@ -1033,6 +1071,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggestion, Applicability::MachineApplicable, true, + false, )); } } diff --git a/compiler/rustc_typeck/src/check/diverges.rs b/compiler/rustc_hir_typeck/src/diverges.rs index 963a93a95..963a93a95 100644 --- a/compiler/rustc_typeck/src/check/diverges.rs +++ b/compiler/rustc_hir_typeck/src/diverges.rs diff --git a/compiler/rustc_typeck/src/check/expectation.rs b/compiler/rustc_hir_typeck/src/expectation.rs index e9e810344..e9e810344 100644 --- a/compiler/rustc_typeck/src/check/expectation.rs +++ b/compiler/rustc_hir_typeck/src/expectation.rs diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 93b008500..9fde62a81 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -2,23 +2,22 @@ //! //! See `mod.rs` for more context on type checking in general. -use crate::astconv::AstConv as _; -use crate::check::cast::{self, CastCheckResult}; -use crate::check::coercion::CoerceMany; -use crate::check::fatally_break_rust; -use crate::check::method::SelfSource; -use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; -use crate::check::{ - report_unexpected_variant_res, BreakableCtxt, Diverges, DynamicCoerceMany, FnCtxt, Needs, - TupleArgumentsFlag::DontTupleArguments, -}; +use crate::cast; +use crate::coercion::CoerceMany; +use crate::coercion::DynamicCoerceMany; +use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; use crate::errors::{ FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, YieldExprOutsideOfGenerator, }; +use crate::fatally_break_rust; +use crate::method::SelfSource; use crate::type_error_struct; - -use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; +use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; +use crate::{ + report_unexpected_variant_res, BreakableCtxt, Diverges, FnCtxt, Needs, + TupleArgumentsFlag::DontTupleArguments, +}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -32,6 +31,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{Closure, ExprKind, HirId, QPath}; +use rustc_hir_analysis::astconv::AstConv as _; +use rustc_hir_analysis::check::ty_kind_suggestion; use rustc_infer::infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::InferOk; @@ -41,6 +42,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::error::TypeError::FieldMisMatch; use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable}; +use rustc_session::errors::ExprParenthesesNeeded; use rustc_session::parse::feature_err; use rustc_span::hygiene::DesugaringKind; use rustc_span::lev_distance::find_best_match_for_name; @@ -394,7 +396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(sp) = tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { - tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp); + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } err.emit(); oprnd_t = tcx.ty_error(); @@ -541,7 +543,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // been resolved or we errored. This is important as we can only check transmute // on concrete types, but the output type may not be known yet (it would only // be known if explicitly specified via turbofish). - self.deferred_transmute_checks.borrow_mut().push((from, to, expr.span)); + self.deferred_transmute_checks.borrow_mut().push((from, to, expr.hir_id)); } if !tcx.features().unsized_fn_params { // We want to remove some Sized bounds from std functions, @@ -752,7 +754,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind: hir::ImplItemKind::Fn(..), span: encl_fn_span, .. - })) = self.tcx.hir().find_by_def_id(encl_item_id) + })) = self.tcx.hir().find_by_def_id(encl_item_id.def_id) { // We are inside a function body, so reporting "return statement // outside of function body" needs an explanation. @@ -761,7 +763,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If this didn't hold, we would not have to report an error in // the first place. - assert_ne!(encl_item_id, encl_body_owner_id); + assert_ne!(encl_item_id.def_id, encl_body_owner_id); let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id); let encl_body = self.tcx.hir().body(encl_body_id); @@ -881,7 +883,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set. + // FIXME: Make this use Diagnostic once error codes can be dynamically set. let mut err = self.tcx.sess.struct_span_err_with_code( op_span, "invalid left-hand side of assignment", @@ -1044,6 +1046,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let rhs_ty = self.check_expr(&rhs); let (applicability, eq) = if self.can_coerce(rhs_ty, lhs_ty) { (Applicability::MachineApplicable, true) + } else if let ExprKind::Binary( + Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + _, + rhs_expr, + ) = lhs.kind + { + // if x == 1 && y == 2 { .. } + // + + let actual_lhs_ty = self.check_expr(&rhs_expr); + (Applicability::MaybeIncorrect, self.can_coerce(rhs_ty, actual_lhs_ty)) + } else if let ExprKind::Binary( + Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. }, + lhs_expr, + _, + ) = rhs.kind + { + // if x == 1 && y == 2 { .. } + // + + let actual_rhs_ty = self.check_expr(&lhs_expr); + (Applicability::MaybeIncorrect, self.can_coerce(actual_rhs_ty, lhs_ty)) } else { (Applicability::MaybeIncorrect, false) }; @@ -1066,9 +1088,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if eq { err.span_suggestion_verbose( - span, + span.shrink_to_hi(), "you might have meant to compare for equality", - "==", + '=', applicability, ); } @@ -1109,11 +1131,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - self.check_lhs_assignable(lhs, "E0070", span, |err| { - let rhs_ty = self.check_expr(&rhs); - suggest_deref_binop(err, rhs_ty); - }); - // This is (basically) inlined `check_expr_coercable_to_type`, but we want // to suggest an additional fixup here in `suggest_deref_binop`. let rhs_ty = self.check_expr_with_hint(&rhs, lhs_ty); @@ -1124,6 +1141,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { diag.emit(); } + self.check_lhs_assignable(lhs, "E0070", span, |err| { + if let Some(rhs_ty) = self.typeck_results.borrow().expr_ty_opt(rhs) { + suggest_deref_binop(err, rhs_ty); + } + }); + self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); if lhs_ty.references_error() || rhs_ty.references_error() { @@ -1249,9 +1272,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // Defer other checks until we're done type checking. let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - match cast::check_cast(self, e, t_expr, t_cast, t.span, expr.span) { - CastCheckResult::Ok => t_cast, - CastCheckResult::Deferred(cast_check) => { + match cast::CastCheck::new( + self, + e, + t_expr, + t_cast, + t.span, + expr.span, + self.param_env.constness(), + ) { + Ok(cast_check) => { debug!( "check_expr_cast: deferring cast from {:?} to {:?}: {:?}", t_cast, t_expr, cast_check, @@ -1259,7 +1289,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { deferred_cast_checks.push(cast_check); t_cast } - CastCheckResult::Err(ErrorGuaranteed { .. }) => self.tcx.ty_error(), + Err(_) => self.tcx.ty_error(), } } } @@ -1341,7 +1371,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Create a new function context. let fcx = FnCtxt::new(self, self.param_env.with_const(), body.value.hir_id); - crate::check::GatherLocalsVisitor::new(&fcx).visit_body(body); + crate::GatherLocalsVisitor::new(&fcx).visit_body(body); let ty = fcx.check_expr_with_expectation(&body.value, expected); fcx.require_type_is_sized(ty, body.value.span, traits::ConstSized); @@ -1640,13 +1670,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Err(_) => { // This should never happen, since we're just subtyping the // remaining_fields, but it's fine to emit this, I guess. - self.report_mismatched_types( - &cause, - target_ty, - fru_ty, - FieldMisMatch(variant.name, ident.name), - ) - .emit(); + self.err_ctxt() + .report_mismatched_types( + &cause, + target_ty, + fru_ty, + FieldMisMatch(variant.name, ident.name), + ) + .emit(); } } } @@ -1933,7 +1964,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.set_tainted_by_errors(); return; } - let mut err = self.type_error_struct_with_diag( + let mut err = self.err_ctxt().type_error_struct_with_diag( field.ident.span, |actual| match ty.kind() { ty::Adt(adt, ..) if adt.is_enum() => struct_span_err!( @@ -2338,7 +2369,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let ty::Adt(def, _) = output_ty.kind() && !def.is_enum() { def.non_enum_variant().fields.iter().any(|field| { field.ident(self.tcx) == ident - && field.vis.is_accessible_from(expr.hir_id.owner, self.tcx) + && field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx) }) } else if let ty::Tuple(tys) = output_ty.kind() && let Ok(idx) = ident.as_str().parse::<usize>() @@ -2863,14 +2894,3 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - -pub(super) fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> { - Some(match ty.kind() { - ty::Bool => "true", - ty::Char => "'a'", - ty::Int(_) | ty::Uint(_) => "42", - ty::Float(_) => "3.14159", - ty::Error(_) | ty::Never => return None, - _ => "value", - }) -} diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index f483342b4..fce2a5888 100644 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -89,15 +89,6 @@ enum ConsumeMode { Move, } -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum MutateMode { - Init, - /// Example: `x = y` - JustWrite, - /// Example: `x += y` - WriteAndRead, -} - /// The ExprUseVisitor type /// /// This is the code that actually walks the tree. @@ -134,7 +125,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { /// - `typeck_results` --- typeck results for the code being analyzed pub fn new( delegate: &'a mut (dyn Delegate<'tcx> + 'a), - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, body_owner: LocalDefId, param_env: ty::ParamEnv<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>, @@ -583,7 +574,9 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { for adjustment in adjustments { debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment); match adjustment.kind { - adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => { + adjustment::Adjust::NeverToAny + | adjustment::Adjust::Pointer(_) + | adjustment::Adjust::DynStar => { // Creating a closure/fn-pointer or unsizing consumes // the input and stores it into the resulting rvalue. self.delegate_consume(&place_with_id, place_with_id.hir_id); diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 4059b3403..747ecb036 100644 --- a/compiler/rustc_typeck/src/check/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -1,4 +1,4 @@ -use crate::check::FnCtxt; +use crate::FnCtxt; use rustc_data_structures::{ fx::{FxHashMap, FxHashSet}, graph::WithSuccessors, @@ -72,7 +72,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { // // - Unconstrained ints are replaced with `i32`. // - // - Unconstrained floats are replaced with with `f64`. + // - Unconstrained floats are replaced with `f64`. // // - Non-numerics may get replaced with `()` or `!`, depending on // how they were categorized by `calculate_diverging_fallback` diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index a40478db9..6a1cffe3e 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -1,12 +1,7 @@ -use crate::astconv::{ - AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, - GenericArgCountResult, IsMethodCall, PathSeg, -}; -use crate::check::callee::{self, DeferredCallResolution}; -use crate::check::method::{self, MethodCallee, SelfSource}; -use crate::check::rvalue_scopes; -use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy}; - +use crate::callee::{self, DeferredCallResolution}; +use crate::method::{self, MethodCallee, SelfSource}; +use crate::rvalue_scopes; +use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; @@ -15,26 +10,28 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, GenericArg, Node, QPath}; +use rustc_hir_analysis::astconv::{ + AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, + GenericArgCountResult, IsMethodCall, PathSeg, +}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc_infer::infer::{InferOk, InferResult}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{ - self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts, -}; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPolyTraitRef, ToPredicate, Ty, UserType, }; +use rustc_middle::ty::{GenericArgKind, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{ self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, }; @@ -60,17 +57,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); - self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { - let msg = format!("unreachable {}", kind); - lint.build(&msg) - .span_label(span, &msg) - .span_label( + let msg = format!("unreachable {}", kind); + self.tcx().struct_span_lint_hir( + lint::builtin::UNREACHABLE_CODE, + id, + span, + &msg, + |lint| { + lint.span_label(span, &msg).span_label( orig_span, custom_note .unwrap_or("any code following this expression is unreachable"), ) - .emit(); - }) + }, + ) } } } @@ -90,14 +90,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>), ) -> Ty<'tcx> { // No Infer()? Nothing needs doing. - if !ty.has_infer_types_or_consts() { + if !ty.has_non_region_infer() { debug!("no inference var, nothing needs doing"); return ty; } // If `ty` is a type variable, see whether we already know what it is. ty = self.resolve_vars_if_possible(ty); - if !ty.has_infer_types_or_consts() { + if !ty.has_non_region_infer() { debug!(?ty); return ty; } @@ -489,18 +489,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> { match length { &hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span), - hir::ArrayLen::Body(anon_const) => self.to_const(anon_const), + hir::ArrayLen::Body(anon_const) => { + let const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); + let span = self.tcx.hir().span(anon_const.hir_id); + let c = ty::Const::from_anon_const(self.tcx, const_def_id); + self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None)); + self.normalize_associated_types_in(span, c) + } } } - pub fn to_const(&self, ast_c: &hir::AnonConst) -> ty::Const<'tcx> { - let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id); - let span = self.tcx.hir().span(ast_c.hir_id); - let c = ty::Const::from_anon_const(self.tcx, const_def_id); - self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None)); - self.normalize_associated_types_in(span, c) - } - pub fn const_arg_to_const( &self, ast_c: &hir::AnonConst, @@ -559,7 +557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. pub fn register_wf_obligation( &self, - arg: subst::GenericArg<'tcx>, + arg: ty::GenericArg<'tcx>, span: Span, code: traits::ObligationCauseCode<'tcx>, ) { @@ -604,9 +602,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut generators = self.deferred_generator_interiors.borrow_mut(); for (body_id, interior, kind) in generators.drain(..) { self.select_obligations_where_possible(false, |_| {}); - crate::check::generator_interior::resolve_interior( - self, def_id, body_id, interior, kind, - ); + crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind); } } @@ -616,7 +612,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !errors.is_empty() { self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); - self.report_fulfillment_errors(&errors, self.inh.body_id, false); + self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id, false); } } @@ -630,7 +626,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !result.is_empty() { mutate_fulfillment_errors(&mut result); self.adjust_fulfillment_errors_for_expr_obligation(&mut result); - self.report_fulfillment_errors(&result, self.inh.body_id, fallback_has_occurred); + self.err_ctxt().report_fulfillment_errors( + &result, + self.inh.body_id, + fallback_has_occurred, + ); } } @@ -1271,7 +1271,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut self, param: &ty::GenericParamDef, arg: &GenericArg<'_>, - ) -> subst::GenericArg<'tcx> { + ) -> ty::GenericArg<'tcx> { match (¶m.kind, arg) { (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { <dyn AstConv<'_>>::ast_region_to_region(self.fcx, lt, Some(param)).into() @@ -1295,10 +1295,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn inferred_kind( &mut self, - substs: Option<&[subst::GenericArg<'tcx>]>, + substs: Option<&[ty::GenericArg<'tcx>]>, param: &ty::GenericParamDef, infer_args: bool, - ) -> subst::GenericArg<'tcx> { + ) -> ty::GenericArg<'tcx> { let tcx = self.fcx.tcx(); match param.kind { GenericParamDefKind::Lifetime => { @@ -1410,7 +1410,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) } - #[instrument(level = "debug", skip(self, code, span, def_id, substs))] + #[instrument(level = "debug", skip(self, code, span, substs))] fn add_required_obligations_with_code( &self, span: Span, @@ -1418,15 +1418,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { substs: SubstsRef<'tcx>, code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>, ) { + let param_env = self.param_env; + + let remap = match self.tcx.def_kind(def_id) { + // Associated consts have `Self: ~const Trait` bounds that should be satisfiable when + // `Self: Trait` is satisfied because it does not matter whether the impl is `const`. + // Therefore we have to remap the param env here to be non-const. + hir::def::DefKind::AssocConst => true, + hir::def::DefKind::AssocFn + if self.tcx.def_kind(self.tcx.parent(def_id)) == hir::def::DefKind::Trait => + { + // N.B.: All callsites to this function involve checking a path expression. + // + // When instantiating a trait method as a function item, it does not actually matter whether + // the trait is `const` or not, or whether `where T: ~const Tr` needs to be satisfied as + // `const`. If we were to introduce instantiating trait methods as `const fn`s, we would + // check that after this, either via a bound `where F: ~const FnOnce` or when coercing to a + // `const fn` pointer. + // + // FIXME(fee1-dead) FIXME(const_trait_impl): update this doc when trait methods can satisfy + // `~const FnOnce` or can be coerced to `const fn` pointer. + true + } + _ => false, + }; let (bounds, _) = self.instantiate_bounds(span, def_id, &substs); - for obligation in traits::predicates_for_generics( + for mut obligation in traits::predicates_for_generics( |idx, predicate_span| { traits::ObligationCause::new(span, self.body_id, code(idx, predicate_span)) }, - self.param_env, + param_env, bounds, ) { + if remap { + obligation = obligation.without_const(self.tcx); + } self.register_predicate(obligation); } } @@ -1440,7 +1467,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty } else { if !self.is_tainted_by_errors() { - self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) + self.err_ctxt() + .emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) .emit(); } let err = self.tcx.ty_error(); diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs index fc83994ca..fc83994ca 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/arg_matrix.rs diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 311fcaada..8e0fcb56c 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1,19 +1,13 @@ -use crate::astconv::AstConv; -use crate::check::coercion::CoerceMany; -use crate::check::fn_ctxt::arg_matrix::{ - ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx, +use crate::coercion::CoerceMany; +use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx}; +use crate::gather_locals::Declaration; +use crate::method::MethodCallee; +use crate::Expectation::*; +use crate::TupleArgumentsFlag::*; +use crate::{ + struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, Needs, + TupleArgumentsFlag, }; -use crate::check::gather_locals::Declaration; -use crate::check::intrinsicck::InlineAsmCtxt; -use crate::check::method::MethodCallee; -use crate::check::Expectation::*; -use crate::check::TupleArgumentsFlag::*; -use crate::check::{ - potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, - LocalTy, Needs, TupleArgumentsFlag, -}; -use crate::structured_errors::StructuredDiagnostic; - use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan}; @@ -21,6 +15,10 @@ use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{ExprKind, Node, QPath}; +use rustc_hir_analysis::astconv::AstConv; +use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; +use rustc_hir_analysis::check::potentially_plural_count; +use rustc_hir_analysis::structured_errors::StructuredDiagnostic; use rustc_index::vec::IndexVec; use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; @@ -35,23 +33,34 @@ use rustc_span::{self, sym, Span}; use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; use std::iter; +use std::mem; use std::ops::ControlFlow; use std::slice; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub(in super::super) fn check_casts(&self) { - let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); + pub(in super::super) fn check_casts(&mut self) { + // don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors + // when writing to `self.param_env`. + let mut deferred_cast_checks = mem::take(&mut *self.deferred_cast_checks.borrow_mut()); + debug!("FnCtxt::check_casts: {} deferred checks", deferred_cast_checks.len()); for cast in deferred_cast_checks.drain(..) { + let prev_env = self.param_env; + self.param_env = self.param_env.with_constness(cast.constness); + cast.check(self); + + self.param_env = prev_env; } + + *self.deferred_cast_checks.borrow_mut() = deferred_cast_checks; } pub(in super::super) fn check_transmutes(&self) { let mut deferred_transmute_checks = self.deferred_transmute_checks.borrow_mut(); debug!("FnCtxt::check_transmutes: {} deferred checks", deferred_transmute_checks.len()); - for (from, to, span) in deferred_transmute_checks.drain(..) { - self.check_transmute(span, from, to); + for (from, to, hir_id) in deferred_transmute_checks.drain(..) { + self.check_transmute(from, to, hir_id); } } @@ -63,7 +72,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let get_operand_ty = |expr| { let ty = self.typeck_results.borrow().expr_ty_adjusted(expr); let ty = self.resolve_vars_if_possible(ty); - if ty.has_infer_types_or_consts() { + if ty.has_non_region_infer() { assert!(self.is_tainted_by_errors()); self.tcx.ty_error() } else { @@ -391,7 +400,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty: Ty<'tcx>, cast_ty: &str, ) { - use crate::structured_errors::MissingCastForVariadicArg; + use rustc_hir_analysis::structured_errors::MissingCastForVariadicArg; MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit(); } @@ -650,7 +659,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if tys.len() == 1 { // A tuple wrap suggestion actually occurs within, // so don't do anything special here. - err = self.report_and_explain_type_error( + err = self.err_ctxt().report_and_explain_type_error( TypeTrace::types( &self.misc(*lo), true, @@ -742,7 +751,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let cause = &self.misc(provided_span); let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) { - self.report_and_explain_type_error(trace, *e).emit(); + self.err_ctxt().report_and_explain_type_error(trace, *e).emit(); return true; } false @@ -766,7 +775,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx]; let cause = &self.misc(provided_arg_span); let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); - let mut err = self.report_and_explain_type_error(trace, *err); + let mut err = self.err_ctxt().report_and_explain_type_error(trace, *err); self.emit_coerce_suggestions( &mut err, &provided_args[*provided_idx], @@ -840,7 +849,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let cause = &self.misc(provided_span); let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); if let Some(e) = error { - self.note_type_err( + self.err_ctxt().note_type_err( &mut err, &trace.cause, None, @@ -1199,7 +1208,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => bug!("unexpected type: {:?}", ty), }, Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) - | Res::SelfTy { .. } => match ty.kind() { + | Res::SelfTyParam { .. } + | Res::SelfTyAlias { .. } => match ty.kind() { ty::Adt(adt, substs) if !adt.is_enum() => { Some((adt.non_enum_variant(), adt.did(), substs)) } @@ -1473,7 +1483,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &mut |err| { if let Some(expected_ty) = expected.only_has_type(self) { if !self.consider_removing_semicolon(blk, expected_ty, err) { - self.consider_returning_binding(blk, expected_ty, err); + self.err_ctxt().consider_returning_binding( + blk, + expected_ty, + err, + ); } if expected_ty == self.tcx.types.bool { // If this is caused by a missing `let` in a `while let`, @@ -1543,7 +1557,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } fn parent_item_span(&self, id: hir::HirId) -> Option<Span> { - let node = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(id)); + let node = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(id).def_id); match node { Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { @@ -1559,7 +1573,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { - let parent = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(blk_id)); + let parent = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(blk_id).def_id); self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs index 0e22971d3..0c600daf4 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs @@ -6,13 +6,13 @@ mod suggestions; pub use _impl::*; pub use suggestions::*; -use crate::astconv::AstConv; -use crate::check::coercion::DynamicCoerceMany; -use crate::check::{Diverges, EnclosingBreakables, Inherited, UnsafetyState}; - +use crate::coercion::DynamicCoerceMany; +use crate::{Diverges, EnclosingBreakables, Inherited, UnsafetyState}; use rustc_hir as hir; use rustc_hir::def_id::DefId; +use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer; +use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::ty::subst::GenericArgKind; @@ -35,7 +35,7 @@ use std::ops::Deref; /// /// See [`ItemCtxt`]'s docs for more. /// -/// [`ItemCtxt`]: crate::collect::ItemCtxt +/// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt /// [`InferCtxt`]: infer::InferCtxt pub struct FnCtxt<'a, 'tcx> { pub(super) body_id: hir::HirId, @@ -117,7 +117,7 @@ pub struct FnCtxt<'a, 'tcx> { pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>, - pub(super) inh: &'a Inherited<'a, 'tcx>, + pub(super) inh: &'a Inherited<'tcx>, /// True if the function or closure's return type is known before /// entering the function/closure, i.e. if the return type is @@ -131,7 +131,7 @@ pub struct FnCtxt<'a, 'tcx> { impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn new( - inh: &'a Inherited<'a, 'tcx>, + inh: &'a Inherited<'tcx>, param_env: ty::ParamEnv<'tcx>, body_id: hir::HirId, ) -> FnCtxt<'a, 'tcx> { @@ -168,13 +168,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self.tcx.sess } + /// 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`. + /// + /// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt + pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> { + TypeErrCtxt { infcx: &self.infcx, typeck_results: Some(self.typeck_results.borrow()) } + } + pub fn errors_reported_since_creation(&self) -> bool { self.tcx.sess.err_count() > self.err_count_on_creation } } impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { - type Target = Inherited<'a, 'tcx>; + type Target = Inherited<'tcx>; fn deref(&self) -> &Self::Target { &self.inh } @@ -276,7 +285,6 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item( self, - self.tcx, span, item_def_id, item_segment, diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index ee0ad7b5d..4db9c56f9 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1,9 +1,7 @@ use super::FnCtxt; -use crate::astconv::AstConv; -use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; -use hir::def_id::DefId; -use rustc_ast::util::parser::ExprPrecedence; +use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; +use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind}; @@ -11,13 +9,16 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{ Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, }; +use rustc_hir_analysis::astconv::AstConv; use rustc_infer::infer::{self, TyCtxtInferExt}; use rustc_infer::traits::{self, StatementAsExpression}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty}; +use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty}; +use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -64,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// When encountering an fn-like type, try accessing the output of the type - /// // and suggesting calling it if it satisfies a predicate (i.e. if the + /// and suggesting calling it if it satisfies a predicate (i.e. if the /// output has a method or a field): /// ```compile_fail,E0308 /// fn foo(x: usize) -> usize { x } @@ -89,7 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if ty.is_suggestable(self.tcx, false) { format!("/* {ty} */") } else { - "".to_string() + "/* value */".to_string() } }) .collect::<Vec<_>>() @@ -101,10 +102,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let msg = match def_id_or_name { DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { - DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(), - DefKind::Ctor(CtorOf::Variant, _) => { - "instantiate this tuple variant".to_string() - } + DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(), + DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(), kind => format!("call this {}", kind.descr(def_id)), }, DefIdOrName::Name(name) => format!("call this {name}"), @@ -138,7 +137,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg, applicability, ); - return true; } false @@ -327,9 +325,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - ) { + ) -> bool { let expr = expr.peel_blocks(); - if let Some((sp, msg, suggestion, applicability, verbose)) = + if let Some((sp, msg, suggestion, applicability, verbose, annotation)) = self.check_ref(expr, found, expected) { if verbose { @@ -337,12 +335,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.span_suggestion(sp, &msg, suggestion, applicability); } + if annotation { + let suggest_annotation = match expr.peel_drop_temps().kind { + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, _) => "&", + hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) => "&mut ", + _ => return true, + }; + let mut tuple_indexes = Vec::new(); + let mut expr_id = expr.hir_id; + for (parent_id, node) in self.tcx.hir().parent_iter(expr.hir_id) { + match node { + Node::Expr(&Expr { kind: ExprKind::Tup(subs), .. }) => { + tuple_indexes.push( + subs.iter() + .enumerate() + .find(|(_, sub_expr)| sub_expr.hir_id == expr_id) + .unwrap() + .0, + ); + expr_id = parent_id; + } + Node::Local(local) => { + if let Some(mut ty) = local.ty { + while let Some(index) = tuple_indexes.pop() { + match ty.kind { + TyKind::Tup(tys) => ty = &tys[index], + _ => return true, + } + } + let annotation_span = ty.span; + err.span_suggestion( + annotation_span.with_hi(annotation_span.lo()), + format!("alternatively, consider changing the type annotation"), + suggest_annotation, + Applicability::MaybeIncorrect, + ); + } + break; + } + _ => break, + } + } + } + return true; + } else if self.suggest_else_fn_with_closure(err, expr, found, expected) { + return true; } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) && let ty::FnDef(def_id, ..) = &found.kind() && let Some(sp) = self.tcx.hir().span_if_local(*def_id) { err.span_label(sp, format!("{found} defined here")); - } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { + return true; + } else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) { + return true; + } else { let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); if !methods.is_empty() { let mut suggestions = methods.iter() @@ -393,6 +439,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { suggestions, Applicability::MaybeIncorrect, ); + return true; } } else if let ty::Adt(found_adt, found_substs) = found.kind() && self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) @@ -417,9 +464,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)), Applicability::MaybeIncorrect, ); + return true; } } } + + false } /// When encountering the expected boxed value allocated in the stack, suggest allocating it @@ -430,13 +480,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, expected: Ty<'tcx>, found: Ty<'tcx>, - ) { + ) -> bool { if self.tcx.hir().is_inside_const_context(expr.hir_id) { // Do not suggest `Box::new` in const context. - return; + return false; } if !expected.is_box() || found.is_box() { - return; + return false; } let boxed_found = self.tcx.mk_box(found); if self.can_coerce(boxed_found, expected) { @@ -454,6 +504,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { https://doc.rust-lang.org/rust-by-example/std/box.html, and \ https://doc.rust-lang.org/std/boxed/index.html", ); + true + } else { + false } } @@ -464,7 +517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err: &mut Diagnostic, expected: Ty<'tcx>, found: Ty<'tcx>, - ) { + ) -> bool { if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) { if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) { // Report upto four upvars being captured to reduce the amount error messages @@ -488,8 +541,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { multi_span, "closures can only be coerced to `fn` types if they do not capture any variables" ); + return true; } } + false } /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. @@ -860,18 +915,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); let ty = self.normalize_associated_types_in(expr.span, ty); let ty = match self.tcx.asyncness(fn_id.owner) { - hir::IsAsync::Async => self - .tcx - .infer_ctxt() - .enter(|infcx| { - infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| { + hir::IsAsync::Async => { + let infcx = self.tcx.infer_ctxt().build(); + infcx + .get_impl_future_output_ty(ty) + .unwrap_or_else(|| { span_bug!( fn_decl.output.span(), "failed to get output type of async function" ) }) - }) - .skip_binder(), + .skip_binder() + } hir::IsAsync::NotAsync => ty, }; if self.can_coerce(found, ty) { @@ -891,11 +946,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut Diagnostic, expr: &hir::Expr<'_>, - ) { + ) -> bool { let sp = self.tcx.sess.source_map().start_point(expr.span); if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` - self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp); + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); + true + } else { + false } } @@ -908,7 +966,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mut expr: &hir::Expr<'_>, mut expr_ty: Ty<'tcx>, mut expected_ty: Ty<'tcx>, - ) { + ) -> bool { loop { match (&expr.kind, expr_ty.kind(), expected_ty.kind()) { ( @@ -922,9 +980,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } (hir::ExprKind::Block(blk, _), _, _) => { self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty); - break; + break true; } - _ => break, + _ => break false, } } } @@ -935,11 +993,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &hir::Expr<'_>, expr_ty: Ty<'tcx>, expected_ty: Ty<'tcx>, - ) { - let ty::Adt(adt_def, substs) = expr_ty.kind() else { return; }; - let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return; }; + ) -> bool { + let ty::Adt(adt_def, substs) = expr_ty.kind() else { return false; }; + let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return false; }; if adt_def != expected_adt_def { - return; + return false; } let mut suggest_copied_or_cloned = || { @@ -958,6 +1016,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ".copied()", Applicability::MachineApplicable, ); + return true; } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( self, @@ -975,8 +1034,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ".cloned()", Applicability::MachineApplicable, ); + return true; } } + false }; if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result) @@ -984,12 +1045,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Check that the error types are equal && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok() { - suggest_copied_or_cloned(); + return suggest_copied_or_cloned(); } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option) && adt_def.did() == option_did { - suggest_copied_or_cloned(); + return suggest_copied_or_cloned(); } + + false + } + + pub(crate) fn suggest_into( + &self, + diag: &mut Diagnostic, + expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + expected_ty: Ty<'tcx>, + ) -> bool { + let expr = expr.peel_blocks(); + + // We have better suggestions for scalar interconversions... + if expr_ty.is_scalar() && expected_ty.is_scalar() { + return false; + } + + // Don't suggest turning a block into another type (e.g. `{}.into()`) + if matches!(expr.kind, hir::ExprKind::Block(..)) { + return false; + } + + // We'll later suggest `.as_ref` when noting the type error, + // so skip if we will suggest that instead. + if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() { + return false; + } + + if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into) + && self.predicate_must_hold_modulo_regions(&traits::Obligation::new( + self.misc(expr.span), + self.param_env, + ty::Binder::dummy(ty::TraitRef { + def_id: into_def_id, + substs: self.tcx.mk_substs_trait(expr_ty, &[expected_ty.into()]), + }) + .to_poly_trait_predicate() + .to_predicate(self.tcx), + )) + { + let sugg = if expr.precedence().order() >= PREC_POSTFIX { + vec![(expr.span.shrink_to_hi(), ".into()".to_owned())] + } else { + vec![(expr.span.shrink_to_lo(), "(".to_owned()), (expr.span.shrink_to_hi(), ").into()".to_owned())] + }; + diag.multipart_suggestion( + format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"), + sugg, + Applicability::MaybeIncorrect + ); + return true; + } + + false } /// Suggest wrapping the block in square brackets instead of curly braces @@ -1110,7 +1226,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty: Ty<'tcx>, err: &mut Diagnostic, ) -> bool { - if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) { + if let Some((span_semi, boxed)) = self.err_ctxt().could_remove_semicolon(blk, expected_ty) { if let StatementAsExpression::NeedsBoxing = boxed { err.span_suggestion_verbose( span_semi, @@ -1121,7 +1237,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { err.span_suggestion_short( span_semi, - "remove this semicolon", + "remove this semicolon to return this value", "", Applicability::MachineApplicable, ); @@ -1132,8 +1248,3 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - -pub enum DefIdOrName { - DefId(DefId), - Name(&'static str), -} diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 8f34a970f..9a096f24f 100644 --- a/compiler/rustc_typeck/src/check/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -1,9 +1,10 @@ -use crate::check::{FnCtxt, LocalTy, UserType}; +use crate::{FnCtxt, LocalTy}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::PatKind; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::ty::Ty; +use rustc_middle::ty::UserType; use rustc_span::Span; use rustc_trait_selection::traits; diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs index 016f4056b..122ad7009 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_build.rs @@ -210,7 +210,7 @@ impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { } /// For an expression with an uninhabited return type (e.g. a function that returns !), - /// this adds a self edge to to the CFG to model the fact that the function does not + /// this adds a self edge to the CFG to model the fact that the function does not /// return. fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) { let ty = self.typeck_results.expr_ty(expr); diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs index 139d17d2e..139d17d2e 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_propagate.rs diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs index c0a0bfe8e..c0a0bfe8e 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/cfg_visualize.rs diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs index 518cd7342..4f3bdfbe7 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/mod.rs @@ -14,7 +14,7 @@ use self::cfg_build::build_control_flow_graph; use self::record_consumed_borrow::find_consumed_and_borrowed; -use crate::check::FnCtxt; +use crate::FnCtxt; use hir::def_id::DefId; use hir::{Body, HirId, HirIdMap, Node}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs index e22675e9d..bfe95852a 100644 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/drop_ranges/record_consumed_borrow.rs @@ -1,13 +1,16 @@ use super::TrackedValue; use crate::{ - check::FnCtxt, expr_use_visitor::{self, ExprUseVisitor}, + FnCtxt, }; use hir::{def_id::DefId, Body, HirId, HirIdMap}; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; -use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind}; use rustc_middle::ty::{ParamEnv, TyCtxt}; +use rustc_middle::{ + hir::place::{PlaceBase, Projection, ProjectionKind}, + ty::TypeVisitable, +}; pub(super) fn find_consumed_and_borrowed<'a, 'tcx>( fcx: &'a FnCtxt<'a, 'tcx>, @@ -198,7 +201,13 @@ impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { // If the type being assigned needs dropped, then the mutation counts as a borrow // since it is essentially doing `Drop::drop(&mut x); x = new_value;`. - if assignee_place.place.base_ty.needs_drop(self.tcx, self.param_env) { + let ty = self.tcx.erase_regions(assignee_place.place.base_ty); + if ty.needs_infer() { + self.tcx.sess.delay_span_bug( + self.tcx.hir().span(assignee_place.hir_id), + &format!("inference variables in {ty}"), + ); + } else if ty.needs_drop(self.tcx, self.param_env) { self.places .borrowed .insert(TrackedValue::from_place_with_projections_allowed(assignee_place)); diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs index 7ab6d9e2b..b7dd599cd 100644 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ b/compiler/rustc_hir_typeck/src/generator_interior/mod.rs @@ -6,7 +6,7 @@ use self::drop_ranges::DropRanges; use super::FnCtxt; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_errors::pluralize; +use rustc_errors::{pluralize, DelayDm}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; @@ -218,7 +218,8 @@ pub fn resolve_interior<'a, 'tcx>( .filter_map(|mut cause| { // Erase regions and canonicalize late-bound regions to deduplicate as many types as we // can. - let erased = fcx.tcx.erase_regions(cause.ty); + let ty = fcx.normalize_associated_types_in(cause.span, cause.ty); + let erased = fcx.tcx.erase_regions(ty); if captured_tys.insert(erased) { // Replace all regions inside the generator interior with late bound regions. // Note that each region slot in the types gets a new fresh late bound region, @@ -263,7 +264,7 @@ pub fn resolve_interior<'a, 'tcx>( // Unify the type variable inside the generator with the new witness match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(interior, witness) { Ok(ok) => fcx.register_infer_ok_obligations(ok), - _ => bug!(), + _ => bug!("failed to relate {interior} and {witness}"), } } @@ -376,15 +377,6 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { debug!("is_borrowed_temporary: {:?}", self.drop_ranges.is_borrowed_temporary(expr)); let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr); - let may_need_drop = |ty: Ty<'tcx>| { - // Avoid ICEs in needs_drop. - let ty = self.fcx.resolve_vars_if_possible(ty); - let ty = self.fcx.tcx.erase_regions(ty); - if ty.needs_infer() { - return true; - } - ty.needs_drop(self.fcx.tcx, self.fcx.param_env) - }; // Typically, the value produced by an expression is consumed by its parent in some way, // so we only have to check if the parent contains a yield (note that the parent may, for @@ -402,9 +394,18 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { // src/test/ui/generator/drop-tracking-parent-expression.rs. let scope = if self.drop_ranges.is_borrowed_temporary(expr) || ty.map_or(true, |ty| { - let needs_drop = may_need_drop(ty); - debug!(?needs_drop, ?ty); - needs_drop + // Avoid ICEs in needs_drop. + let ty = self.fcx.resolve_vars_if_possible(ty); + let ty = self.fcx.tcx.erase_regions(ty); + if ty.needs_infer() { + self.fcx + .tcx + .sess + .delay_span_bug(expr.span, &format!("inference variables in {ty}")); + true + } else { + ty.needs_drop(self.fcx.tcx, self.fcx.param_env) + } }) { self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) } else { @@ -609,33 +610,33 @@ fn check_must_not_suspend_def( rustc_session::lint::builtin::MUST_NOT_SUSPEND, hir_id, data.source_span, - |lint| { - let msg = format!( + DelayDm(|| { + format!( "{}`{}`{} held across a suspend point, but should not be", data.descr_pre, tcx.def_path_str(def_id), data.descr_post, - ); - let mut err = lint.build(&msg); - + ) + }), + |lint| { // add span pointing to the offending yield/await - err.span_label(data.yield_span, "the value is held across this suspend point"); + lint.span_label(data.yield_span, "the value is held across this suspend point"); // Add optional reason note if let Some(note) = attr.value_str() { // FIXME(guswynn): consider formatting this better - err.span_note(data.source_span, note.as_str()); + lint.span_note(data.source_span, note.as_str()); } // Add some quick suggestions on what to do // FIXME: can `drop` work as a suggestion here as well? - err.span_help( + lint.span_help( data.source_span, "consider using a block (`{ ... }`) \ to shrink the value's scope, ending before the suspend point", ); - err.emit(); + lint }, ); diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs index 37c830d4e..0fb7651b3 100644 --- a/compiler/rustc_typeck/src/check/inherited.rs +++ b/compiler/rustc_hir_typeck/src/inherited.rs @@ -6,7 +6,7 @@ use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::HirIdMap; use rustc_infer::infer; -use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; +use rustc_infer::infer::{DefiningAnchor, InferCtxt, InferOk, TyCtxtInferExt}; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::visit::TypeVisitable; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -29,10 +29,10 @@ use std::ops::Deref; /// Here, the function `foo()` and the closure passed to /// `bar()` will each have their own `FnCtxt`, but they will /// share the inherited fields. -pub struct Inherited<'a, 'tcx> { - pub(super) infcx: InferCtxt<'a, 'tcx>, +pub struct Inherited<'tcx> { + pub(super) infcx: InferCtxt<'tcx>, - pub(super) typeck_results: &'a RefCell<ty::TypeckResults<'tcx>>, + pub(super) typeck_results: RefCell<ty::TypeckResults<'tcx>>, pub(super) locals: RefCell<HirIdMap<super::LocalTy<'tcx>>>, @@ -55,7 +55,7 @@ pub struct Inherited<'a, 'tcx> { pub(super) deferred_cast_checks: RefCell<Vec<super::cast::CastCheck<'tcx>>>, - pub(super) deferred_transmute_checks: RefCell<Vec<(Ty<'tcx>, Ty<'tcx>, Span)>>, + pub(super) deferred_transmute_checks: RefCell<Vec<(Ty<'tcx>, Ty<'tcx>, hir::HirId)>>, pub(super) deferred_asm_checks: RefCell<Vec<(&'tcx hir::InlineAsm<'tcx>, hir::HirId)>>, @@ -70,22 +70,23 @@ pub struct Inherited<'a, 'tcx> { pub(super) diverging_type_vars: RefCell<FxHashSet<Ty<'tcx>>>, } -impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> { - type Target = InferCtxt<'a, 'tcx>; +impl<'tcx> Deref for Inherited<'tcx> { + type Target = InferCtxt<'tcx>; fn deref(&self) -> &Self::Target { &self.infcx } } /// A temporary returned by `Inherited::build(...)`. This is necessary -/// for multiple `InferCtxt` to share the same `in_progress_typeck_results` +/// for multiple `InferCtxt` to share the same `typeck_results` /// without using `Rc` or something similar. pub struct InheritedBuilder<'tcx> { infcx: infer::InferCtxtBuilder<'tcx>, def_id: LocalDefId, + typeck_results: RefCell<ty::TypeckResults<'tcx>>, } -impl<'tcx> Inherited<'_, 'tcx> { +impl<'tcx> Inherited<'tcx> { pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> { let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; @@ -93,7 +94,7 @@ impl<'tcx> Inherited<'_, 'tcx> { infcx: tcx .infer_ctxt() .ignoring_regions() - .with_fresh_in_progress_typeck_results(hir_owner) + .with_opaque_type_inference(DefiningAnchor::Bind(hir_owner.def_id)) .with_normalize_fn_sig_for_diagnostic(Lrc::new(move |infcx, fn_sig| { if fn_sig.has_escaping_bound_vars() { return fn_sig; @@ -117,26 +118,29 @@ impl<'tcx> Inherited<'_, 'tcx> { }) })), def_id, + typeck_results: RefCell::new(ty::TypeckResults::new(hir_owner)), } } } impl<'tcx> InheritedBuilder<'tcx> { - pub fn enter<F, R>(&mut self, f: F) -> R + pub fn enter<F, R>(mut self, f: F) -> R where - F: for<'a> FnOnce(Inherited<'a, 'tcx>) -> R, + F: FnOnce(&Inherited<'tcx>) -> R, { let def_id = self.def_id; - self.infcx.enter(|infcx| f(Inherited::new(infcx, def_id))) + f(&Inherited::new(self.infcx.build(), def_id, self.typeck_results)) } } -impl<'a, 'tcx> Inherited<'a, 'tcx> { - fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { +impl<'tcx> Inherited<'tcx> { + fn new( + infcx: InferCtxt<'tcx>, + def_id: LocalDefId, + typeck_results: RefCell<ty::TypeckResults<'tcx>>, + ) -> Self { let tcx = infcx.tcx; let body_id = tcx.hir().maybe_body_owned_by(def_id); - let typeck_results = - infcx.in_progress_typeck_results.expect("building `FnCtxt` without typeck results"); Inherited { typeck_results, diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index ced919f66..362f1c343 100644 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -92,7 +92,7 @@ impl HirNode for hir::Pat<'_> { #[derive(Clone)] pub(crate) struct MemCategorizationContext<'a, 'tcx> { pub(crate) typeck_results: &'a ty::TypeckResults<'tcx>, - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_owner: LocalDefId, upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>, @@ -103,7 +103,7 @@ pub(crate) type McResult<T> = Result<T, ()>; impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// Creates a `MemCategorizationContext`. pub(crate) fn new( - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, body_owner: LocalDefId, typeck_results: &'a ty::TypeckResults<'tcx>, @@ -184,7 +184,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// modes #42640) may look like `Some(x)` but in fact have /// implicit deref patterns attached (e.g., it is really /// `&Some(x)`). In that case, we return the "outermost" type - /// (e.g., `&Option<T>). + /// (e.g., `&Option<T>`). pub(crate) fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> { // Check for implicit `&` types wrapping the pattern; note // that these are never attached to binding patterns, so @@ -265,6 +265,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment) } + #[instrument(level = "debug", skip(self, previous))] fn cat_expr_adjusted_with<F>( &self, expr: &hir::Expr<'_>, @@ -274,7 +275,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { where F: FnOnce() -> McResult<PlaceWithHirId<'tcx>>, { - debug!("cat_expr_adjusted_with({:?}): {:?}", adjustment, expr); let target = self.resolve_vars_if_possible(adjustment.target); match adjustment.kind { adjustment::Adjust::Deref(overloaded) => { @@ -292,13 +292,15 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) - | adjustment::Adjust::Borrow(_) => { + | adjustment::Adjust::Borrow(_) + | adjustment::Adjust::DynStar => { // Result is an rvalue. Ok(self.cat_rvalue(expr.hir_id, expr.span, target)) } } } + #[instrument(level = "debug", skip(self))] pub(crate) fn cat_expr_unadjusted( &self, expr: &hir::Expr<'_>, @@ -387,6 +389,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } + #[instrument(level = "debug", skip(self, span))] pub(crate) fn cat_res( &self, hir_id: hir::HirId, @@ -394,8 +397,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { expr_ty: Ty<'tcx>, res: Res, ) -> McResult<PlaceWithHirId<'tcx>> { - debug!("cat_res: id={:?} expr={:?} def={:?}", hir_id, expr_ty, res); - match res { Res::Def( DefKind::Ctor(..) @@ -475,13 +476,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { ret } + #[instrument(level = "debug", skip(self))] fn cat_overloaded_place( &self, expr: &hir::Expr<'_>, base: &hir::Expr<'_>, ) -> McResult<PlaceWithHirId<'tcx>> { - debug!("cat_overloaded_place(expr={:?}, base={:?})", expr, base); - // Reconstruct the output assuming it's a reference with the // same region and mutability as the receiver. This holds for // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. @@ -497,13 +497,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.cat_deref(expr, base) } + #[instrument(level = "debug", skip(self, node))] fn cat_deref( &self, node: &impl HirNode, base_place: PlaceWithHirId<'tcx>, ) -> McResult<PlaceWithHirId<'tcx>> { - debug!("cat_deref: base_place={:?}", base_place); - let base_curr_ty = base_place.place.ty(); let deref_ty = match base_curr_ty.builtin_deref(true) { Some(mt) => mt.ty, @@ -562,7 +561,8 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _) | Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) | Res::SelfCtor(..) - | Res::SelfTy { .. } => { + | Res::SelfTyParam { .. } + | Res::SelfTyAlias { .. } => { // Structs and Unions have only have one variant. Ok(VariantIdx::new(0)) } diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 59fd5c315..be4ea9986 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -1,16 +1,16 @@ use super::{probe, MethodCallee}; -use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; -use crate::check::{callee, FnCtxt}; +use crate::{callee, FnCtxt}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::GenericArg; +use rustc_hir_analysis::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; use rustc_infer::infer::{self, InferOk}; use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{self, Subst, SubstsRef}; +use rustc_middle::ty::subst::{self, SubstsRef}; use rustc_middle::ty::{self, GenericParamDefKind, Ty}; use rustc_span::Span; use rustc_trait_selection::traits; diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 249e9c66b..a1278edef 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -10,15 +10,14 @@ mod suggest; pub use self::suggest::SelfSource; pub use self::MethodError::*; -use crate::check::{Expectation, FnCtxt}; -use crate::ObligationCause; +use crate::{Expectation, FnCtxt}; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace}; use rustc_hir::def_id::DefId; use rustc_infer::infer::{self, InferOk}; -use rustc_middle::ty::subst::Subst; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TypeVisitable}; use rustc_span::symbol::Ident; diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_hir_typeck/src/method/prelude2021.rs index 392695cca..3c98a2aa3 100644 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ b/compiler/rustc_hir_typeck/src/method/prelude2021.rs @@ -1,3 +1,7 @@ +use crate::{ + method::probe::{self, Pick}, + FnCtxt, +}; use hir::def_id::DefId; use hir::HirId; use hir::ItemKind; @@ -12,11 +16,6 @@ use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; -use crate::check::{ - method::probe::{self, Pick}, - FnCtxt, -}; - impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(super) fn lint_dot_call_from_2018( &self, @@ -82,14 +81,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prelude_or_array_lint, self_expr.hir_id, self_expr.span, + format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), |lint| { let sp = self_expr.span; - let mut lint = lint.build(&format!( - "trait method `{}` will become ambiguous in Rust 2021", - segment.ident.name - )); - let derefs = "*".repeat(pick.autoderefs); let autoref = match pick.autoref_or_ptr_adjustment { @@ -133,7 +128,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - lint.emit(); + lint }, ); } else { @@ -143,6 +138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { prelude_or_array_lint, call_expr.hir_id, call_expr.span, + format!("trait method `{}` will become ambiguous in Rust 2021", segment.ident.name), |lint| { let sp = call_expr.span; let trait_name = self.trait_path_or_bare_name( @@ -151,11 +147,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pick.item.container_id(self.tcx), ); - let mut lint = lint.build(&format!( - "trait method `{}` will become ambiguous in Rust 2021", - segment.ident.name - )); - let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp); if precise { let args = args @@ -202,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - lint.emit(); + lint }, ); } @@ -257,15 +248,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| { - // "type" refers to either a type or, more likely, a trait from which - // the associated function or method is from. - let container_id = pick.item.container_id(self.tcx); - let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id); - let trait_generics = self.tcx.generics_of(container_id); - - let trait_name = - if trait_generics.params.len() <= trait_generics.has_self as usize { + self.tcx.struct_span_lint_hir( + RUST_2021_PRELUDE_COLLISIONS, + expr_id, + span, + format!( + "trait-associated function `{}` will become ambiguous in Rust 2021", + method_name.name + ), + |lint| { + // "type" refers to either a type or, more likely, a trait from which + // the associated function or method is from. + let container_id = pick.item.container_id(self.tcx); + let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id); + let trait_generics = self.tcx.generics_of(container_id); + + let trait_name = if trait_generics.params.len() <= trait_generics.has_self as usize + { trait_path } else { let counts = trait_generics.own_counts(); @@ -282,44 +281,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }; - let mut lint = lint.build(&format!( - "trait-associated function `{}` will become ambiguous in Rust 2021", - method_name.name - )); - - let mut self_ty_name = self_ty_span - .find_ancestor_inside(span) - .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) - .unwrap_or_else(|| self_ty.to_string()); - - // Get the number of generics the self type has (if an Adt) unless we can determine that - // the user has written the self type with generics already which we (naively) do by looking - // for a "<" in `self_ty_name`. - if !self_ty_name.contains('<') { - if let Adt(def, _) = self_ty.kind() { - let generics = self.tcx.generics_of(def.did()); - if !generics.params.is_empty() { - let counts = generics.own_counts(); - self_ty_name += &format!( - "<{}>", - std::iter::repeat("'_") - .take(counts.lifetimes) - .chain(std::iter::repeat("_").take(counts.types + counts.consts)) - .collect::<Vec<_>>() - .join(", ") - ); + let mut self_ty_name = self_ty_span + .find_ancestor_inside(span) + .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) + .unwrap_or_else(|| self_ty.to_string()); + + // Get the number of generics the self type has (if an Adt) unless we can determine that + // the user has written the self type with generics already which we (naively) do by looking + // for a "<" in `self_ty_name`. + if !self_ty_name.contains('<') { + if let Adt(def, _) = self_ty.kind() { + let generics = self.tcx.generics_of(def.did()); + if !generics.params.is_empty() { + let counts = generics.own_counts(); + self_ty_name += &format!( + "<{}>", + std::iter::repeat("'_") + .take(counts.lifetimes) + .chain( + std::iter::repeat("_").take(counts.types + counts.consts) + ) + .collect::<Vec<_>>() + .join(", ") + ); + } } } - } - lint.span_suggestion( - span, - "disambiguate the associated function", - format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), - Applicability::MachineApplicable, - ); - - lint.emit(); - }); + lint.span_suggestion( + span, + "disambiguate the associated function", + format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), + Applicability::MachineApplicable, + ); + + lint + }, + ); } fn trait_path_or_bare_name( diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index e9f55ab34..28aa2302f 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -3,14 +3,12 @@ use super::CandidateSource; use super::MethodError; use super::NoMatchData; -use crate::check::FnCtxt; use crate::errors::MethodCallOnUnknownType; -use crate::hir::def::DefKind; -use crate::hir::def_id::DefId; - +use crate::FnCtxt; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def::Namespace; use rustc_infer::infer::canonical::OriginalQueryValues; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; @@ -19,10 +17,11 @@ use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::middle::stability; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable}; +use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_session::lint; +use rustc_span::def_id::DefId; use rustc_span::def_id::LocalDefId; use rustc_span::lev_distance::{ find_best_match_for_name_with_substrings, lev_distance_with_substrings, @@ -253,7 +252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// would result in an error (basically, the same criteria we /// would use to decide if a method is a plausible fit for /// ambiguity purposes). - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self, candidate_filter))] pub fn probe_for_return_type( &self, span: Span, @@ -261,6 +260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return_type: Ty<'tcx>, self_ty: Ty<'tcx>, scope_expr_id: hir::HirId, + candidate_filter: impl Fn(&ty::AssocItem) -> bool, ) -> Vec<ty::AssocItem> { let method_names = self .probe_op( @@ -272,7 +272,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty, scope_expr_id, ProbeScope::AllTraits, - |probe_cx| Ok(probe_cx.candidate_method_names()), + |probe_cx| Ok(probe_cx.candidate_method_names(candidate_filter)), ) .unwrap_or_default(); method_names @@ -409,9 +409,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint::builtin::TYVAR_BEHIND_RAW_POINTER, scope_expr_id, span, - |lint| { - lint.build("type annotations needed").emit(); - }, + "type annotations needed", + |lint| lint, ); } } else { @@ -473,69 +472,65 @@ fn method_autoderef_steps<'tcx>( ) -> MethodAutoderefStepsResult<'tcx> { debug!("method_autoderef_steps({:?})", goal); - tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| { - let ParamEnvAnd { param_env, value: self_ty } = goal; - - let mut autoderef = - Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP) - .include_raw_pointers() - .silence_errors(); - let mut reached_raw_pointer = false; - let mut steps: Vec<_> = autoderef - .by_ref() - .map(|(ty, d)| { - let step = CandidateStep { - self_ty: infcx.make_query_response_ignoring_pending_obligations( - inference_vars.clone(), - ty, - ), - autoderefs: d, - from_unsafe_deref: reached_raw_pointer, - unsize: false, - }; - if let ty::RawPtr(_) = ty.kind() { - // all the subsequent steps will be from_unsafe_deref - reached_raw_pointer = true; - } - step - }) - .collect(); - - let final_ty = autoderef.final_ty(true); - let opt_bad_ty = match final_ty.kind() { - ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { - reached_raw_pointer, - ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars, final_ty), - }), - ty::Array(elem_ty, _) => { - let dereferences = steps.len() - 1; - - steps.push(CandidateStep { - self_ty: infcx.make_query_response_ignoring_pending_obligations( - inference_vars, - infcx.tcx.mk_slice(*elem_ty), - ), - autoderefs: dereferences, - // this could be from an unsafe deref if we had - // a *mut/const [T; N] - from_unsafe_deref: reached_raw_pointer, - unsize: true, - }); - - None + let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal); + let ParamEnvAnd { param_env, value: self_ty } = goal; + + let mut autoderef = + Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP) + .include_raw_pointers() + .silence_errors(); + let mut reached_raw_pointer = false; + let mut steps: Vec<_> = autoderef + .by_ref() + .map(|(ty, d)| { + let step = CandidateStep { + self_ty: infcx + .make_query_response_ignoring_pending_obligations(inference_vars.clone(), ty), + autoderefs: d, + from_unsafe_deref: reached_raw_pointer, + unsize: false, + }; + if let ty::RawPtr(_) = ty.kind() { + // all the subsequent steps will be from_unsafe_deref + reached_raw_pointer = true; } - _ => None, - }; - - debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); + step + }) + .collect(); + + let final_ty = autoderef.final_ty(true); + let opt_bad_ty = match final_ty.kind() { + ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { + reached_raw_pointer, + ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty), + }), + ty::Array(elem_ty, _) => { + let dereferences = steps.len() - 1; + + steps.push(CandidateStep { + self_ty: infcx.make_query_response_ignoring_pending_obligations( + inference_vars, + infcx.tcx.mk_slice(*elem_ty), + ), + autoderefs: dereferences, + // this could be from an unsafe deref if we had + // a *mut/const [T; N] + from_unsafe_deref: reached_raw_pointer, + unsize: true, + }); - MethodAutoderefStepsResult { - steps: tcx.arena.alloc_from_iter(steps), - opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), - reached_recursion_limit: autoderef.reached_recursion_limit(), + None } - }) + _ => None, + }; + + debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); + + MethodAutoderefStepsResult { + steps: tcx.arena.alloc_from_iter(steps), + opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), + reached_recursion_limit: autoderef.reached_recursion_limit(), + } } impl<'a, 'tcx> ProbeContext<'a, 'tcx> { @@ -972,12 +967,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - fn candidate_method_names(&self) -> Vec<Ident> { + fn candidate_method_names( + &self, + candidate_filter: impl Fn(&ty::AssocItem) -> bool, + ) -> Vec<Ident> { let mut set = FxHashSet::default(); let mut names: Vec<_> = self .inherent_candidates .iter() .chain(&self.extension_candidates) + .filter(|candidate| candidate_filter(&candidate.item)) .filter(|candidate| { if let Some(return_ty) = self.return_type { self.matches_return_type(&candidate.item, None, return_ty) @@ -1358,24 +1357,24 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { stable_pick: &Pick<'_>, unstable_candidates: &[(Candidate<'tcx>, Symbol)], ) { + let def_kind = stable_pick.item.kind.as_def_kind(); self.tcx.struct_span_lint_hir( lint::builtin::UNSTABLE_NAME_COLLISIONS, self.scope_expr_id, self.span, + format!( + "{} {} with this name may be added to the standard library in the future", + def_kind.article(), + def_kind.descr(stable_pick.item.def_id), + ), |lint| { - let def_kind = stable_pick.item.kind.as_def_kind(); - let mut diag = lint.build(&format!( - "{} {} with this name may be added to the standard library in the future", - def_kind.article(), - def_kind.descr(stable_pick.item.def_id), - )); match (stable_pick.item.kind, stable_pick.item.container) { (ty::AssocKind::Fn, _) => { // FIXME: This should be a `span_suggestion` instead of `help` // However `self.span` only // highlights the method name, so we can't use it. Also consider reusing // the code from `report_method_error()`. - diag.help(&format!( + lint.help(&format!( "call with fully qualified syntax `{}(...)` to keep using the current \ method", self.tcx.def_path_str(stable_pick.item.def_id), @@ -1383,7 +1382,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } (ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => { let def_id = stable_pick.item.container_id(self.tcx); - diag.span_suggestion( + lint.span_suggestion( self.span, "use the fully qualified path to the associated const", format!( @@ -1399,7 +1398,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } if self.tcx.sess.is_nightly_build() { for (candidate, feature) in unstable_candidates { - diag.help(&format!( + lint.help(&format!( "add `#![feature({})]` to the crate attributes to enable `{}`", feature, self.tcx.def_path_str(candidate.item.def_id), @@ -1407,7 +1406,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { } } - diag.emit(); + lint }, ); } @@ -1695,7 +1694,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pcx.allow_similar_names = true; pcx.assemble_inherent_candidates(); - let method_names = pcx.candidate_method_names(); + let method_names = pcx.candidate_method_names(|_| true); pcx.allow_similar_names = false; let applicable_close_candidates: Vec<ty::AssocItem> = method_names .iter() diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 2d459b2cc..6c21ed902 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1,7 +1,9 @@ //! Give useful errors and suggestions to users when an item can't be //! found or is otherwise invalid. -use crate::check::FnCtxt; +use crate::errors; +use crate::FnCtxt; +use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, @@ -21,7 +23,7 @@ use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; -use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{ FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote, @@ -30,7 +32,7 @@ use rustc_trait_selection::traits::{ use std::cmp::Ordering; use std::iter; -use super::probe::{IsSuggestion, Mode, ProbeScope}; +use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::{CandidateSource, MethodError, NoMatchData}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -104,7 +106,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let report_candidates = |span: Span, err: &mut Diagnostic, - mut sources: Vec<CandidateSource>, + sources: &mut Vec<CandidateSource>, sugg_span: Span| { sources.sort(); sources.dedup(); @@ -246,7 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match error { MethodError::NoMatch(NoMatchData { - static_candidates: static_sources, + static_candidates: mut static_sources, unsatisfied_predicates, out_of_scope_traits, lev_candidate, @@ -270,7 +272,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; - if self.suggest_constraining_numerical_ty( + if self.suggest_wrapping_range_with_parens( + tcx, actual, source, span, item_name, &ty_str, + ) || self.suggest_constraining_numerical_ty( tcx, actual, source, span, item_kind, item_name, &ty_str, ) { return None; @@ -418,9 +422,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.help(&format!("try with `{}::{}`", ty_str, item_name,)); } - report_candidates(span, &mut err, static_sources, sugg_span); + report_candidates(span, &mut err, &mut static_sources, sugg_span); } else if static_sources.len() > 1 { - report_candidates(span, &mut err, static_sources, sugg_span); + report_candidates(span, &mut err, &mut static_sources, sugg_span); } let mut bound_spans = vec![]; @@ -854,8 +858,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Avoid crashing. return (None, None); } - let OnUnimplementedNote { message, label, .. } = - self.on_unimplemented_note(trait_ref, &obligation); + let OnUnimplementedNote { message, label, .. } = self + .err_ctxt() + .on_unimplemented_note(trait_ref, &obligation); (message, label) }) .unwrap_or((None, None)) @@ -983,7 +988,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_for_field_method(&mut err, source, span, actual, item_name); } - self.check_for_unwrap_self(&mut err, source, span, actual, item_name); + self.check_for_inner_self(&mut err, source, span, actual, item_name); bound_spans.sort(); bound_spans.dedup(); @@ -1002,6 +1007,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { source, out_of_scope_traits, &unsatisfied_predicates, + &static_sources, unsatisfied_bounds, ); } @@ -1074,7 +1080,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return Some(err); } - MethodError::Ambiguity(sources) => { + MethodError::Ambiguity(mut sources) => { let mut err = struct_span_err!( self.sess(), item_name.span, @@ -1083,7 +1089,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(item_name.span, format!("multiple `{}` found", item_name)); - report_candidates(span, &mut err, sources, sugg_span); + report_candidates(span, &mut err, &mut sources, sugg_span); err.emit(); } @@ -1200,6 +1206,89 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + /// Suggest possible range with adding parentheses, for example: + /// when encountering `0..1.map(|i| i + 1)` suggest `(0..1).map(|i| i + 1)`. + fn suggest_wrapping_range_with_parens( + &self, + tcx: TyCtxt<'tcx>, + actual: Ty<'tcx>, + source: SelfSource<'tcx>, + span: Span, + item_name: Ident, + ty_str: &str, + ) -> bool { + if let SelfSource::MethodCall(expr) = source { + for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) { + if let Node::Expr(parent_expr) = parent { + let lang_item = match parent_expr.kind { + ExprKind::Struct(ref qpath, _, _) => match **qpath { + QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range), + QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo), + QPath::LangItem(LangItem::RangeToInclusive, ..) => { + Some(LangItem::RangeToInclusive) + } + _ => None, + }, + ExprKind::Call(ref func, _) => match func.kind { + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. + ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => { + Some(LangItem::RangeInclusiveStruct) + } + _ => None, + }, + _ => None, + }; + + if lang_item.is_none() { + continue; + } + + let span_included = match parent_expr.kind { + hir::ExprKind::Struct(_, eps, _) => { + eps.len() > 0 && eps.last().map_or(false, |ep| ep.span.contains(span)) + } + // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. + hir::ExprKind::Call(ref func, ..) => func.span.contains(span), + _ => false, + }; + + if !span_included { + continue; + } + + let range_def_id = self.tcx.require_lang_item(lang_item.unwrap(), None); + let range_ty = + self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); + + let pick = self.probe_for_name( + span, + Mode::MethodCall, + item_name, + IsSuggestion(true), + range_ty, + expr.hir_id, + ProbeScope::AllTraits, + ); + if pick.is_ok() { + let range_span = parent_expr.span.with_hi(expr.span.hi()); + tcx.sess.emit_err(errors::MissingParentheseInRange { + span, + ty_str: ty_str.to_string(), + method_name: item_name.as_str().to_string(), + add_missing_parentheses: Some(errors::AddMissingParenthesesInRange { + func_name: item_name.name.as_str().to_string(), + left: range_span.shrink_to_lo(), + right: range_span.shrink_to_hi(), + }), + }); + return true; + } + } + } + } + false + } + fn suggest_constraining_numerical_ty( &self, tcx: TyCtxt<'tcx>, @@ -1262,7 +1351,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If this is a floating point literal that ends with '.', // get rid of it to stop this from becoming a member access. let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); - err.span_suggestion( lit.span, &format!( @@ -1395,7 +1483,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - fn check_for_unwrap_self( + fn check_for_inner_self( &self, err: &mut Diagnostic, source: SelfSource<'tcx>, @@ -1408,81 +1496,168 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); let ty::Adt(kind, substs) = actual.kind() else { return; }; - if !kind.is_enum() { - return; - } + match kind.adt_kind() { + ty::AdtKind::Enum => { + let matching_variants: Vec<_> = kind + .variants() + .iter() + .flat_map(|variant| { + let [field] = &variant.fields[..] else { return None; }; + let field_ty = field.ty(tcx, substs); + + // Skip `_`, since that'll just lead to ambiguity. + if self.resolve_vars_if_possible(field_ty).is_ty_var() { + return None; + } - let matching_variants: Vec<_> = kind - .variants() - .iter() - .flat_map(|variant| { - let [field] = &variant.fields[..] else { return None; }; - let field_ty = field.ty(tcx, substs); + self.lookup_probe( + span, + item_name, + field_ty, + call_expr, + ProbeScope::TraitsInScope, + ) + .ok() + .map(|pick| (variant, field, pick)) + }) + .collect(); + + let ret_ty_matches = |diagnostic_item| { + if let Some(ret_ty) = self + .ret_coercion + .as_ref() + .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty())) + && let ty::Adt(kind, _) = ret_ty.kind() + && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did()) + { + true + } else { + false + } + }; - // Skip `_`, since that'll just lead to ambiguity. - if self.resolve_vars_if_possible(field_ty).is_ty_var() { - return None; + match &matching_variants[..] { + [(_, field, pick)] => { + let self_ty = field.ty(tcx, substs); + err.span_note( + tcx.def_span(pick.item.def_id), + &format!("the method `{item_name}` exists on the type `{self_ty}`"), + ); + let (article, kind, variant, question) = + if tcx.is_diagnostic_item(sym::Result, kind.did()) { + ("a", "Result", "Err", ret_ty_matches(sym::Result)) + } else if tcx.is_diagnostic_item(sym::Option, kind.did()) { + ("an", "Option", "None", ret_ty_matches(sym::Option)) + } else { + return; + }; + if question { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "use the `?` operator to extract the `{self_ty}` value, propagating \ + {article} `{kind}::{variant}` value to the caller" + ), + "?", + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \ + panicking if the value is {article} `{kind}::{variant}`" + ), + ".expect(\"REASON\")", + Applicability::HasPlaceholders, + ); + } + } + // FIXME(compiler-errors): Support suggestions for other matching enum variants + _ => {} } - - self.lookup_probe(span, item_name, field_ty, call_expr, ProbeScope::AllTraits) - .ok() - .map(|pick| (variant, field, pick)) - }) - .collect(); - - let ret_ty_matches = |diagnostic_item| { - if let Some(ret_ty) = self - .ret_coercion - .as_ref() - .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty())) - && let ty::Adt(kind, _) = ret_ty.kind() - && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did()) - { - true - } else { - false } - }; + // Target wrapper types - types that wrap or pretend to wrap another type, + // perhaps this inner type is meant to be called? + ty::AdtKind::Struct | ty::AdtKind::Union => { + let [first] = ***substs else { return; }; + let ty::GenericArgKind::Type(ty) = first.unpack() else { return; }; + let Ok(pick) = self.lookup_probe( + span, + item_name, + ty, + call_expr, + ProbeScope::TraitsInScope, + ) else { return; }; - match &matching_variants[..] { - [(_, field, pick)] => { - let self_ty = field.ty(tcx, substs); - err.span_note( - tcx.def_span(pick.item.def_id), - &format!("the method `{item_name}` exists on the type `{self_ty}`"), - ); - let (article, kind, variant, question) = - if Some(kind.did()) == tcx.get_diagnostic_item(sym::Result) { - ("a", "Result", "Err", ret_ty_matches(sym::Result)) - } else if Some(kind.did()) == tcx.get_diagnostic_item(sym::Option) { - ("an", "Option", "None", ret_ty_matches(sym::Option)) - } else { - return; + let name = self.ty_to_value_string(actual); + let inner_id = kind.did(); + let mutable = if let Some(AutorefOrPtrAdjustment::Autoref { mutbl, .. }) = + pick.autoref_or_ptr_adjustment + { + Some(mutbl) + } else { + None + }; + + if tcx.is_diagnostic_item(sym::LocalKey, inner_id) { + err.help("use `with` or `try_with` to access thread local storage"); + } else if Some(kind.did()) == tcx.lang_items().maybe_uninit() { + err.help(format!( + "if this `{name}` has been initialized, \ + use one of the `assume_init` methods to access the inner value" + )); + } else if tcx.is_diagnostic_item(sym::RefCell, inner_id) { + let (suggestion, borrow_kind, panic_if) = match mutable { + Some(Mutability::Not) => (".borrow()", "borrow", "a mutable borrow exists"), + Some(Mutability::Mut) => { + (".borrow_mut()", "mutably borrow", "any borrows exist") + } + None => return, }; - if question { err.span_suggestion_verbose( expr.span.shrink_to_hi(), format!( - "use the `?` operator to extract the `{self_ty}` value, propagating \ - {article} `{kind}::{variant}` value to the caller" + "use `{suggestion}` to {borrow_kind} the `{ty}`, \ + panicking if {panic_if}" ), - "?", - Applicability::MachineApplicable, + suggestion, + Applicability::MaybeIncorrect, ); - } else { + } else if tcx.is_diagnostic_item(sym::Mutex, inner_id) { err.span_suggestion_verbose( expr.span.shrink_to_hi(), format!( - "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \ - panicking if the value is {article} `{kind}::{variant}`" + "use `.lock().unwrap()` to borrow the `{ty}`, \ + blocking the current thread until it can be acquired" ), - ".expect(\"REASON\")", - Applicability::HasPlaceholders, + ".lock().unwrap()", + Applicability::MaybeIncorrect, ); - } + } else if tcx.is_diagnostic_item(sym::RwLock, inner_id) { + let (suggestion, borrow_kind) = match mutable { + Some(Mutability::Not) => (".read().unwrap()", "borrow"), + Some(Mutability::Mut) => (".write().unwrap()", "mutably borrow"), + None => return, + }; + err.span_suggestion_verbose( + expr.span.shrink_to_hi(), + format!( + "use `{suggestion}` to {borrow_kind} the `{ty}`, \ + blocking the current thread until it can be acquired" + ), + suggestion, + Applicability::MaybeIncorrect, + ); + } else { + return; + }; + + err.span_note( + tcx.def_span(pick.item.def_id), + &format!("the method `{item_name}` exists on the type `{ty}`"), + ); } - // FIXME(compiler-errors): Support suggestions for other matching enum variants - _ => {} } } @@ -1841,6 +2016,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>, )], + static_candidates: &[CandidateSource], unsatisfied_bounds: bool, ) { let mut alt_rcvr_sugg = false; @@ -1955,6 +2131,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => true, }) .filter(|info| { + // Static candidates are already implemented, and known not to work + // Do not suggest them again + static_candidates.iter().all(|sc| match *sc { + CandidateSource::Trait(def_id) => def_id != info.def_id, + CandidateSource::Impl(def_id) => { + self.tcx.trait_id_of_impl(def_id) != Some(info.def_id) + } + }) + }) + .filter(|info| { // We approximate the coherence rules to only suggest // traits that are legal to implement by requiring that // either the type or trait is local. Multi-dispatch means @@ -2077,7 +2263,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Colon, Nothing, } - let ast_generics = hir.get_generics(id.owner).unwrap(); + let ast_generics = hir.get_generics(id.owner.def_id).unwrap(); let (sp, mut introducer) = if let Some(span) = ast_generics.bounds_span_for_suggestions(def_id) { @@ -2236,6 +2422,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// issue #102320, for `unwrap_or` with closure as argument, suggest `unwrap_or_else` + /// FIXME: currently not working for suggesting `map_or_else`, see #102408 + pub(crate) fn suggest_else_fn_with_closure( + &self, + err: &mut Diagnostic, + expr: &hir::Expr<'_>, + found: Ty<'tcx>, + expected: Ty<'tcx>, + ) -> bool { + let Some((_def_id_or_name, output, _inputs)) = self.extract_callable_info(expr, found) + else { return false; }; + + if !self.can_coerce(output, expected) { + return false; + } + + let parent = self.tcx.hir().get_parent_node(expr.hir_id); + if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) && + let hir::ExprKind::MethodCall( + hir::PathSegment { ident: method_name, .. }, + self_expr, + args, + .., + ) = call_expr.kind && + let Some(self_ty) = self.typeck_results.borrow().expr_ty_opt(self_expr) { + let new_name = Ident { + name: Symbol::intern(&format!("{}_else", method_name.as_str())), + span: method_name.span, + }; + let probe = self.lookup_probe( + expr.span, + new_name, + self_ty, + self_expr, + ProbeScope::TraitsInScope, + ); + + // check the method arguments number + if let Ok(pick) = probe && + let fn_sig = self.tcx.fn_sig(pick.item.def_id) && + let fn_args = fn_sig.skip_binder().inputs() && + fn_args.len() == args.len() + 1 { + err.span_suggestion_verbose( + method_name.span.shrink_to_hi(), + &format!("try calling `{}` instead", new_name.name.as_str()), + "_else", + Applicability::MaybeIncorrect, + ); + return true; + } + } + false + } + /// Checks whether there is a local type somewhere in the chain of /// autoderefs of `rcvr_ty`. fn type_derefs_to_local( diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 4754717c2..895739976 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -2,7 +2,7 @@ use super::method::MethodCallee; use super::{has_expected_num_generic_args, FnCtxt}; -use crate::check::Expectation; +use crate::Expectation; use rustc_ast as ast; use rustc_errors::{self, struct_span_err, Applicability, Diagnostic}; use rustc_hir as hir; @@ -13,11 +13,12 @@ use rustc_middle::ty::adjustment::{ }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable}; +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::infer::InferCtxtExt; -use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt}; use rustc_type_ir::sty::TyKind::*; @@ -470,7 +471,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This has nothing here because it means we did string // concatenation (e.g., "Hello " + "World!"). This means // we don't want the note in the else clause to be emitted - } else if lhs_ty.has_param_types_or_consts() { + } else if lhs_ty.has_non_region_param() { // Look for a TraitPredicate in the Fulfillment errors, // and use it to generate a suggestion. // @@ -511,7 +512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { _ => None, }; - self.suggest_restricting_param_bound( + self.err_ctxt().suggest_restricting_param_bound( &mut err, trait_pred, output_associated_item, @@ -656,12 +657,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { format!("cannot apply unary operator `{}`", op.as_str()), ); - if operand_ty.has_param_types_or_consts() { + if operand_ty.has_non_region_param() { let predicates = errors.iter().filter_map(|error| { error.obligation.predicate.to_opt_poly_trait_pred() }); for pred in predicates { - self.suggest_restricting_param_bound( + self.err_ctxt().suggest_restricting_param_bound( &mut err, pred, None, @@ -677,7 +678,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the previous expression was a block expression, suggest parentheses // (turning this into a binary subtraction operation instead.) // for example, `{2} - 2` -> `({2}) - 2` (see src\test\ui\parser\expr-as-stmt.rs) - self.tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp); + err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } else { match actual.kind() { Uint(_) if op == hir::UnOp::Neg => { diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 8906b622b..ea90da4a6 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1,6 +1,5 @@ -use crate::check::FnCtxt; +use crate::FnCtxt; use rustc_ast as ast; - use rustc_data_structures::fx::FxHashMap; use rustc_errors::{ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, @@ -1790,10 +1789,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &unmentioned_fields.iter().map(|(_, i)| i).collect::<Vec<_>>(), ); - self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, |build| { - let mut lint = build.build("some fields are not explicitly listed"); + self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, "some fields are not explicitly listed", |lint| { lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns)); - lint.help( "ensure that all fields are mentioned explicitly by adding the suggested fields", ); @@ -1801,7 +1798,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "the pattern is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", ty, )); - lint.emit(); + + lint }); } diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 2e0f37eba..ba8cf6926 100644 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -1,5 +1,5 @@ -use crate::check::method::MethodCallee; -use crate::check::{has_expected_num_generic_args, FnCtxt, PlaceOp}; +use crate::method::MethodCallee; +use crate::{has_expected_num_generic_args, FnCtxt, PlaceOp}; use rustc_ast as ast; use rustc_errors::Applicability; use rustc_hir as hir; diff --git a/compiler/rustc_typeck/src/check/rvalue_scopes.rs b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs index 22c9e7961..22c9e7961 100644 --- a/compiler/rustc_typeck/src/check/rvalue_scopes.rs +++ b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 0b207a6c0..4dea40829 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -352,7 +352,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// and that the path can be captured with required capture kind (depending on use in closure, /// move closure etc.) /// - /// Returns the set of of adjusted information along with the inferred closure kind and span + /// Returns the set of adjusted information along with the inferred closure kind and span /// associated with the closure kind inference. /// /// Note that we *always* infer a minimal kind, even if @@ -749,10 +749,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_hir_id, closure_head_span, + reasons.migration_message(), |lint| { - let mut diagnostics_builder = lint.build( - &reasons.migration_message(), - ); for NeededMigration { var_hir_id, diagnostics_info } in &need_migrations { // Labels all the usage of the captured variable and why they are responsible // for migration being needed @@ -760,13 +758,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &lint_note.captures_info { UpvarMigrationInfo::CapturingPrecise { source_expr: Some(capture_expr_id), var_name: captured_name } => { let cause_span = self.tcx.hir().span(*capture_expr_id); - diagnostics_builder.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`", + lint.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`", self.tcx.hir().name(*var_hir_id), captured_name, )); } UpvarMigrationInfo::CapturingNothing { use_span } => { - diagnostics_builder.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect", + lint.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect", self.tcx.hir().name(*var_hir_id), )); } @@ -781,13 +779,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &lint_note.captures_info { UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { - diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure", + lint.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure", self.tcx.hir().name(*var_hir_id), captured_name, )); } UpvarMigrationInfo::CapturingNothing { use_span: _ } => { - diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", + lint.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", v = self.tcx.hir().name(*var_hir_id), )); } @@ -800,7 +798,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match &lint_note.captures_info { UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { let var_name = self.tcx.hir().name(*var_hir_id); - diagnostics_builder.span_label(closure_head_span, format!("\ + lint.span_label(closure_head_span, format!("\ in Rust 2018, this closure implements {missing_trait} \ as `{var_name}` implements {missing_trait}, but in Rust 2021, \ this closure will no longer implement {missing_trait} \ @@ -814,7 +812,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - diagnostics_builder.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>"); + lint.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>"); let diagnostic_msg = format!( "add a dummy let to cause {} to be fully captured", @@ -857,7 +855,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We take the indentation from the next non-empty line. let line2 = lines.find(|line| !line.is_empty()).unwrap_or_default(); let indent = line2.split_once(|c: char| !c.is_whitespace()).unwrap_or_default().0; - diagnostics_builder.span_suggestion( + lint.span_suggestion( closure_body_span.with_lo(closure_body_span.lo() + BytePos::from_usize(line1.len())).shrink_to_lo(), &diagnostic_msg, format!("\n{indent}{migration_string};"), @@ -868,7 +866,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // braces, but with more than just the opening // brace on the first line. We put the `let` // directly after the `{`. - diagnostics_builder.span_suggestion( + lint.span_suggestion( closure_body_span.with_lo(closure_body_span.lo() + BytePos(1)).shrink_to_lo(), &diagnostic_msg, format!(" {migration_string};"), @@ -877,7 +875,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { // This is a closure without braces around the body. // We add braces to add the `let` before the body. - diagnostics_builder.multipart_suggestion( + lint.multipart_suggestion( &diagnostic_msg, vec![ (closure_body_span.shrink_to_lo(), format!("{{ {migration_string}; ")), @@ -887,7 +885,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } else { - diagnostics_builder.span_suggestion( + lint.span_suggestion( closure_span, &diagnostic_msg, migration_string, @@ -895,7 +893,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } - diagnostics_builder.emit(); + lint }, ); } diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 9ecf34e9a..1e26daa9c 100644 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -2,7 +2,7 @@ // unresolved type variables and replaces "ty_var" types with their // substitutions. -use crate::check::FnCtxt; +use crate::FnCtxt; use hir::def_id::LocalDefId; use rustc_data_structures::fx::FxHashMap; use rustc_errors::ErrorGuaranteed; @@ -536,33 +536,37 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { let opaque_types = self.fcx.infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); for (opaque_type_key, decl) in opaque_types { - let hidden_type = match decl.origin { - hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { - let ty = self.resolve(decl.hidden_type.ty, &decl.hidden_type.span); - struct RecursionChecker { - def_id: LocalDefId, - } - impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker { - type BreakTy = (); - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::Opaque(def_id, _) = *t.kind() { - if def_id == self.def_id.to_def_id() { - return ControlFlow::Break(()); - } - } - t.super_visit_with(self) + let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span); + let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span); + + struct RecursionChecker { + def_id: LocalDefId, + } + impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker { + type BreakTy = (); + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + if let ty::Opaque(def_id, _) = *t.kind() { + if def_id == self.def_id.to_def_id() { + return ControlFlow::Break(()); } } - if ty - .visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id }) - .is_break() - { - return; - } - Some(ty) + t.super_visit_with(self) } - hir::OpaqueTyOrigin::TyAlias => None, - }; + } + if hidden_type + .visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id }) + .is_break() + { + continue; + } + + let hidden_type = hidden_type.remap_generic_params_to_declaration_params( + opaque_type_key, + self.fcx.infcx.tcx, + true, + decl.origin, + ); + self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type); } } @@ -700,7 +704,7 @@ impl Locatable for hir::HirId { /// unresolved types and so forth. struct Resolver<'cx, 'tcx> { tcx: TyCtxt<'tcx>, - infcx: &'cx InferCtxt<'cx, 'tcx>, + infcx: &'cx InferCtxt<'tcx>, span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, @@ -717,27 +721,14 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { Resolver { tcx: fcx.tcx, infcx: fcx, span, body, replaced_with_error: false } } - fn report_type_error(&self, t: Ty<'tcx>) { + fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) { if !self.tcx.sess.has_errors().is_some() { self.infcx + .err_ctxt() .emit_inference_failure_err( Some(self.body.id()), self.span.to_span(self.tcx), - t.into(), - E0282, - false, - ) - .emit(); - } - } - - fn report_const_error(&self, c: ty::Const<'tcx>) { - if self.tcx.sess.has_errors().is_none() { - self.infcx - .emit_inference_failure_err( - Some(self.body.id()), - self.span.to_span(self.tcx), - c.into(), + p.into(), E0282, false, ) @@ -782,7 +773,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { } Err(_) => { debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); - self.report_type_error(t); + self.report_error(t); self.replaced_with_error = true; self.tcx().ty_error() } @@ -799,7 +790,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { Ok(ct) => self.tcx.erase_regions(ct), Err(_) => { debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); - self.report_const_error(ct); + self.report_error(ct); self.replaced_with_error = true; self.tcx().const_error(ct.ty()) } diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs deleted file mode 100644 index d08c0d4db..000000000 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ /dev/null @@ -1,594 +0,0 @@ -//! Check properties that are required by built-in traits and set -//! up data structures required by type-checking/codegen. - -use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem}; -use rustc_errors::{struct_span_err, MultiSpan}; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::lang_items::LangItem; -use rustc_hir::ItemKind; -use rustc_infer::infer; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::TyCtxtInferExt; -use rustc_middle::ty::adjustment::CoerceUnsizedInfo; -use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable}; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt; -use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError}; -use rustc_trait_selection::traits::predicate_for_trait_def; -use rustc_trait_selection::traits::{self, ObligationCause}; -use std::collections::BTreeMap; - -pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) { - let lang_items = tcx.lang_items(); - Checker { tcx, trait_def_id } - .check(lang_items.drop_trait(), visit_implementation_of_drop) - .check(lang_items.copy_trait(), visit_implementation_of_copy) - .check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized) - .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn); -} - -struct Checker<'tcx> { - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, -} - -impl<'tcx> Checker<'tcx> { - fn check<F>(&self, trait_def_id: Option<DefId>, mut f: F) -> &Self - where - F: FnMut(TyCtxt<'tcx>, LocalDefId), - { - if Some(self.trait_def_id) == trait_def_id { - for &impl_def_id in self.tcx.hir().trait_impls(self.trait_def_id) { - f(self.tcx, impl_def_id); - } - } - self - } -} - -fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) { - // Destructors only work on local ADT types. - match tcx.type_of(impl_did).kind() { - ty::Adt(def, _) if def.did().is_local() => return, - ty::Error(_) => return, - _ => {} - } - - let sp = match tcx.hir().expect_item(impl_did).kind { - ItemKind::Impl(ref impl_) => impl_.self_ty.span, - _ => bug!("expected Drop impl item"), - }; - - tcx.sess.emit_err(DropImplOnWrongItem { span: sp }); -} - -fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) { - debug!("visit_implementation_of_copy: impl_did={:?}", impl_did); - - let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did); - - let self_type = tcx.type_of(impl_did); - debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type); - - let span = tcx.hir().span(impl_hir_id); - let param_env = tcx.param_env(impl_did); - assert!(!self_type.has_escaping_bound_vars()); - - debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type); - - let cause = traits::ObligationCause::misc(span, impl_hir_id); - match can_type_implement_copy(tcx, param_env, self_type, cause) { - Ok(()) => {} - Err(CopyImplementationError::InfrigingFields(fields)) => { - let item = tcx.hir().expect_item(impl_did); - let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(ref tr), .. }) = item.kind { - tr.path.span - } else { - span - }; - - let mut err = struct_span_err!( - tcx.sess, - span, - E0204, - "the trait `Copy` may not be implemented for this type" - ); - - // We'll try to suggest constraining type parameters to fulfill the requirements of - // their `Copy` implementation. - let mut errors: BTreeMap<_, Vec<_>> = Default::default(); - let mut bounds = vec![]; - - for (field, ty) in fields { - let field_span = tcx.def_span(field.did); - let field_ty_span = match tcx.hir().get_if_local(field.did) { - Some(hir::Node::Field(field_def)) => field_def.ty.span, - _ => field_span, - }; - err.span_label(field_span, "this field does not implement `Copy`"); - // Spin up a new FulfillmentContext, so we can get the _precise_ reason - // why this field does not implement Copy. This is useful because sometimes - // it is not immediately clear why Copy is not implemented for a field, since - // all we point at is the field itself. - tcx.infer_ctxt().ignoring_regions().enter(|infcx| { - for error in traits::fully_solve_bound( - &infcx, - traits::ObligationCause::dummy_with_span(field_ty_span), - param_env, - ty, - tcx.lang_items().copy_trait().unwrap(), - ) { - let error_predicate = error.obligation.predicate; - // Only note if it's not the root obligation, otherwise it's trivial and - // should be self-explanatory (i.e. a field literally doesn't implement Copy). - - // FIXME: This error could be more descriptive, especially if the error_predicate - // contains a foreign type or if it's a deeply nested type... - if error_predicate != error.root_obligation.predicate { - errors - .entry((ty.to_string(), error_predicate.to_string())) - .or_default() - .push(error.obligation.cause.span); - } - if let ty::PredicateKind::Trait(ty::TraitPredicate { - trait_ref, - polarity: ty::ImplPolarity::Positive, - .. - }) = error_predicate.kind().skip_binder() - { - let ty = trait_ref.self_ty(); - if let ty::Param(_) = ty.kind() { - bounds.push(( - format!("{ty}"), - trait_ref.print_only_trait_path().to_string(), - Some(trait_ref.def_id), - )); - } - } - } - }); - } - for ((ty, error_predicate), spans) in errors { - let span: MultiSpan = spans.into(); - err.span_note( - span, - &format!("the `Copy` impl for `{}` requires that `{}`", ty, error_predicate), - ); - } - suggest_constraining_type_params( - tcx, - tcx.hir().get_generics(impl_did).expect("impls always have generics"), - &mut err, - bounds.iter().map(|(param, constraint, def_id)| { - (param.as_str(), constraint.as_str(), *def_id) - }), - ); - err.emit(); - } - Err(CopyImplementationError::NotAnAdt) => { - let item = tcx.hir().expect_item(impl_did); - let span = - if let ItemKind::Impl(ref impl_) = item.kind { impl_.self_ty.span } else { span }; - - tcx.sess.emit_err(CopyImplOnNonAdt { span }); - } - Err(CopyImplementationError::HasDestructor) => { - tcx.sess.emit_err(CopyImplOnTypeWithDtor { span }); - } - } -} - -fn visit_implementation_of_coerce_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) { - debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did); - - // Just compute this for the side-effects, in particular reporting - // errors; other parts of the code may demand it for the info of - // course. - let span = tcx.def_span(impl_did); - tcx.at(span).coerce_unsized_info(impl_did); -} - -fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) { - debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did); - - let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did); - let span = tcx.hir().span(impl_hir_id); - - let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span)); - - let source = tcx.type_of(impl_did); - assert!(!source.has_escaping_bound_vars()); - let target = { - let trait_ref = tcx.impl_trait_ref(impl_did).unwrap(); - assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait); - - trait_ref.substs.type_at(1) - }; - - debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target); - - let param_env = tcx.param_env(impl_did); - - let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg); - - tcx.infer_ctxt().enter(|infcx| { - let cause = ObligationCause::misc(span, impl_hir_id); - - use rustc_type_ir::sty::TyKind::*; - match (source.kind(), target.kind()) { - (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b)) - if infcx.at(&cause, param_env).eq(r_a, *r_b).is_ok() && mutbl_a == *mutbl_b => {} - (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (), - (&Adt(def_a, substs_a), &Adt(def_b, substs_b)) - if def_a.is_struct() && def_b.is_struct() => - { - if def_a != def_b { - let source_path = tcx.def_path_str(def_a.did()); - let target_path = tcx.def_path_str(def_b.did()); - - create_err(&format!( - "the trait `DispatchFromDyn` may only be implemented \ - for a coercion between structures with the same \ - definition; expected `{}`, found `{}`", - source_path, target_path, - )) - .emit(); - - return; - } - - if def_a.repr().c() || def_a.repr().packed() { - create_err( - "structs implementing `DispatchFromDyn` may not have \ - `#[repr(packed)]` or `#[repr(C)]`", - ) - .emit(); - } - - let fields = &def_a.non_enum_variant().fields; - - let coerced_fields = fields - .iter() - .filter(|field| { - let ty_a = field.ty(tcx, substs_a); - let ty_b = field.ty(tcx, substs_b); - - if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) { - if layout.is_zst() && layout.align.abi.bytes() == 1 { - // ignore ZST fields with alignment of 1 byte - return false; - } - } - - if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) { - if ok.obligations.is_empty() { - create_err( - "the trait `DispatchFromDyn` may only be implemented \ - for structs containing the field being coerced, \ - ZST fields with 1 byte alignment, and nothing else", - ) - .note(&format!( - "extra field `{}` of type `{}` is not allowed", - field.name, ty_a, - )) - .emit(); - - return false; - } - } - - return true; - }) - .collect::<Vec<_>>(); - - if coerced_fields.is_empty() { - create_err( - "the trait `DispatchFromDyn` may only be implemented \ - for a coercion between structures with a single field \ - being coerced, none found", - ) - .emit(); - } else if coerced_fields.len() > 1 { - create_err( - "implementing the `DispatchFromDyn` trait requires multiple coercions", - ) - .note( - "the trait `DispatchFromDyn` may only be implemented \ - for a coercion between structures with a single field \ - being coerced", - ) - .note(&format!( - "currently, {} fields need coercions: {}", - coerced_fields.len(), - coerced_fields - .iter() - .map(|field| { - format!( - "`{}` (`{}` to `{}`)", - field.name, - field.ty(tcx, substs_a), - field.ty(tcx, substs_b), - ) - }) - .collect::<Vec<_>>() - .join(", ") - )) - .emit(); - } else { - let errors = traits::fully_solve_obligations( - &infcx, - coerced_fields.into_iter().map(|field| { - predicate_for_trait_def( - tcx, - param_env, - cause.clone(), - dispatch_from_dyn_trait, - 0, - field.ty(tcx, substs_a), - &[field.ty(tcx, substs_b).into()], - ) - }), - ); - if !errors.is_empty() { - infcx.report_fulfillment_errors(&errors, None, false); - } - - // Finally, resolve all regions. - let outlives_env = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env); - } - } - _ => { - create_err( - "the trait `DispatchFromDyn` may only be implemented \ - for a coercion between structures", - ) - .emit(); - } - } - }) -} - -pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo { - debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did); - - // this provider should only get invoked for local def-ids - let impl_did = impl_did.expect_local(); - let span = tcx.def_span(impl_did); - - let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span)); - - let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| { - tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err.to_string())); - }); - - let source = tcx.type_of(impl_did); - let trait_ref = tcx.impl_trait_ref(impl_did).unwrap(); - assert_eq!(trait_ref.def_id, coerce_unsized_trait); - let target = trait_ref.substs.type_at(1); - debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target); - - let param_env = tcx.param_env(impl_did); - assert!(!source.has_escaping_bound_vars()); - - let err_info = CoerceUnsizedInfo { custom_kind: None }; - - debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target); - - tcx.infer_ctxt().enter(|infcx| { - let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did); - let cause = ObligationCause::misc(span, impl_hir_id); - let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>, - mt_b: ty::TypeAndMut<'tcx>, - mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| { - if (mt_a.mutbl, mt_b.mutbl) == (hir::Mutability::Not, hir::Mutability::Mut) { - infcx - .report_mismatched_types( - &cause, - mk_ptr(mt_b.ty), - target, - ty::error::TypeError::Mutability, - ) - .emit(); - } - (mt_a.ty, mt_b.ty, unsize_trait, None) - }; - let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) { - (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => { - infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a); - let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; - let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b }; - check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty)) - } - - (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => { - let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a }; - check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)) - } - - (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => { - check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)) - } - - (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) - if def_a.is_struct() && def_b.is_struct() => - { - if def_a != def_b { - let source_path = tcx.def_path_str(def_a.did()); - let target_path = tcx.def_path_str(def_b.did()); - struct_span_err!( - tcx.sess, - span, - E0377, - "the trait `CoerceUnsized` may only be implemented \ - for a coercion between structures with the same \ - definition; expected `{}`, found `{}`", - source_path, - target_path - ) - .emit(); - return err_info; - } - - // Here we are considering a case of converting - // `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`, - // which acts like a pointer to `U`, but carries along some extra data of type `T`: - // - // struct Foo<T, U> { - // extra: T, - // ptr: *mut U, - // } - // - // We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized - // to `Foo<T, [i32]>`. That impl would look like: - // - // impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {} - // - // Here `U = [i32; 3]` and `V = [i32]`. At runtime, - // when this coercion occurs, we would be changing the - // field `ptr` from a thin pointer of type `*mut [i32; - // 3]` to a fat pointer of type `*mut [i32]` (with - // extra data `3`). **The purpose of this check is to - // make sure that we know how to do this conversion.** - // - // To check if this impl is legal, we would walk down - // the fields of `Foo` and consider their types with - // both substitutes. We are looking to find that - // exactly one (non-phantom) field has changed its - // type, which we will expect to be the pointer that - // is becoming fat (we could probably generalize this - // to multiple thin pointers of the same type becoming - // fat, but we don't). In this case: - // - // - `extra` has type `T` before and type `T` after - // - `ptr` has type `*mut U` before and type `*mut V` after - // - // Since just one field changed, we would then check - // that `*mut U: CoerceUnsized<*mut V>` is implemented - // (in other words, that we know how to do this - // conversion). This will work out because `U: - // Unsize<V>`, and we have a builtin rule that `*mut - // U` can be coerced to `*mut V` if `U: Unsize<V>`. - let fields = &def_a.non_enum_variant().fields; - let diff_fields = fields - .iter() - .enumerate() - .filter_map(|(i, f)| { - let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b)); - - if tcx.type_of(f.did).is_phantom_data() { - // Ignore PhantomData fields - return None; - } - - // Ignore fields that aren't changed; it may - // be that we could get away with subtyping or - // something more accepting, but we use - // equality because we want to be able to - // perform this check without computing - // variance where possible. (This is because - // we may have to evaluate constraint - // expressions in the course of execution.) - // See e.g., #41936. - if let Ok(ok) = infcx.at(&cause, param_env).eq(a, b) { - if ok.obligations.is_empty() { - return None; - } - } - - // Collect up all fields that were significantly changed - // i.e., those that contain T in coerce_unsized T -> U - Some((i, a, b)) - }) - .collect::<Vec<_>>(); - - if diff_fields.is_empty() { - struct_span_err!( - tcx.sess, - span, - E0374, - "the trait `CoerceUnsized` may only be implemented \ - for a coercion between structures with one field \ - being coerced, none found" - ) - .emit(); - return err_info; - } else if diff_fields.len() > 1 { - let item = tcx.hir().expect_item(impl_did); - let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(ref t), .. }) = - item.kind - { - t.path.span - } else { - tcx.def_span(impl_did) - }; - - struct_span_err!( - tcx.sess, - span, - E0375, - "implementing the trait \ - `CoerceUnsized` requires multiple \ - coercions" - ) - .note( - "`CoerceUnsized` may only be implemented for \ - a coercion between structures with one field being coerced", - ) - .note(&format!( - "currently, {} fields need coercions: {}", - diff_fields.len(), - diff_fields - .iter() - .map(|&(i, a, b)| { - format!("`{}` (`{}` to `{}`)", fields[i].name, a, b) - }) - .collect::<Vec<_>>() - .join(", ") - )) - .span_label(span, "requires multiple coercions") - .emit(); - return err_info; - } - - let (i, a, b) = diff_fields[0]; - let kind = ty::adjustment::CustomCoerceUnsized::Struct(i); - (a, b, coerce_unsized_trait, Some(kind)) - } - - _ => { - struct_span_err!( - tcx.sess, - span, - E0376, - "the trait `CoerceUnsized` may only be implemented \ - for a coercion between structures" - ) - .emit(); - return err_info; - } - }; - - // Register an obligation for `A: Trait<B>`. - let cause = traits::ObligationCause::misc(span, impl_hir_id); - let predicate = predicate_for_trait_def( - tcx, - param_env, - cause, - trait_def_id, - 0, - source, - &[target.into()], - ); - let errors = traits::fully_solve_obligation(&infcx, predicate); - if !errors.is_empty() { - infcx.report_fulfillment_errors(&errors, None, false); - } - - // Finally, resolve all regions. - let outlives_env = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env); - - CoerceUnsizedInfo { custom_kind: kind } - }) -} |