diff options
Diffstat (limited to 'compiler/rustc_hir_analysis')
| -rw-r--r-- | compiler/rustc_hir_analysis/messages.ftl | 9 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/check.rs | 20 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs | 80 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/errors.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs | 127 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 25 |
7 files changed, 137 insertions, 136 deletions
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 935f4de6c58..194f2cd04e4 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -504,12 +504,9 @@ hir_analysis_supertrait_item_shadowee = item from `{$supertrait}` is shadowed by hir_analysis_supertrait_item_shadowing = trait item `{$item}` from `{$subtrait}` shadows identically named item from supertrait -hir_analysis_tait_forward_compat = item constrains opaque type that is not in its signature - .note = this item must mention the opaque type in its signature in order to be able to register hidden types - -hir_analysis_tait_forward_compat2 = item does not constrain `{$opaque_type}`, but has it in its signature - .note = consider moving the opaque type's declaration and defining uses into a separate module - .opaque = this opaque type is in the signature +hir_analysis_tait_forward_compat2 = item does not constrain `{$opaque_type}` + .note = consider removing `#[define_opaque]` or adding an empty `#[define_opaque()]` + .opaque = this opaque type is supposed to be constrained hir_analysis_target_feature_on_main = `main` function is not allowed to have `#[target_feature]` diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 8f9997cb62c..8dc5d361750 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -392,6 +392,12 @@ fn best_definition_site_of_opaque<'tcx>( return ControlFlow::Continue(()); } + let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id); + // Don't try to check items that cannot possibly constrain the type. + if !opaque_types_defined_by.contains(&self.opaque_def_id) { + return ControlFlow::Continue(()); + } + if let Some(hidden_ty) = self.tcx.mir_borrowck(item_def_id).concrete_opaque_types.get(&self.opaque_def_id) { @@ -451,19 +457,7 @@ fn best_definition_site_of_opaque<'tcx>( None } hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => { - let scope = tcx.hir_get_defining_scope(tcx.local_def_id_to_hir_id(opaque_def_id)); - let found = if scope == hir::CRATE_HIR_ID { - tcx.hir_walk_toplevel_module(&mut locator) - } else { - match tcx.hir_node(scope) { - Node::Item(it) => locator.visit_item(it), - Node::ImplItem(it) => locator.visit_impl_item(it), - Node::TraitItem(it) => locator.visit_trait_item(it), - Node::ForeignItem(it) => locator.visit_foreign_item(it), - other => bug!("{:?} is not a valid scope for an opaque type item", other), - } - }; - found.break_value() + tcx.hir_walk_toplevel_module(&mut locator).break_value() } } } diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index 399c4fbe55a..142078900f0 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -1,14 +1,13 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem, def}; +use rustc_hir::{self as hir, Expr, ImplItem, Item, Node, TraitItem, def, intravisit}; use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::DUMMY_SP; use tracing::{debug, instrument, trace}; -use crate::errors::{TaitForwardCompat, TaitForwardCompat2, UnconstrainedOpaqueType}; +use crate::errors::{TaitForwardCompat2, UnconstrainedOpaqueType}; /// Checks "defining uses" of opaque `impl Trait` in associated types. /// These can only be defined by associated items of the same trait. @@ -82,38 +81,9 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type( /// ``` #[instrument(skip(tcx), level = "debug")] pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { - let hir_id = tcx.local_def_id_to_hir_id(def_id); - let scope = tcx.hir_get_defining_scope(hir_id); let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] }; - debug!(?scope); - - if scope == hir::CRATE_HIR_ID { - tcx.hir_walk_toplevel_module(&mut locator); - } else { - trace!("scope={:#?}", tcx.hir_node(scope)); - match tcx.hir_node(scope) { - // We explicitly call `visit_*` methods, instead of using `intravisit::walk_*` methods - // This allows our visitor to process the defining item itself, causing - // it to pick up any 'sibling' defining uses. - // - // For example, this code: - // ``` - // fn foo() { - // type Blah = impl Debug; - // let my_closure = || -> Blah { true }; - // } - // ``` - // - // requires us to explicitly process `foo()` in order - // to notice the defining usage of `Blah`. - Node::Item(it) => locator.visit_item(it), - Node::ImplItem(it) => locator.visit_impl_item(it), - Node::TraitItem(it) => locator.visit_trait_item(it), - Node::ForeignItem(it) => locator.visit_foreign_item(it), - other => bug!("{:?} is not a valid scope for an opaque type item", other), - } - } + tcx.hir_walk_toplevel_module(&mut locator); if let Some(hidden) = locator.found { // Only check against typeck if we didn't already error @@ -137,12 +107,7 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType { span: tcx.def_span(def_id), name: tcx.item_ident(parent_def_id.to_def_id()), - what: match tcx.hir_node(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", - }, + what: "crate", }); Ty::new_error(tcx, reported) } @@ -176,6 +141,13 @@ impl TaitConstraintLocator<'_> { return; } + let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id); + // Don't try to check items that cannot possibly constrain the type. + if !opaque_types_defined_by.contains(&self.def_id) { + debug!("no constraint: no opaque types defined"); + return; + } + // Function items with `_` in their return type already emit an error, skip any // "non-defining use" errors for them. // Note that we use `Node::fn_sig` instead of `Node::fn_decl` here, because the former @@ -215,8 +187,6 @@ impl TaitConstraintLocator<'_> { return; } - let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id); - let mut constrained = false; for (&opaque_type_key, &hidden_type) in &tables.concrete_opaque_types { if opaque_type_key.def_id != self.def_id { @@ -224,20 +194,6 @@ impl TaitConstraintLocator<'_> { } constrained = true; - if !opaque_types_defined_by.contains(&self.def_id) { - let guar = self.tcx.dcx().emit_err(TaitForwardCompat { - span: hidden_type.span, - item_span: self - .tcx - .def_ident_span(item_def_id) - .unwrap_or_else(|| self.tcx.def_span(item_def_id)), - }); - // Avoid "opaque type not constrained" errors on the opaque itself. - self.found = Some(ty::OpaqueHiddenType { - span: DUMMY_SP, - ty: Ty::new_error(self.tcx, guar), - }); - } let concrete_type = self.tcx.erase_regions(hidden_type.remap_generic_params_to_declaration_params( opaque_type_key, @@ -309,19 +265,13 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { } fn visit_item(&mut self, it: &'tcx Item<'tcx>) { trace!(?it.owner_id); - // The opaque type itself or its children are not within its reveal scope. - if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); - intravisit::walk_item(self, it); - } + self.check(it.owner_id.def_id); + intravisit::walk_item(self, it); } fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { trace!(?it.owner_id); - // The opaque type itself or its children are not within its reveal scope. - if it.owner_id.def_id != self.def_id { - self.check(it.owner_id.def_id); - intravisit::walk_impl_item(self, it); - } + 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.owner_id); diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 4c6c2504126..f2560f22874 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -425,16 +425,6 @@ pub(crate) struct UnconstrainedOpaqueType { } #[derive(Diagnostic)] -#[diag(hir_analysis_tait_forward_compat)] -#[note] -pub(crate) struct TaitForwardCompat { - #[primary_span] - pub span: Span, - #[note] - pub item_span: Span, -} - -#[derive(Diagnostic)] #[diag(hir_analysis_tait_forward_compat2)] #[note] pub(crate) struct TaitForwardCompat2 { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index ace5e34b382..fa061c80618 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -1520,7 +1520,7 @@ fn generics_args_err_extend<'a>( }) .collect(); if args.len() > 1 - && let Some(span) = args.into_iter().last() + && let Some(span) = args.into_iter().next_back() { err.note( "generic arguments are not allowed on both an enum and its variant's path \ diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index 3611db7c68f..e78801dd601 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -78,13 +78,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } if self_ty.span.edition().at_least_rust_2021() { - let msg = "expected a type, found a trait"; - let label = "you can add the `dyn` keyword if you want a trait object"; - let mut diag = - rustc_errors::struct_span_code_err!(self.dcx(), self_ty.span, E0782, "{}", msg); + let mut diag = rustc_errors::struct_span_code_err!( + self.dcx(), + self_ty.span, + E0782, + "{}", + "expected a type, found a trait" + ); if self_ty.span.can_be_used_for_suggestions() && !self.maybe_suggest_impl_trait(self_ty, &mut diag) - && !self.maybe_suggest_dyn_trait(self_ty, label, sugg, &mut diag) + && !self.maybe_suggest_dyn_trait(self_ty, sugg, &mut diag) { self.maybe_suggest_add_generic_impl_trait(self_ty, &mut diag); } @@ -123,31 +126,62 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + /// For a struct or enum with an invalid bare trait object field, suggest turning + /// it into a generic type bound. fn maybe_suggest_add_generic_impl_trait( &self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>, ) -> bool { let tcx = self.tcx(); - let msg = "you might be missing a type parameter"; - let mut sugg = vec![]; - let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - let parent_item = tcx.hir_node_by_def_id(parent_id).expect_item(); - match parent_item.kind { - hir::ItemKind::Struct(_, generics) | hir::ItemKind::Enum(_, generics) => { - sugg.push(( - generics.where_clause_span, - format!( - "<T: {}>", - self.tcx().sess.source_map().span_to_snippet(self_ty.span).unwrap() - ), - )); - sugg.push((self_ty.span, "T".to_string())); + let parent_hir_id = tcx.parent_hir_id(self_ty.hir_id); + let parent_item = tcx.hir_get_parent_item(self_ty.hir_id).def_id; + + let generics = match tcx.hir_node_by_def_id(parent_item) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(variant, generics), .. + }) => { + if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) { + return false; + } + generics } - _ => {} + hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(def, generics), .. }) => { + if !def + .variants + .iter() + .flat_map(|variant| variant.data.fields().iter()) + .any(|field| field.hir_id == parent_hir_id) + { + return false; + } + generics + } + _ => return false, + }; + + let Ok(rendered_ty) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { + return false; + }; + + let param = "TUV" + .chars() + .map(|c| c.to_string()) + .chain((0..).map(|i| format!("P{i}"))) + .find(|s| !generics.params.iter().any(|param| param.name.ident().as_str() == s)) + .expect("we definitely can find at least one param name to generate"); + let mut sugg = vec![(self_ty.span, param.to_string())]; + if let Some(insertion_span) = generics.span_for_param_suggestion() { + sugg.push((insertion_span, format!(", {param}: {}", rendered_ty))); + } else { + sugg.push((generics.where_clause_span, format!("<{param}: {}>", rendered_ty))); } - diag.multipart_suggestion_verbose(msg, sugg, Applicability::MachineApplicable); + diag.multipart_suggestion_verbose( + "you might be missing a type parameter", + sugg, + Applicability::MachineApplicable, + ); true } /// Make sure that we are in the condition to suggest the blanket implementation. @@ -198,32 +232,59 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + /// Try our best to approximate when adding `dyn` would be helpful for a bare + /// trait object. + /// + /// Right now, this is if the type is either directly nested in another ty, + /// or if it's in the tail field within a struct. This approximates what the + /// user would've gotten on edition 2015, except for the case where we have + /// an *obvious* knock-on `Sized` error. fn maybe_suggest_dyn_trait( &self, self_ty: &hir::Ty<'_>, - label: &str, sugg: Vec<(Span, String)>, diag: &mut Diag<'_>, ) -> bool { let tcx = self.tcx(); - let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id; - let parent_item = tcx.hir_node_by_def_id(parent_id).expect_item(); - // If the parent item is an enum, don't suggest the dyn trait. - if let hir::ItemKind::Enum(..) = parent_item.kind { - return false; - } + // Look at the direct HIR parent, since we care about the relationship between + // the type and the thing that directly encloses it. + match tcx.parent_hir_node(self_ty.hir_id) { + // These are all generally ok. Namely, when a trait object is nested + // into another expression or ty, it's either very certain that they + // missed the ty (e.g. `&Trait`) or it's not really possible to tell + // what their intention is, so let's not give confusing suggestions and + // just mention `dyn`. The user can make up their mind what to do here. + hir::Node::Ty(_) + | hir::Node::Expr(_) + | hir::Node::PatExpr(_) + | hir::Node::PathSegment(_) + | hir::Node::AssocItemConstraint(_) + | hir::Node::TraitRef(_) + | hir::Node::Item(_) + | hir::Node::WherePredicate(_) => {} - // If the parent item is a struct, check if self_ty is the last field. - if let hir::ItemKind::Struct(variant_data, _) = parent_item.kind { - if variant_data.fields().last().unwrap().ty.span != self_ty.span { - return false; + hir::Node::Field(field) => { + // Enums can't have unsized fields, fields can only have an unsized tail field. + if let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Struct(variant, _), .. + }) = tcx.parent_hir_node(field.hir_id) + && variant + .fields() + .last() + .is_some_and(|tail_field| tail_field.hir_id == field.hir_id) + { + // Ok + } else { + return false; + } } + _ => return false, } // FIXME: Only emit this suggestion if the trait is dyn-compatible. diag.multipart_suggestion_verbose( - label.to_string(), + "you can add the `dyn` keyword if you want a trait object", sugg, Applicability::MachineApplicable, ); diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index dd6c40bfbb8..5f91f1d7b3e 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2294,18 +2294,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { { let anon_const_type = tcx.type_of(param_def_id).instantiate(tcx, args); - // We must error if the instantiated type has any inference variables as we will - // use this type to feed the `type_of` and query results must not contain inference - // variables otherwise we will ICE. - // + // FIXME(generic_const_parameter_types): Ideally we remove these errors below when + // we have the ability to intermix typeck of anon const const args with the parent + // bodies typeck. + // We also error if the type contains any regions as effectively any region will wind // up as a region variable in mir borrowck. It would also be somewhat concerning if // hir typeck was using equality but mir borrowck wound up using subtyping as that could // result in a non-infer in hir typeck but a region variable in borrowck. - // - // FIXME(generic_const_parameter_types): Ideally we remove these errors one day when - // we have the ability to intermix typeck of anon const const args with the parent - // bodies typeck. if tcx.features().generic_const_parameter_types() && (anon_const_type.has_free_regions() || anon_const_type.has_erased_regions()) { @@ -2316,6 +2312,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e))); return ty::Const::new_error(tcx, e); } + // We must error if the instantiated type has any inference variables as we will + // use this type to feed the `type_of` and query results must not contain inference + // variables otherwise we will ICE. if anon_const_type.has_non_region_infer() { let e = tcx.dcx().span_err( const_arg.span(), @@ -2324,6 +2323,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e))); return ty::Const::new_error(tcx, e); } + // We error when the type contains unsubstituted generics since we do not currently + // give the anon const any of the generics from the parent. + if anon_const_type.has_non_region_param() { + let e = tcx.dcx().span_err( + const_arg.span(), + "anonymous constants referencing generics are not yet supported", + ); + tcx.feed_anon_const_type(anon.def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, e))); + return ty::Const::new_error(tcx, e); + } tcx.feed_anon_const_type( anon.def_id, |
