From 8449d10a29b09c0852660ee168a77083d8179cf2 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Thu, 27 Jun 2024 14:11:54 +0800 Subject: Extend rules of dead code analysis for impls for adts to impls for types refer to adts --- compiler/rustc_passes/src/dead.rs | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index bbd586386dd..0a3b6ae06df 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -54,7 +54,24 @@ impl Publicness { } } -fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool { +fn adt_of<'tcx>(ty: &hir::Ty<'tcx>) -> Option<(LocalDefId, DefKind)> { + match ty.kind { + TyKind::Path(hir::QPath::Resolved(_, path)) => { + if let Res::Def(def_kind, def_id) = path.res + && let Some(local_def_id) = def_id.as_local() + { + Some((local_def_id, def_kind)) + } else { + None + } + } + TyKind::Slice(ty) | TyKind::Array(ty, _) => adt_of(ty), + TyKind::Ptr(ty) | TyKind::Ref(_, ty) => adt_of(ty.ty), + _ => None, + } +} + +fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: LocalDefId) -> bool { // treat PhantomData and positional ZST as public, // we don't want to lint types which only have them, // cause it's a common way to use such types to check things like well-formedness @@ -79,10 +96,7 @@ fn struct_all_fields_are_public(tcx: TyCtxt<'_>, id: DefId) -> bool { /// for enum and union, just check they are public, /// and doesn't solve types like &T for now, just skip them fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness { - if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && let Res::Def(def_kind, def_id) = path.res - && def_id.is_local() - { + if let Some((def_id, def_kind)) = adt_of(ty) { return match def_kind { DefKind::Enum | DefKind::Union => { let ty_is_public = tcx.visibility(def_id).is_public(); @@ -584,10 +598,8 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool { - if let TyKind::Path(hir::QPath::Resolved(_, path)) = - self.tcx.hir().item(impl_id).expect_impl().self_ty.kind - && let Res::Def(def_kind, def_id) = path.res - && let Some(local_def_id) = def_id.as_local() + if let Some((local_def_id, def_kind)) = + adt_of(self.tcx.hir().item(impl_id).expect_impl().self_ty) && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) { if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id @@ -931,7 +943,7 @@ fn create_and_seed_worklist( match tcx.def_kind(id) { DefKind::Impl { .. } => false, DefKind::AssocConst | DefKind::AssocTy | DefKind::AssocFn => !matches!(tcx.associated_item(id).container, AssocItemContainer::ImplContainer), - DefKind::Struct => struct_all_fields_are_public(tcx, id.to_def_id()) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), + DefKind::Struct => struct_all_fields_are_public(tcx, id) || has_allow_dead_code_or_lang_attr(tcx, id).is_some(), _ => true }) .map(|id| (id, ComesFromAllowExpect::No)) -- cgit 1.4.1-3-g733a5 From ef121f28d8684d32306becb7577b4e36d7c0ff08 Mon Sep 17 00:00:00 2001 From: León Orell Valerian Liehr Date: Mon, 10 Jun 2024 03:38:09 +0200 Subject: Suggesting an available assoc item is always maybe-incorrect --- compiler/rustc_hir_analysis/src/errors.rs | 8 +++++--- compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs | 12 +----------- 2 files changed, 6 insertions(+), 14 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index c83788928a9..d1f9ad9d58b 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -126,12 +126,14 @@ pub enum AssocItemNotFoundSugg<'a> { assoc_kind: &'static str, suggested_name: Symbol, }, - #[suggestion(hir_analysis_assoc_item_not_found_other_sugg, code = "{suggested_name}")] + #[suggestion( + hir_analysis_assoc_item_not_found_other_sugg, + code = "{suggested_name}", + applicability = "maybe-incorrect" + )] Other { #[primary_span] span: Span, - #[applicability] - applicability: Applicability, ty_param_name: &'a str, assoc_kind: &'static str, suggested_name: Symbol, 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 8ff6ced8b39..40675a1edba 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -269,20 +269,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } // If we still couldn't find any associated item, and only one associated item exists, - // suggests using it. + // suggest using it. if let [candidate_name] = all_candidate_names.as_slice() { - // This should still compile, except on `#![feature(associated_type_defaults)]` - // where it could suggests `type A = Self::A`, thus recursing infinitely. - let applicability = - if assoc_kind == ty::AssocKind::Type && tcx.features().associated_type_defaults { - Applicability::Unspecified - } else { - Applicability::MaybeIncorrect - }; - err.sugg = Some(errors::AssocItemNotFoundSugg::Other { span: assoc_name.span, - applicability, ty_param_name, assoc_kind: assoc_kind_str, suggested_name: *candidate_name, -- cgit 1.4.1-3-g733a5 From 898448ca5872aed82920dbb8342096e25e50aa5a Mon Sep 17 00:00:00 2001 From: León Orell Valerian Liehr Date: Fri, 14 Jun 2024 12:28:23 +0200 Subject: HIR ty lowering: Refactor the way the projectee ("QSelf") gets passed to diagnostics --- compiler/rustc_hir_analysis/messages.ftl | 6 +- compiler/rustc_hir_analysis/src/errors.rs | 6 +- .../src/hir_ty_lowering/bounds.rs | 9 +-- .../src/hir_ty_lowering/errors.rs | 59 +++++++++++------ .../rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 75 +++++++++++----------- 5 files changed, 86 insertions(+), 69 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index cc404daa51f..c209bf20c43 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -1,4 +1,4 @@ -hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$ty_param_name}` +hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$qself}` .label = ambiguous associated {$assoc_kind} `{$assoc_name}` hir_analysis_ambiguous_lifetime_bound = @@ -12,14 +12,14 @@ hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private .label = private {$kind} .defined_here_label = the {$kind} is defined here -hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}` +hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$qself}` hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named -> [true] an *[false] a similarly named } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}` hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found -hir_analysis_assoc_item_not_found_other_sugg = `{$ty_param_name}` has the following associated {$assoc_kind} +hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind} hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}` hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index d1f9ad9d58b..8e8b3b3d32a 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -22,7 +22,7 @@ pub struct AmbiguousAssocItem<'a> { pub span: Span, pub assoc_kind: &'static str, pub assoc_name: Ident, - pub ty_param_name: &'a str, + pub qself: &'a str, } #[derive(Diagnostic)] @@ -75,7 +75,7 @@ pub struct AssocItemNotFound<'a> { pub span: Span, pub assoc_name: Ident, pub assoc_kind: &'static str, - pub ty_param_name: &'a str, + pub qself: &'a str, #[subdiagnostic] pub label: Option>, #[subdiagnostic] @@ -134,7 +134,7 @@ pub enum AssocItemNotFoundSugg<'a> { Other { #[primary_span] span: Span, - ty_param_name: &'a str, + qself: &'a str, assoc_kind: &'static str, suggested_name: Symbol, }, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 6f9c481650b..214eb6b2f06 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -6,7 +6,6 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::bug; -use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; use rustc_span::symbol::Ident; use rustc_span::{ErrorGuaranteed, Span, Symbol}; @@ -16,9 +15,8 @@ use smallvec::SmallVec; use crate::bounds::Bounds; use crate::errors; -use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter}; - -use super::RegionInferReason; +use crate::hir_ty_lowering::HirTyLowerer; +use crate::hir_ty_lowering::{AssocItemQSelf, OnlySelfBounds, PredicateFilter, RegionInferReason}; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Add a `Sized` bound to the `bounds` if appropriate. @@ -288,8 +286,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // one that does define it. self.probe_single_bound_for_assoc_item( || traits::supertraits(tcx, trait_ref), - trait_ref.skip_binder().print_only_trait_name(), - None, + AssocItemQSelf::Trait(trait_ref.def_id()), assoc_kind, constraint.ident, path_span, 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 40675a1edba..ca741e4fbb6 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -3,7 +3,7 @@ use crate::errors::{ ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits, }; use crate::fluent_generated as fluent; -use crate::hir_ty_lowering::HirTyLowerer; +use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordMap; @@ -11,9 +11,9 @@ use rustc_errors::MultiSpan; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, }; +use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, Node}; +use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::query::Key; use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; @@ -116,8 +116,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub(super) fn complain_about_assoc_item_not_found( &self, all_candidates: impl Fn() -> I, - ty_param_name: &str, - ty_param_def_id: Option, + qself: AssocItemQSelf, assoc_kind: ty::AssocKind, assoc_name: Ident, span: Span, @@ -139,7 +138,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } - let assoc_kind_str = super::assoc_kind_str(assoc_kind); + let assoc_kind_str = assoc_kind_str(assoc_kind); + let qself_str = qself.to_string(tcx); // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a // valid span, so we point at the whole path segment instead. @@ -149,7 +149,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: if is_dummy { span } else { assoc_name.span }, assoc_name, assoc_kind: assoc_kind_str, - ty_param_name, + qself: &qself_str, label: None, sugg: None, }; @@ -219,19 +219,28 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { suggested_name, identically_named: suggested_name == assoc_name.name, }); - let hir = tcx.hir(); - if let Some(def_id) = ty_param_def_id - && let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id)) - && let Some(generics) = hir.get_generics(parent.def_id) + if let AssocItemQSelf::TyParam(ty_param_def_id) = qself + // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're + // inside an opaque type while we're interested in the overarching type alias (TAIT). + // FIXME: However, for trait aliases, this incorrectly returns the enclosing module... + && let item_def_id = + tcx.hir().get_parent_item(tcx.local_def_id_to_hir_id(ty_param_def_id)) + // FIXME: ...which obviously won't have any generics. + && let Some(generics) = tcx.hir().get_generics(item_def_id.def_id) { - if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any( - |b| match b { + // FIXME: Suggest adding supertrait bounds if we have a `Self` type param. + // FIXME(trait_alias): Suggest adding `Self: Trait` to + // `trait Alias = where Self::Proj:;` with `trait Trait { type Proj; }`. + if generics + .bounds_for_param(ty_param_def_id) + .flat_map(|pred| pred.bounds.iter()) + .any(|b| match b { hir::GenericBound::Trait(t, ..) => { t.trait_ref.trait_def_id() == Some(best_trait) } _ => false, - }, - ) { + }) + { // The type param already has a bound for `trait_name`, we just need to // change the associated item. err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait { @@ -247,7 +256,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx, generics, &mut err, - &ty_param_name, + &qself_str, &trait_name, None, None, @@ -273,7 +282,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let [candidate_name] = all_candidate_names.as_slice() { err.sugg = Some(errors::AssocItemNotFoundSugg::Other { span: assoc_name.span, - ty_param_name, + qself: &qself_str, assoc_kind: assoc_kind_str, suggested_name: *candidate_name, }); @@ -339,10 +348,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.dcx().emit_err(errors::AssocKindMismatch { span, - expected: super::assoc_kind_str(expected), - got: super::assoc_kind_str(got), + expected: assoc_kind_str(expected), + got: assoc_kind_str(got), expected_because_label, - assoc_kind: super::assoc_kind_str(assoc_item.kind), + assoc_kind: assoc_kind_str(assoc_item.kind), def_span: tcx.def_span(assoc_item.def_id), bound_on_assoc_const_label, wrap_in_braces_sugg, @@ -736,7 +745,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id)); in_expr_or_pat = match grandparent { - Node::Expr(_) | Node::Pat(_) => true, + hir::Node::Expr(_) | hir::Node::Pat(_) => true, _ => false, }; match bound.trait_ref.path.segments { @@ -1602,3 +1611,11 @@ fn generics_args_err_extend<'a>( _ => {} } } + +pub(super) fn assoc_kind_str(kind: ty::AssocKind) -> &'static str { + match kind { + ty::AssocKind::Fn => "function", + ty::AssocKind::Const => "constant", + ty::AssocKind::Type => "type", + } +} 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 d6eb1a66902..8d9055076a3 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -55,7 +55,6 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, ObligationCtxt}; -use std::fmt::Display; use std::slice; /// A path segment that is semantically allowed to have generic arguments. @@ -193,6 +192,25 @@ pub trait HirTyLowerer<'tcx> { } } +/// The "qualified self" of an associated item path. +/// +/// For diagnostic purposes only. +enum AssocItemQSelf { + Trait(DefId), + TyParam(LocalDefId), + SelfTyAlias, +} + +impl AssocItemQSelf { + fn to_string(&self, tcx: TyCtxt<'_>) -> String { + match *self { + Self::Trait(def_id) => tcx.def_path_str(def_id), + Self::TyParam(def_id) => tcx.hir().ty_param_name(def_id).to_string(), + Self::SelfTyAlias => kw::SelfUpper.to_string(), + } + } +} + /// New-typed boolean indicating whether explicit late-bound lifetimes /// are present in a set of generic arguments. /// @@ -811,19 +829,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name).predicates; debug!("predicates={:#?}", predicates); - let param_name = tcx.hir().ty_param_name(ty_param_def_id); self.probe_single_bound_for_assoc_item( || { - traits::transitive_bounds_that_define_assoc_item( - tcx, - predicates - .iter() - .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))), - assoc_name, - ) + let trait_refs = predicates + .iter() + .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))); + traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name) }, - param_name, - Some(ty_param_def_id), + AssocItemQSelf::TyParam(ty_param_def_id), ty::AssocKind::Type, assoc_name, span, @@ -835,12 +848,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// /// This fails if there is no such bound in the list of candidates or if there are multiple /// candidates in which case it reports ambiguity. - #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, constraint), ret)] + #[instrument(level = "debug", skip(self, all_candidates, qself, constraint), ret)] fn probe_single_bound_for_assoc_item( &self, all_candidates: impl Fn() -> I, - ty_param_name: impl Display, - ty_param_def_id: Option, + qself: AssocItemQSelf, assoc_kind: ty::AssocKind, assoc_name: Ident, span: Span, @@ -858,8 +870,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let Some(bound) = matching_candidates.next() else { let reported = self.complain_about_assoc_item_not_found( all_candidates, - &ty_param_name.to_string(), - ty_param_def_id, + qself, assoc_kind, assoc_name, span, @@ -872,13 +883,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let Some(bound2) = matching_candidates.next() { debug!(?bound2); - let assoc_kind_str = assoc_kind_str(assoc_kind); - let ty_param_name = &ty_param_name.to_string(); + let assoc_kind_str = errors::assoc_kind_str(assoc_kind); + let qself_str = qself.to_string(tcx); let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem { span, assoc_kind: assoc_kind_str, assoc_name, - ty_param_name, + qself: &qself_str, }); // Provide a more specific error code index entry for equality bindings. err.code( @@ -929,7 +940,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.span_suggestion_verbose( span.with_hi(assoc_name.span.lo()), "use fully-qualified syntax to disambiguate", - format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()), + format!("<{qself_str} as {}>::", bound.print_only_trait_path()), Applicability::MaybeIncorrect, ); } @@ -943,7 +954,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if !where_bounds.is_empty() { err.help(format!( "consider introducing a new type parameter `T` and adding `where` constraints:\ - \n where\n T: {ty_param_name},\n{}", + \n where\n T: {qself_str},\n{}", where_bounds.join(",\n"), )); } @@ -997,11 +1008,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); let assoc_ident = assoc_segment.ident; - let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { - path.res - } else { - Res::Err - }; // Check if we have an enum variant or an inherent associated type. let mut variant_resolution = None; @@ -1038,6 +1044,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { + path.res + } else { + Res::Err + }; + // Find the type of the associated item, and the trait where the associated // item is declared. let bound = match (&qself_ty.kind(), qself_res) { @@ -1056,8 +1068,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Binder::dummy(trait_ref.instantiate_identity()), ) }, - kw::SelfUpper, - None, + AssocItemQSelf::SelfTyAlias, ty::AssocKind::Type, assoc_ident, span, @@ -2522,11 +2533,3 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Some(r) } } - -fn assoc_kind_str(kind: ty::AssocKind) -> &'static str { - match kind { - ty::AssocKind::Fn => "function", - ty::AssocKind::Const => "constant", - ty::AssocKind::Type => "type", - } -} -- cgit 1.4.1-3-g733a5 From 3c8b1085127b64de64cb6d880b59b55c1d6fc8e9 Mon Sep 17 00:00:00 2001 From: León Orell Valerian Liehr Date: Fri, 14 Jun 2024 12:38:21 +0200 Subject: Inside eager ty aliases on unresolved assoc tys suggest fully qualifying instead of restricting their self ty --- compiler/rustc_hir_analysis/messages.ftl | 7 ++- compiler/rustc_hir_analysis/src/errors.rs | 16 +++++++ .../src/hir_ty_lowering/errors.rs | 55 ++++++++++++++-------- .../rustc_hir_analysis/src/hir_ty_lowering/mod.rs | 8 ++-- tests/ui/resolve/issue-55673.stderr | 2 +- .../unresolved-assoc-ty-suggest-trait.eager.stderr | 25 ++++++++++ .../unresolved-assoc-ty-suggest-trait.lazy.stderr | 29 ++++++++++++ .../unresolved-assoc-ty-suggest-trait.rs | 20 ++++++++ 8 files changed, 138 insertions(+), 24 deletions(-) create mode 100644 tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr create mode 100644 tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr create mode 100644 tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs (limited to 'compiler') diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index c209bf20c43..367f6e17e7f 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -20,8 +20,13 @@ hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identi } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}` hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind} +hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg = + consider fully qualifying{$identically_named -> + [true] {""} + *[false] {" "}and renaming + } the associated {$assoc_kind} hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}` -hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name +hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = ...and changing the associated {$assoc_kind} name hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got} diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 8e8b3b3d32a..90861ba1ff8 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -126,6 +126,22 @@ pub enum AssocItemNotFoundSugg<'a> { assoc_kind: &'static str, suggested_name: Symbol, }, + #[multipart_suggestion( + hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg, + style = "verbose", + applicability = "maybe-incorrect" + )] + SimilarInOtherTraitQPath { + #[suggestion_part(code = "<")] + lo: Span, + #[suggestion_part(code = " as {trait_}>")] + mi: Span, + #[suggestion_part(code = "{suggested_name}")] + hi: Option, + trait_: &'a str, + suggested_name: Symbol, + identically_named: bool, + }, #[suggestion( hir_analysis_assoc_item_not_found_other_sugg, code = "{suggested_name}", 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 ca741e4fbb6..fd2bbabe779 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -219,7 +219,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { suggested_name, identically_named: suggested_name == assoc_name.name, }); - if let AssocItemQSelf::TyParam(ty_param_def_id) = qself + if let AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span) = qself // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're // inside an opaque type while we're interested in the overarching type alias (TAIT). // FIXME: However, for trait aliases, this incorrectly returns the enclosing module... @@ -251,27 +251,44 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return self.dcx().emit_err(err); } - let mut err = self.dcx().create_err(err); - if suggest_constraining_type_param( - tcx, - generics, - &mut err, - &qself_str, - &trait_name, - None, - None, - ) && suggested_name != assoc_name.name + let identically_named = suggested_name == assoc_name.name; + + if let DefKind::TyAlias = tcx.def_kind(item_def_id) + && !tcx.type_alias_is_lazy(item_def_id) { - // We suggested constraining a type parameter, but the associated item on it - // was also not an exact match, so we also suggest changing it. - err.span_suggestion_verbose( - assoc_name.span, - fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, + err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTraitQPath { + lo: ty_param_span.shrink_to_lo(), + mi: ty_param_span.shrink_to_hi(), + hi: (!identically_named).then_some(assoc_name.span), + // FIXME(fmease): Use a full trait ref here (with placeholders). + trait_: &trait_name, + identically_named, suggested_name, - Applicability::MaybeIncorrect, - ); + }); + } else { + let mut err = self.dcx().create_err(err); + if suggest_constraining_type_param( + tcx, + generics, + &mut err, + &qself_str, + // FIXME(fmease): Use a full trait ref here (with placeholders). + &trait_name, + None, + None, + ) && !identically_named + { + // We suggested constraining a type parameter, but the associated item on it + // was also not an exact match, so we also suggest changing it. + err.span_suggestion_verbose( + assoc_name.span, + fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, + suggested_name, + Applicability::MaybeIncorrect, + ); + } + return err.emit(); } - return err.emit(); } return self.dcx().emit_err(err); } 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 8d9055076a3..ce298641e60 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -197,7 +197,7 @@ pub trait HirTyLowerer<'tcx> { /// For diagnostic purposes only. enum AssocItemQSelf { Trait(DefId), - TyParam(LocalDefId), + TyParam(LocalDefId, Span), SelfTyAlias, } @@ -205,7 +205,7 @@ impl AssocItemQSelf { fn to_string(&self, tcx: TyCtxt<'_>) -> String { match *self { Self::Trait(def_id) => tcx.def_path_str(def_id), - Self::TyParam(def_id) => tcx.hir().ty_param_name(def_id).to_string(), + Self::TyParam(def_id, _) => tcx.hir().ty_param_name(def_id).to_string(), Self::SelfTyAlias => kw::SelfUpper.to_string(), } } @@ -820,6 +820,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn probe_single_ty_param_bound_for_assoc_ty( &self, ty_param_def_id: LocalDefId, + ty_param_span: Span, assoc_name: Ident, span: Span, ) -> Result, ErrorGuaranteed> { @@ -836,7 +837,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))); traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name) }, - AssocItemQSelf::TyParam(ty_param_def_id), + AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span), ty::AssocKind::Type, assoc_name, span, @@ -1080,6 +1081,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), ) => self.probe_single_ty_param_bound_for_assoc_ty( param_did.expect_local(), + qself.span, assoc_ident, span, )?, diff --git a/tests/ui/resolve/issue-55673.stderr b/tests/ui/resolve/issue-55673.stderr index ffc3252230a..4069b35a998 100644 --- a/tests/ui/resolve/issue-55673.stderr +++ b/tests/ui/resolve/issue-55673.stderr @@ -19,7 +19,7 @@ help: consider further restricting type parameter `T` | LL | T::Baa: std::fmt::Debug, T: Foo | ~~~~~~~~ -help: and also change the associated type name +help: ...and changing the associated type name | LL | T::Bar: std::fmt::Debug, | ~~~ diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr new file mode 100644 index 00000000000..8e6490c548d --- /dev/null +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr @@ -0,0 +1,25 @@ +error[E0220]: associated type `Assoc` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22 + | +LL | type AssocOf = T::Assoc; + | ^^^^^ there is an associated type `Assoc` in the trait `Trait` + | +help: consider fully qualifying the associated type + | +LL | type AssocOf = ::Assoc; + | + +++++++++ + +error[E0220]: associated type `Assok` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22 + | +LL | type AssokOf = T::Assok; + | ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait` + | +help: consider fully qualifying and renaming the associated type + | +LL | type AssokOf = ::Assoc; + | + +++++++++ ~~~~~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr new file mode 100644 index 00000000000..051f181cb6f --- /dev/null +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr @@ -0,0 +1,29 @@ +error[E0220]: associated type `Assoc` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22 + | +LL | type AssocOf = T::Assoc; + | ^^^^^ there is an associated type `Assoc` in the trait `Trait` + | +help: consider restricting type parameter `T` + | +LL | type AssocOf = T::Assoc; + | +++++++ + +error[E0220]: associated type `Assok` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22 + | +LL | type AssokOf = T::Assok; + | ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait` + | +help: consider restricting type parameter `T` + | +LL | type AssokOf = T::Assok; + | +++++++ +help: ...and changing the associated type name + | +LL | type AssokOf = T::Assoc; + | ~~~~~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs new file mode 100644 index 00000000000..75e4eb496b4 --- /dev/null +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs @@ -0,0 +1,20 @@ +// Ensure that we don't suggest *type alias bounds* for **eager** type aliases. +// issue: rust-lang/rust#125789 + +//@ revisions: eager lazy +#![cfg_attr(lazy, feature(lazy_type_alias), allow(incomplete_features))] + +// FIXME(fmease): Suggest a full trait ref (with placeholders) instead of just a trait name. + +trait Trait { type Assoc; } + +type AssocOf = T::Assoc; //~ ERROR associated type `Assoc` not found for `T` +//[eager]~^ HELP consider fully qualifying the associated type +//[lazy]~| HELP consider restricting type parameter `T` + +type AssokOf = T::Assok; //~ ERROR associated type `Assok` not found for `T` +//[eager]~^ HELP consider fully qualifying and renaming the associated type +//[lazy]~| HELP consider restricting type parameter `T` +//[lazy]~| HELP and changing the associated type name + +fn main() {} -- cgit 1.4.1-3-g733a5 From 02a2f0272736c65b51996b0c6e7cbd991ac21d5c Mon Sep 17 00:00:00 2001 From: León Orell Valerian Liehr Date: Sat, 15 Jun 2024 11:08:12 +0200 Subject: Suggest full trait ref (with placeholders) on unresolved assoc tys --- compiler/rustc_hir_analysis/src/errors.rs | 9 ++++---- .../src/hir_ty_lowering/errors.rs | 25 +++++++++++++--------- .../unresolved-assoc-ty-suggest-trait.eager.stderr | 17 ++++++++++++--- .../unresolved-assoc-ty-suggest-trait.lazy.stderr | 17 ++++++++++++--- .../unresolved-assoc-ty-suggest-trait.rs | 12 ++++++++--- 5 files changed, 57 insertions(+), 23 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 90861ba1ff8..c364a561631 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -128,19 +128,20 @@ pub enum AssocItemNotFoundSugg<'a> { }, #[multipart_suggestion( hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg, - style = "verbose", - applicability = "maybe-incorrect" + style = "verbose" )] SimilarInOtherTraitQPath { #[suggestion_part(code = "<")] lo: Span, - #[suggestion_part(code = " as {trait_}>")] + #[suggestion_part(code = " as {trait_ref}>")] mi: Span, #[suggestion_part(code = "{suggested_name}")] hi: Option, - trait_: &'a str, + trait_ref: String, suggested_name: Symbol, identically_named: bool, + #[applicability] + applicability: Applicability, }, #[suggestion( hir_analysis_assoc_item_not_found_other_sugg, 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 fd2bbabe779..56f508a2d43 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -251,6 +251,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return self.dcx().emit_err(err); } + let trait_args = &ty::GenericArgs::identity_for_item(tcx, best_trait)[1..]; + let mut trait_ref = trait_name.clone(); + let applicability = if let [arg, args @ ..] = trait_args { + use std::fmt::Write; + write!(trait_ref, ""; + Applicability::HasPlaceholders + } else { + Applicability::MaybeIncorrect + }; + let identically_named = suggested_name == assoc_name.name; if let DefKind::TyAlias = tcx.def_kind(item_def_id) @@ -260,22 +272,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { lo: ty_param_span.shrink_to_lo(), mi: ty_param_span.shrink_to_hi(), hi: (!identically_named).then_some(assoc_name.span), - // FIXME(fmease): Use a full trait ref here (with placeholders). - trait_: &trait_name, + trait_ref, identically_named, suggested_name, + applicability, }); } else { let mut err = self.dcx().create_err(err); if suggest_constraining_type_param( - tcx, - generics, - &mut err, - &qself_str, - // FIXME(fmease): Use a full trait ref here (with placeholders). - &trait_name, - None, - None, + tcx, generics, &mut err, &qself_str, &trait_ref, None, None, ) && !identically_named { // We suggested constraining a type parameter, but the associated item on it diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr index 8e6490c548d..e891ff10fda 100644 --- a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr @@ -1,5 +1,5 @@ error[E0220]: associated type `Assoc` not found for `T` - --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22 + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:9:22 | LL | type AssocOf = T::Assoc; | ^^^^^ there is an associated type `Assoc` in the trait `Trait` @@ -10,7 +10,7 @@ LL | type AssocOf = ::Assoc; | + +++++++++ error[E0220]: associated type `Assok` not found for `T` - --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22 + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:13:22 | LL | type AssokOf = T::Assok; | ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait` @@ -20,6 +20,17 @@ help: consider fully qualifying and renaming the associated type LL | type AssokOf = ::Assoc; | + +++++++++ ~~~~~ -error: aborting due to 2 previous errors +error[E0220]: associated type `Proj` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:22:21 + | +LL | type ProjOf = T::Proj; + | ^^^^ there is an associated type `Proj` in the trait `Parametrized` + | +help: consider fully qualifying the associated type + | +LL | type ProjOf = >::Proj; + | + ++++++++++++++++++++++++++++++++ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr index 051f181cb6f..96179a7b484 100644 --- a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr @@ -1,5 +1,5 @@ error[E0220]: associated type `Assoc` not found for `T` - --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22 + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:9:22 | LL | type AssocOf = T::Assoc; | ^^^^^ there is an associated type `Assoc` in the trait `Trait` @@ -10,7 +10,7 @@ LL | type AssocOf = T::Assoc; | +++++++ error[E0220]: associated type `Assok` not found for `T` - --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22 + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:13:22 | LL | type AssokOf = T::Assok; | ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait` @@ -24,6 +24,17 @@ help: ...and changing the associated type name LL | type AssokOf = T::Assoc; | ~~~~~ -error: aborting due to 2 previous errors +error[E0220]: associated type `Proj` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:22:21 + | +LL | type ProjOf = T::Proj; + | ^^^^ there is an associated type `Proj` in the trait `Parametrized` + | +help: consider restricting type parameter `T` + | +LL | type ProjOf> = T::Proj; + | ++++++++++++++++++++++++++++++ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs index 75e4eb496b4..2c8d448f308 100644 --- a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs @@ -4,9 +4,7 @@ //@ revisions: eager lazy #![cfg_attr(lazy, feature(lazy_type_alias), allow(incomplete_features))] -// FIXME(fmease): Suggest a full trait ref (with placeholders) instead of just a trait name. - -trait Trait { type Assoc; } +trait Trait { type Assoc; } type AssocOf = T::Assoc; //~ ERROR associated type `Assoc` not found for `T` //[eager]~^ HELP consider fully qualifying the associated type @@ -17,4 +15,12 @@ type AssokOf = T::Assok; //~ ERROR associated type `Assok` not found for `T` //[lazy]~| HELP consider restricting type parameter `T` //[lazy]~| HELP and changing the associated type name +trait Parametrized<'a, T, const N: usize> { + type Proj; +} + +type ProjOf = T::Proj; //~ ERROR associated type `Proj` not found for `T` +//[eager]~^ HELP consider fully qualifying the associated type +//[lazy]~| HELP consider restricting type parameter `T` + fn main() {} -- cgit 1.4.1-3-g733a5 From 63a54d93be33bb1c0357c48532df778f6f2a416b Mon Sep 17 00:00:00 2001 From: León Orell Valerian Liehr Date: Fri, 14 Jun 2024 15:30:11 +0200 Subject: Don't suppress lint type_alias_bounds for ty aliases containing inherent assoc tys --- compiler/rustc_lint/src/builtin.rs | 28 ++++++++------------- compiler/rustc_type_ir/src/visit.rs | 4 --- .../type-alias-bounds-are-enforced.rs | 20 --------------- .../associated-inherent-types/type-alias-bounds.rs | 29 ++++++++++++++++++++++ .../type-alias-bounds.stderr | 15 +++++++++++ 5 files changed, 55 insertions(+), 41 deletions(-) delete mode 100644 tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs create mode 100644 tests/ui/associated-inherent-types/type-alias-bounds.rs create mode 100644 tests/ui/associated-inherent-types/type-alias-bounds.stderr (limited to 'compiler') diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 9ebada0fff3..40b336cd5ea 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1425,30 +1425,27 @@ impl TypeAliasBounds { impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - let hir::ItemKind::TyAlias(hir_ty, type_alias_generics) = &item.kind else { return }; + let hir::ItemKind::TyAlias(hir_ty, generics) = &item.kind else { return }; - // Bounds of lazy type aliases and TAITs are respected. - if cx.tcx.type_alias_is_lazy(item.owner_id) { + // There must not be a where clause. + if generics.predicates.is_empty() { return; } - let ty = cx.tcx.type_of(item.owner_id).skip_binder(); - if ty.has_inherent_projections() { - // Bounds of type aliases that contain opaque types or inherent projections are - // respected. E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`, `type X = - // Type::Inherent;`. + // Bounds of lazy type aliases and TAITs are respected. + if cx.tcx.type_alias_is_lazy(item.owner_id) { return; } - // There must not be a where clause - if type_alias_generics.predicates.is_empty() { - return; - } + // NOTE(inherent_associated_types): While we currently do take some bounds in type + // aliases into consideration during IAT *selection*, we don't perform full use+def + // site wfchecking for such type aliases. Therefore TAB should still trigger. + // See also `tests/ui/associated-inherent-types/type-alias-bounds.rs`. let mut where_spans = Vec::new(); let mut inline_spans = Vec::new(); let mut inline_sugg = Vec::new(); - for p in type_alias_generics.predicates { + for p in generics.predicates { let span = p.span(); if p.in_where_clause() { where_spans.push(span); @@ -1469,10 +1466,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { cx.emit_span_lint( TYPE_ALIAS_BOUNDS, where_spans, - BuiltinTypeAliasWhereClause { - suggestion: type_alias_generics.where_clause_span, - sub, - }, + BuiltinTypeAliasWhereClause { suggestion: generics.where_clause_span, sub }, ); } diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 6ec38b78fc2..d5e114b2175 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -241,10 +241,6 @@ pub trait TypeVisitableExt: TypeVisitable { self.has_type_flags(TypeFlags::HAS_ALIAS) } - fn has_inherent_projections(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_INHERENT) - } - fn has_opaque_types(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) } diff --git a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs deleted file mode 100644 index 5ac7e1e58b8..00000000000 --- a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ compile-flags: --crate-type=lib -//@ check-pass - -#![feature(inherent_associated_types)] -#![allow(incomplete_features)] - -// Bounds on the self type play a major role in the resolution of inherent associated types (*). -// As a result of that, if a type alias contains any then its bounds have to be respected and the -// lint `type_alias_bounds` should not fire. - -#![deny(type_alias_bounds)] - -pub type Alias = (Source::Assoc,); - -pub struct Source(T); -pub trait Bound {} - -impl Source { - pub type Assoc = (); -} diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.rs b/tests/ui/associated-inherent-types/type-alias-bounds.rs new file mode 100644 index 00000000000..c27bcbed43a --- /dev/null +++ b/tests/ui/associated-inherent-types/type-alias-bounds.rs @@ -0,0 +1,29 @@ +//@ compile-flags: --crate-type=lib +//@ check-pass + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// FIXME(inherent_associated_types): +// While we currently do take some clauses of the ParamEnv into consideration +// when performing IAT selection, we do not perform full well-formedness checking +// for (eager) type alias definition and usage sites. +// +// Therefore it's *correct* for lint `type_alias_bounds` to fire here despite the +// fact that removing `Bound` from `T` in `Alias` would lead to an error! +// +// Obviously, the present situation isn't ideal and we should fix it in one way +// or another. Either we somehow delay IAT selection until after HIR ty lowering +// to avoid the need to specify any bounds inside (eager) type aliases or we +// force the overarching type alias to be *lazy* (similar to TAITs) which would +// automatically lead to full wfchecking and lint TAB getting suppressed. + +pub type Alias = (Source::Assoc,); +//~^ WARN bounds on generic parameters are not enforced in type aliases + +pub struct Source(T); +pub trait Bound {} + +impl Source { + pub type Assoc = (); +} diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.stderr b/tests/ui/associated-inherent-types/type-alias-bounds.stderr new file mode 100644 index 00000000000..c5d0e9e5041 --- /dev/null +++ b/tests/ui/associated-inherent-types/type-alias-bounds.stderr @@ -0,0 +1,15 @@ +warning: bounds on generic parameters are not enforced in type aliases + --> $DIR/type-alias-bounds.rs:21:19 + | +LL | pub type Alias = (Source::Assoc,); + | ^^^^^ + | + = note: `#[warn(type_alias_bounds)]` on by default +help: the bound will not be checked when the type alias is used, and should be removed + | +LL - pub type Alias = (Source::Assoc,); +LL + pub type Alias = (Source::Assoc,); + | + +warning: 1 warning emitted + -- cgit 1.4.1-3-g733a5 From a8b3dfd25336dc12f17da23a0ed7004a4ebee234 Mon Sep 17 00:00:00 2001 From: León Orell Valerian Liehr Date: Fri, 14 Jun 2024 16:18:32 +0200 Subject: Suppress lint type_alias_bounds for ty aliases containing const projections under GCE --- compiler/rustc_lint/src/builtin.rs | 10 +++ .../type-alias-bounds.neg.stderr | 63 +++++++++++++++++++ .../generic_const_exprs/type-alias-bounds.rs | 71 ++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr create mode 100644 tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs (limited to 'compiler') diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 40b336cd5ea..90555007607 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1437,6 +1437,16 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { return; } + + // FIXME(generic_const_exprs): Revisit this before stabilization. + // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`. + let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); + if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) + && cx.tcx.features().generic_const_exprs + { + return; + } + // NOTE(inherent_associated_types): While we currently do take some bounds in type // aliases into consideration during IAT *selection*, we don't perform full use+def // site wfchecking for such type aliases. Therefore TAB should still trigger. diff --git a/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr new file mode 100644 index 00000000000..fa12dd14753 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr @@ -0,0 +1,63 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:23:12 + | +LL | let _: AliasConstUnused; + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `ct_unused_0::AliasConstUnused` + --> $DIR/type-alias-bounds.rs:20:30 + | +LL | type AliasConstUnused = (T, I32<{ DATA }>); + | ^^^^ required by this bound in `AliasConstUnused` + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:31:12 + | +LL | let _: AliasConstUnused; + | ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `ct_unused_1::AliasConstUnused` + --> $DIR/type-alias-bounds.rs:29:41 + | +LL | type AliasConstUnused where String: Copy = I32<{ 0; 0 }>; + | ^^^^ required by this bound in `AliasConstUnused` + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:39:12 + | +LL | let _: AliasFnUnused; + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `AliasFnUnused` + --> $DIR/type-alias-bounds.rs:36:27 + | +LL | type AliasFnUnused = (T, I32<{ code() }>); + | ^^^^ required by this bound in `AliasFnUnused` + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:57:12 + | +LL | let _: AliasAssocConstUsed; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `AliasAssocConstUsed` + --> $DIR/type-alias-bounds.rs:55:41 + | +LL | type AliasAssocConstUsed = I32<{ T::DATA }>; + | ^^^^ required by this bound in `AliasAssocConstUsed` + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:65:12 + | +LL | let _: AliasFnUsed; + | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `AliasFnUsed` + --> $DIR/type-alias-bounds.rs:62:33 + | +LL | type AliasFnUsed = I32<{ code::() }>; + | ^^^^ required by this bound in `AliasFnUsed` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs new file mode 100644 index 00000000000..f16e646129c --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs @@ -0,0 +1,71 @@ +//@ revisions: pos neg +//@[pos] check-pass + +#![feature(generic_const_exprs)] +#![feature(trivial_bounds)] // only used in test case `ct_unused_1` +#![allow(incomplete_features)] + +// FIXME(generic_const_exprs): Revisit this before stabilization. +// Check that we don't emit the lint `type_alias_bounds` for (eager) type aliases +// whose RHS contains a const projection (aka uneval'ed const). +// Since anon consts inherit the parent generics and predicates and we effectively +// check them before and after instantiaton for well-formedness, the type alias +// bounds are in every sense "enforced". +// Note that the test cases whose name ends in "unused" just demonstrate that this +// holds even if the const projections don't "visibly" capture any generics and/or +// predicates. +#![deny(type_alias_bounds)] + +fn ct_unused_0() { + type AliasConstUnused = (T, I32<{ DATA }>); + const DATA: i32 = 0; + #[cfg(neg)] + let _: AliasConstUnused; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +fn ct_unused_1() { + #[allow(trivial_bounds)] + type AliasConstUnused where String: Copy = I32<{ 0; 0 }>; + #[cfg(neg)] + let _: AliasConstUnused; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +fn fn_unused() { + type AliasFnUnused = (T, I32<{ code() }>); + const fn code() -> i32 { 0 } + #[cfg(neg)] + let _: AliasFnUnused; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +trait Trait { + type Proj; + const DATA: i32; +} + +impl Trait for String { + type Proj = i32; + const DATA: i32 = 0; +} + +// Regression test for issue #94398. +fn assoc_ct_used() { + type AliasAssocConstUsed = I32<{ T::DATA }>; + #[cfg(neg)] + let _: AliasAssocConstUsed; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +fn fn_used() { + type AliasFnUsed = I32<{ code::() }>; + const fn code() -> i32 { T::DATA } + #[cfg(neg)] + let _: AliasFnUsed; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +struct I32; + +fn main() {} -- cgit 1.4.1-3-g733a5 From fdf8f024ad71c6e9c46867fb31b74df0fcaaf3f2 Mon Sep 17 00:00:00 2001 From: León Orell Valerian Liehr Date: Sat, 15 Jun 2024 21:34:44 +0200 Subject: Improve the impl and diag output of lint type_alias_bounds --- compiler/rustc_lint/messages.ftl | 19 +- compiler/rustc_lint/src/builtin.rs | 92 ++++++---- compiler/rustc_lint/src/lints.rs | 148 ++++++++------- .../associated-inherent-types/type-alias-bounds.rs | 2 +- .../type-alias-bounds.stderr | 15 +- tests/ui/associated-type-bounds/type-alias.stderr | 204 +++++++++++---------- tests/ui/privacy/private-in-public-warn.rs | 4 +- tests/ui/privacy/private-in-public-warn.stderr | 34 ++-- .../trivial-bounds-inconsistent.stderr | 17 +- ...ssue-67690-type-alias-bound-diagnostic-crash.rs | 2 +- ...-67690-type-alias-bound-diagnostic-crash.stderr | 15 +- tests/ui/type/type-alias-bounds.rs | 21 ++- tests/ui/type/type-alias-bounds.stderr | 163 ++++++++-------- 13 files changed, 401 insertions(+), 335 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 7d7b97e2eb1..ce99c8686ba 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -139,13 +139,18 @@ lint_builtin_special_module_name_used_main = found module declaration for main.r lint_builtin_trivial_bounds = {$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters -lint_builtin_type_alias_bounds_help = use fully disambiguated paths (i.e., `::Assoc`) to refer to associated types in type aliases - -lint_builtin_type_alias_generic_bounds = bounds on generic parameters are not enforced in type aliases - .suggestion = the bound will not be checked when the type alias is used, and should be removed - -lint_builtin_type_alias_where_clause = where clauses are not enforced in type aliases - .suggestion = the clause will not be checked when the type alias is used, and should be removed +lint_builtin_type_alias_bounds_enable_feat_help = add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics +lint_builtin_type_alias_bounds_label = will not be checked at usage sites of the type alias +lint_builtin_type_alias_bounds_limitation_note = this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information +lint_builtin_type_alias_bounds_param_bounds = bounds on generic parameters in type aliases are not enforced + .suggestion = remove {$count -> + [one] this bound + *[other] these bounds + } +lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg = fully qualify this associated type +lint_builtin_type_alias_bounds_where_clause = where clauses on type aliases are not enforced + .suggestion = remove this where clause lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 90555007607..a8c8c71927a 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -31,12 +31,12 @@ use crate::{ BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, - BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds, - BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause, - BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, - BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, - BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, - BuiltinWhileTrue, InvalidAsmLabel, SuggestChangingAssocTypes, + BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds, + BuiltinTypeAliasParamBoundsSuggestion, BuiltinUngatedAsyncFnTrackCaller, + BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, + BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, + BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, + TypeAliasBoundsQualifyAssocTysSugg, }, EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, }; @@ -1406,23 +1406,6 @@ declare_lint_pass!( TypeAliasBounds => [TYPE_ALIAS_BOUNDS] ); -impl TypeAliasBounds { - pub(crate) fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool { - match *qpath { - hir::QPath::TypeRelative(ty, _) => { - // If this is a type variable, we found a `T::Assoc`. - match ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(None, path)) => { - matches!(path.res, Res::Def(DefKind::TyParam, _)) - } - _ => false, - } - } - hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false, - } - } -} - impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { let hir::ItemKind::TyAlias(hir_ty, generics) = &item.kind else { return }; @@ -1437,7 +1420,6 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { return; } - // FIXME(generic_const_exprs): Revisit this before stabilization. // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`. let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); @@ -1455,6 +1437,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { let mut where_spans = Vec::new(); let mut inline_spans = Vec::new(); let mut inline_sugg = Vec::new(); + let mut affects_object_lifetime_defaults = false; + for p in generics.predicates { let span = p.span(); if p.in_where_clause() { @@ -1465,31 +1449,61 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { } inline_sugg.push((span, String::new())); } + + // FIXME(fmease): Move this into a "diagnostic decorator" for increased laziness + // Bounds of the form `T: 'a` where `T` is a type param of + // the type alias affect object lifetime defaults. + if !affects_object_lifetime_defaults + && let hir::WherePredicate::BoundPredicate(pred) = p + && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_))) + && pred.bound_generic_params.is_empty() + && let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = pred.bounded_ty.kind + && let Res::Def(DefKind::TyParam, _) = path.res + { + affects_object_lifetime_defaults = true; + } } - let mut suggested_changing_assoc_types = false; - if !where_spans.is_empty() { - let sub = (!suggested_changing_assoc_types).then(|| { - suggested_changing_assoc_types = true; - SuggestChangingAssocTypes { ty: hir_ty } - }); + // FIXME(fmease): Add a disclaimer (in the form of a multi-span note) that the removal of + // type-param-outlives-bounds affects OLDs and explicit object lifetime + // bounds might be required [...]. + // FIXME(fmease): The applicability should also depend on the outcome of the HIR walker + // inside of `TypeAliasBoundsQualifyAssocTysSugg`: Whether it found a + // shorthand projection or not. + let applicability = if affects_object_lifetime_defaults { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + + let mut qualify_assoc_tys_sugg = Some(TypeAliasBoundsQualifyAssocTysSugg { ty: hir_ty }); + let enable_feat_help = cx.tcx.sess.is_nightly_build().then_some(()); + + if let [.., label_sp] = *where_spans { cx.emit_span_lint( TYPE_ALIAS_BOUNDS, where_spans, - BuiltinTypeAliasWhereClause { suggestion: generics.where_clause_span, sub }, + BuiltinTypeAliasBounds::WhereClause { + label: label_sp, + enable_feat_help, + suggestion: (generics.where_clause_span, applicability), + qualify_assoc_tys_sugg: qualify_assoc_tys_sugg.take(), + }, ); } - - if !inline_spans.is_empty() { - let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg }; - let sub = (!suggested_changing_assoc_types).then(|| { - suggested_changing_assoc_types = true; - SuggestChangingAssocTypes { ty: hir_ty } - }); + if let [.., label_sp] = *inline_spans { cx.emit_span_lint( TYPE_ALIAS_BOUNDS, inline_spans, - BuiltinTypeAliasGenericBounds { suggestion, sub }, + BuiltinTypeAliasBounds::ParamBounds { + label: label_sp, + enable_feat_help, + suggestion: BuiltinTypeAliasParamBoundsSuggestion { + suggestions: inline_sugg, + applicability, + }, + qualify_assoc_tys_sugg, + }, ); } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 6c5f366727f..6cf8b9330fc 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -9,7 +9,7 @@ use rustc_errors::{ ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp, Subdiagnostic, SuggestionStyle, }; -use rustc_hir::{def::Namespace, def_id::DefId}; +use rustc_hir::{self as hir, def::Namespace, def_id::DefId}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{ inhabitedness::InhabitedPredicate, Clause, PolyExistentialTraitRef, Ty, TyCtxt, @@ -22,9 +22,7 @@ use rustc_span::{ Span, Symbol, }; -use crate::{ - builtin::InitError, builtin::TypeAliasBounds, errors::OverruledAttributeSub, LateContext, -}; +use crate::{builtin::InitError, errors::OverruledAttributeSub, LateContext}; // array_into_iter.rs #[derive(LintDiagnostic)] @@ -263,84 +261,104 @@ pub struct BuiltinUnreachablePub<'a> { pub help: Option<()>, } -pub struct SuggestChangingAssocTypes<'a, 'b> { - pub ty: &'a rustc_hir::Ty<'b>, +#[derive(LintDiagnostic)] +#[diag(lint_macro_expr_fragment_specifier_2024_migration)] +pub struct MacroExprFragment2024 { + #[suggestion(code = "expr_2021", applicability = "machine-applicable")] + pub suggestion: Span, +} + +#[derive(LintDiagnostic)] +pub enum BuiltinTypeAliasBounds<'a, 'hir> { + #[diag(lint_builtin_type_alias_bounds_where_clause)] + #[note(lint_builtin_type_alias_bounds_limitation_note)] + WhereClause { + #[label(lint_builtin_type_alias_bounds_label)] + label: Span, + #[help(lint_builtin_type_alias_bounds_enable_feat_help)] + enable_feat_help: Option<()>, + #[suggestion(code = "")] + suggestion: (Span, Applicability), + #[subdiagnostic] + qualify_assoc_tys_sugg: Option>, + }, + #[diag(lint_builtin_type_alias_bounds_param_bounds)] + #[note(lint_builtin_type_alias_bounds_limitation_note)] + ParamBounds { + #[label(lint_builtin_type_alias_bounds_label)] + label: Span, + #[help(lint_builtin_type_alias_bounds_enable_feat_help)] + enable_feat_help: Option<()>, + #[subdiagnostic] + suggestion: BuiltinTypeAliasParamBoundsSuggestion, + #[subdiagnostic] + qualify_assoc_tys_sugg: Option>, + }, +} + +pub struct BuiltinTypeAliasParamBoundsSuggestion { + pub suggestions: Vec<(Span, String)>, + pub applicability: Applicability, } -impl<'a, 'b> Subdiagnostic for SuggestChangingAssocTypes<'a, 'b> { +impl Subdiagnostic for BuiltinTypeAliasParamBoundsSuggestion { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, _f: &F, ) { - // Access to associates types should use `::Assoc`, which does not need a - // bound. Let's see if this type does that. - - // We use a HIR visitor to walk the type. - use rustc_hir::intravisit::{self, Visitor}; - struct WalkAssocTypes<'a, 'b, G: EmissionGuarantee> { - err: &'a mut Diag<'b, G>, - } - impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for WalkAssocTypes<'a, 'b, G> { - fn visit_qpath( - &mut self, - qpath: &rustc_hir::QPath<'_>, - id: rustc_hir::HirId, - span: Span, - ) { - if TypeAliasBounds::is_type_variable_assoc(qpath) { - self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help); - } - intravisit::walk_qpath(self, qpath, id) - } - } - - // Let's go for a walk! - let mut visitor = WalkAssocTypes { err: diag }; - visitor.visit_ty(self.ty); + diag.arg("count", self.suggestions.len()); + diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, self.applicability); } } -#[derive(LintDiagnostic)] -#[diag(lint_builtin_type_alias_where_clause)] -pub struct BuiltinTypeAliasWhereClause<'a, 'b> { - #[suggestion(code = "", applicability = "machine-applicable")] - pub suggestion: Span, - #[subdiagnostic] - pub sub: Option>, -} - -#[derive(LintDiagnostic)] -#[diag(lint_builtin_type_alias_generic_bounds)] -pub struct BuiltinTypeAliasGenericBounds<'a, 'b> { - #[subdiagnostic] - pub suggestion: BuiltinTypeAliasGenericBoundsSuggestion, - #[subdiagnostic] - pub sub: Option>, -} - -#[derive(LintDiagnostic)] -#[diag(lint_macro_expr_fragment_specifier_2024_migration)] -pub struct MacroExprFragment2024 { - #[suggestion(code = "expr_2021", applicability = "machine-applicable")] - pub suggestion: Span, +pub struct TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir> { + pub ty: &'a hir::Ty<'hir>, } -pub struct BuiltinTypeAliasGenericBoundsSuggestion { - pub suggestions: Vec<(Span, String)>, -} - -impl Subdiagnostic for BuiltinTypeAliasGenericBoundsSuggestion { +impl<'a, 'hir> Subdiagnostic for TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir> { fn add_to_diag_with>( self, diag: &mut Diag<'_, G>, _f: &F, ) { - diag.multipart_suggestion( - fluent::lint_suggestion, - self.suggestions, - Applicability::MachineApplicable, - ); + // We perform the walk in here instead of in `` to + // avoid doing throwaway work in case the lint ends up getting suppressed. + + use hir::intravisit::Visitor; + struct ProbeShorthandAssocTys<'a, 'b, G: EmissionGuarantee> { + diag: &'a mut Diag<'b, G>, + } + impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for ProbeShorthandAssocTys<'a, 'b, G> { + fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) { + // Look for "type-parameter shorthand-associated-types". I.e., paths of the + // form `T::Assoc` with `T` type param. These are reliant on trait bounds. + // Suggest fully qualifying them via `::Assoc`. + // + // Instead of attempting to figure out the necessary trait ref, just use a + // placeholder. Since we don't record type-dependent resolutions for non-body + // items like type aliases, we can't simply deduce the corresp. trait from + // the HIR path alone without rerunning parts of HIR ty lowering here + // (namely `probe_single_ty_param_bound_for_assoc_ty`) which is infeasible. + // + // (We could employ some simple heuristics but that's likely not worth it). + if let hir::QPath::TypeRelative(qself, _) = qpath + && let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = qself.kind + && let hir::def::Res::Def(hir::def::DefKind::TyParam, _) = path.res + { + self.diag.multipart_suggestion( + fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg, + vec![ + (qself.span.shrink_to_lo(), "<".into()), + (qself.span.shrink_to_hi(), " as /* Trait */>".into()), + ], + Applicability::HasPlaceholders, + ); + } + hir::intravisit::walk_qpath(self, qpath, id) + } + } + ProbeShorthandAssocTys { diag }.visit_ty(self.ty); } } diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.rs b/tests/ui/associated-inherent-types/type-alias-bounds.rs index c27bcbed43a..61641a83994 100644 --- a/tests/ui/associated-inherent-types/type-alias-bounds.rs +++ b/tests/ui/associated-inherent-types/type-alias-bounds.rs @@ -19,7 +19,7 @@ // automatically lead to full wfchecking and lint TAB getting suppressed. pub type Alias = (Source::Assoc,); -//~^ WARN bounds on generic parameters are not enforced in type aliases +//~^ WARN bounds on generic parameters in type aliases are not enforced pub struct Source(T); pub trait Bound {} diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.stderr b/tests/ui/associated-inherent-types/type-alias-bounds.stderr index c5d0e9e5041..c56dd498f77 100644 --- a/tests/ui/associated-inherent-types/type-alias-bounds.stderr +++ b/tests/ui/associated-inherent-types/type-alias-bounds.stderr @@ -1,15 +1,16 @@ -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias-bounds.rs:21:19 | LL | pub type Alias = (Source::Assoc,); - | ^^^^^ + | --^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - pub type Alias = (Source::Assoc,); -LL + pub type Alias = (Source::Assoc,); - | warning: 1 warning emitted diff --git a/tests/ui/associated-type-bounds/type-alias.stderr b/tests/ui/associated-type-bounds/type-alias.stderr index 072c471467c..d59952b4a14 100644 --- a/tests/ui/associated-type-bounds/type-alias.stderr +++ b/tests/ui/associated-type-bounds/type-alias.stderr @@ -1,147 +1,159 @@ -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:3:25 | LL | type _TaWhere1 where T: Iterator = T; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | + | ------^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere1 where T: Iterator = T; -LL + type _TaWhere1 = T; - | -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:4:25 | LL | type _TaWhere2 where T: Iterator = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere2 where T: Iterator = T; -LL + type _TaWhere2 = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:5:25 | LL | type _TaWhere3 where T: Iterator = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere3 where T: Iterator = T; -LL + type _TaWhere3 = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:6:25 | LL | type _TaWhere4 where T: Iterator = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere4 where T: Iterator = T; -LL + type _TaWhere4 = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:7:25 | LL | type _TaWhere5 where T: Iterator Into<&'a u8>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere5 where T: Iterator Into<&'a u8>> = T; -LL + type _TaWhere5 = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:8:25 | LL | type _TaWhere6 where T: Iterator> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere6 where T: Iterator> = T; -LL + type _TaWhere6 = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:10:20 | LL | type _TaInline1> = T; - | ^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline1> = T; -LL + type _TaInline1 = T; - | + | --^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:11:20 | LL | type _TaInline2> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline2> = T; -LL + type _TaInline2 = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:12:20 | LL | type _TaInline3> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline3> = T; -LL + type _TaInline3 = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:13:20 | LL | type _TaInline4> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline4> = T; -LL + type _TaInline4 = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:14:20 | LL | type _TaInline5 Into<&'a u8>>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline5 Into<&'a u8>>> = T; -LL + type _TaInline5 = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:15:20 | LL | type _TaInline6>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline6>> = T; -LL + type _TaInline6 = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics warning: 12 warnings emitted diff --git a/tests/ui/privacy/private-in-public-warn.rs b/tests/ui/privacy/private-in-public-warn.rs index 99d318e36be..746b98fbd07 100644 --- a/tests/ui/privacy/private-in-public-warn.rs +++ b/tests/ui/privacy/private-in-public-warn.rs @@ -39,7 +39,7 @@ mod traits { pub trait PubTr {} pub type Alias = T; //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Alias` - //~^ WARNING bounds on generic parameters are not enforced in type aliases + //~^ WARNING bounds on generic parameters in type aliases are not enforced pub trait Tr1: PrivTr {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr1` pub trait Tr2 {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr2` pub trait Tr3 { @@ -58,7 +58,7 @@ mod traits_where { pub type Alias where T: PrivTr = T; //~^ ERROR trait `traits_where::PrivTr` is more private than the item `traits_where::Alias` - //~| WARNING where clauses are not enforced in type aliases + //~| WARNING where clauses on type aliases are not enforced pub trait Tr2 where T: PrivTr {} //~^ ERROR trait `traits_where::PrivTr` is more private than the item `traits_where::Tr2` pub trait Tr3 { diff --git a/tests/ui/privacy/private-in-public-warn.stderr b/tests/ui/privacy/private-in-public-warn.stderr index ac7e5547de9..3f7b8c281e7 100644 --- a/tests/ui/privacy/private-in-public-warn.stderr +++ b/tests/ui/privacy/private-in-public-warn.stderr @@ -395,30 +395,32 @@ note: but type `Priv2` is only usable at visibility `pub(self)` LL | struct Priv2; | ^^^^^^^^^^^^ -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/private-in-public-warn.rs:41:23 | LL | pub type Alias = T; - | ^^^^^^ - | + | --^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - pub type Alias = T; -LL + pub type Alias = T; - | -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/private-in-public-warn.rs:59:29 | LL | pub type Alias where T: PrivTr = T; - | ^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - pub type Alias where T: PrivTr = T; -LL + pub type Alias = T; - | + | ------^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics error: aborting due to 34 previous errors; 2 warnings emitted diff --git a/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr b/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr index d66e468873b..0eae68bfcf0 100644 --- a/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr +++ b/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr @@ -24,18 +24,19 @@ warning: trait bound i32: Foo does not depend on any type or lifetime parameters LL | union U where i32: Foo { f: i32 } | ^^^ -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/trivial-bounds-inconsistent.rs:22:14 | LL | type Y where i32: Foo = (); - | ^^^^^^^^ - | + | ------^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type Y where i32: Foo = (); -LL + type Y = (); - | warning: trait bound i32: Foo does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent.rs:22:19 diff --git a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs index 5ee3c027f40..52e0887175d 100644 --- a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs +++ b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs @@ -3,6 +3,6 @@ //@ check-pass pub type T = P; -//~^ WARN bounds on generic parameters are not enforced in type aliases +//~^ WARN bounds on generic parameters in type aliases are not enforced fn main() {} diff --git a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr index 125ffbbb417..9fd0fe4913b 100644 --- a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr +++ b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr @@ -1,15 +1,16 @@ -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/issue-67690-type-alias-bound-diagnostic-crash.rs:5:15 | LL | pub type T = P; - | ^^^^ ^^^^ ^^^^ + | --^^^^---^^^^---^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - pub type T = P; -LL + pub type T

= P; - | warning: 1 warning emitted diff --git a/tests/ui/type/type-alias-bounds.rs b/tests/ui/type/type-alias-bounds.rs index 6d63c0c7e1b..37c073fe1f9 100644 --- a/tests/ui/type/type-alias-bounds.rs +++ b/tests/ui/type/type-alias-bounds.rs @@ -6,15 +6,15 @@ use std::rc::Rc; type SVec = Vec; -//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] +//~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds] type S2Vec where T: Send = Vec; -//~^ WARN where clauses are not enforced in type aliases [type_alias_bounds] +//~^ WARN where clauses on type aliases are not enforced [type_alias_bounds] type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>); -//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] +//~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds] type WVec<'b, T: 'b + 'b> = (&'b u32, Vec); -//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] +//~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds] type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec); -//~^ WARN where clauses are not enforced in type aliases [type_alias_bounds] +//~^ WARN where clauses on type aliases are not enforced [type_alias_bounds] static STATIC: u32 = 0; @@ -42,10 +42,11 @@ fn foo<'a>(y: &'a i32) { struct Sendable(T); type MySendable = Sendable; // no error here! -// However, bounds *are* taken into account when accessing associated types +// Bounds on type params do enable shorthand type alias paths. +// However, that doesn't actually mean that they are properly enforced. trait Bound { type Assoc; } -type T1 = U::Assoc; //~ WARN not enforced in type aliases -type T2 where U: Bound = U::Assoc; //~ WARN not enforced in type aliases +type T1 = U::Assoc; //~ WARN are not enforced +type T2 where U: Bound = U::Assoc; //~ WARN are not enforced // This errors: // `type T3 = U::Assoc;` @@ -53,7 +54,7 @@ type T2 where U: Bound = U::Assoc; //~ WARN not enforced in type aliases type T4 = ::Assoc; // Make sure the help about associated types is not shown incorrectly -type T5 = ::Assoc; //~ WARN not enforced in type aliases -type T6 = ::std::vec::Vec; //~ WARN not enforced in type aliases +type T5 = ::Assoc; //~ WARN are not enforced +type T6 = ::std::vec::Vec; //~ WARN are not enforced fn main() {} diff --git a/tests/ui/type/type-alias-bounds.stderr b/tests/ui/type/type-alias-bounds.stderr index 92e573393c9..15c00901066 100644 --- a/tests/ui/type/type-alias-bounds.stderr +++ b/tests/ui/type/type-alias-bounds.stderr @@ -1,121 +1,132 @@ -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias-bounds.rs:8:14 | LL | type SVec = Vec; - | ^^^^ ^^^^ - | + | --^^^^---^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type SVec = Vec; -LL + type SVec = Vec; - | -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias-bounds.rs:10:21 | LL | type S2Vec where T: Send = Vec; - | ^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type S2Vec where T: Send = Vec; -LL + type S2Vec = Vec; - | + | ------^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias-bounds.rs:12:19 | LL | type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>); - | ^^ ^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>); -LL + type VVec<'b, 'a> = (&'b u32, Vec<&'a i32>); - | + | --^^---^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias-bounds.rs:14:18 | LL | type WVec<'b, T: 'b + 'b> = (&'b u32, Vec); - | ^^ ^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type WVec<'b, T: 'b + 'b> = (&'b u32, Vec); -LL + type WVec<'b, T> = (&'b u32, Vec); - | + | --^^---^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias-bounds.rs:16:25 | LL | type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec); - | ^^^^^ ^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec); -LL + type W2Vec<'b, T> = (&'b u32, Vec); - | + | ------^^^^^--^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:47:12 +warning: bounds on generic parameters in type aliases are not enforced + --> $DIR/type-alias-bounds.rs:48:12 | LL | type T1 = U::Assoc; - | ^^^^^ + | ^^^^^ will not be checked at usage sites of the type alias | -help: use fully disambiguated paths (i.e., `::Assoc`) to refer to associated types in type aliases - --> $DIR/type-alias-bounds.rs:47:21 - | -LL | type T1 = U::Assoc; - | ^^^^^^^^ -help: the bound will not be checked when the type alias is used, and should be removed + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics +help: remove this bound | LL - type T1 = U::Assoc; LL + type T1 = U::Assoc; | +help: fully qualify this associated type + | +LL | type T1 = ::Assoc; + | + +++++++++++++++ -warning: where clauses are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:48:18 +warning: where clauses on type aliases are not enforced + --> $DIR/type-alias-bounds.rs:49:18 | LL | type T2 where U: Bound = U::Assoc; - | ^^^^^^^^ + | ^^^^^^^^ will not be checked at usage sites of the type alias | -help: use fully disambiguated paths (i.e., `::Assoc`) to refer to associated types in type aliases - --> $DIR/type-alias-bounds.rs:48:29 - | -LL | type T2 where U: Bound = U::Assoc; - | ^^^^^^^^ -help: the clause will not be checked when the type alias is used, and should be removed + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics +help: remove this where clause | LL - type T2 where U: Bound = U::Assoc; LL + type T2 = U::Assoc; | +help: fully qualify this associated type + | +LL | type T2 where U: Bound = ::Assoc; + | + +++++++++++++++ -warning: bounds on generic parameters are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:56:12 +warning: bounds on generic parameters in type aliases are not enforced + --> $DIR/type-alias-bounds.rs:57:12 | LL | type T5 = ::Assoc; - | ^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type T5 = ::Assoc; -LL + type T5 = ::Assoc; - | + | --^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:57:12 +warning: bounds on generic parameters in type aliases are not enforced + --> $DIR/type-alias-bounds.rs:58:12 | LL | type T6 = ::std::vec::Vec; - | ^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type T6 = ::std::vec::Vec; -LL + type T6 = ::std::vec::Vec; - | + | --^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics warning: 9 warnings emitted -- cgit 1.4.1-3-g733a5 From d67b61637e96e6e72b5a676b2f02d1a01b3a94a3 Mon Sep 17 00:00:00 2001 From: León Orell Valerian Liehr Date: Sun, 16 Jun 2024 12:22:12 +0200 Subject: Make lint type_alias_bounds's removal sugg maybe-incorrect if the RHS contains shorthand assoc tys --- compiler/rustc_lint/src/builtin.rs | 91 ++++++++++++----------- compiler/rustc_lint/src/lints.rs | 147 +++++++++++++++---------------------- 2 files changed, 109 insertions(+), 129 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index a8c8c71927a..4e1e19e522e 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -32,11 +32,10 @@ use crate::{ BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds, - BuiltinTypeAliasParamBoundsSuggestion, BuiltinUngatedAsyncFnTrackCaller, - BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, - BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, - BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, - TypeAliasBoundsQualifyAssocTysSugg, + BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, + BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, + BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, + BuiltinWhileTrue, InvalidAsmLabel, }, EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, }; @@ -1406,9 +1405,23 @@ declare_lint_pass!( TypeAliasBounds => [TYPE_ALIAS_BOUNDS] ); +impl TypeAliasBounds { + pub(crate) fn affects_object_lifetime_defaults(pred: &hir::WherePredicate<'_>) -> bool { + // Bounds of the form `T: 'a` with `T` type param affect object lifetime defaults. + if let hir::WherePredicate::BoundPredicate(pred) = pred + && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_))) + && pred.bound_generic_params.is_empty() // indeed, even if absent from the RHS + && pred.bounded_ty.as_generic_param().is_some() + { + return true; + } + false + } +} + impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - let hir::ItemKind::TyAlias(hir_ty, generics) = &item.kind else { return }; + let hir::ItemKind::TyAlias(hir_ty, generics) = item.kind else { return }; // There must not be a where clause. if generics.predicates.is_empty() { @@ -1437,7 +1450,6 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { let mut where_spans = Vec::new(); let mut inline_spans = Vec::new(); let mut inline_sugg = Vec::new(); - let mut affects_object_lifetime_defaults = false; for p in generics.predicates { let span = p.span(); @@ -1449,45 +1461,22 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { } inline_sugg.push((span, String::new())); } - - // FIXME(fmease): Move this into a "diagnostic decorator" for increased laziness - // Bounds of the form `T: 'a` where `T` is a type param of - // the type alias affect object lifetime defaults. - if !affects_object_lifetime_defaults - && let hir::WherePredicate::BoundPredicate(pred) = p - && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_))) - && pred.bound_generic_params.is_empty() - && let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = pred.bounded_ty.kind - && let Res::Def(DefKind::TyParam, _) = path.res - { - affects_object_lifetime_defaults = true; - } } - // FIXME(fmease): Add a disclaimer (in the form of a multi-span note) that the removal of - // type-param-outlives-bounds affects OLDs and explicit object lifetime - // bounds might be required [...]. - // FIXME(fmease): The applicability should also depend on the outcome of the HIR walker - // inside of `TypeAliasBoundsQualifyAssocTysSugg`: Whether it found a - // shorthand projection or not. - let applicability = if affects_object_lifetime_defaults { - Applicability::MaybeIncorrect - } else { - Applicability::MachineApplicable - }; - - let mut qualify_assoc_tys_sugg = Some(TypeAliasBoundsQualifyAssocTysSugg { ty: hir_ty }); - let enable_feat_help = cx.tcx.sess.is_nightly_build().then_some(()); + let mut ty = Some(hir_ty); + let enable_feat_help = cx.tcx.sess.is_nightly_build(); if let [.., label_sp] = *where_spans { cx.emit_span_lint( TYPE_ALIAS_BOUNDS, where_spans, - BuiltinTypeAliasBounds::WhereClause { + BuiltinTypeAliasBounds { + in_where_clause: true, label: label_sp, enable_feat_help, - suggestion: (generics.where_clause_span, applicability), - qualify_assoc_tys_sugg: qualify_assoc_tys_sugg.take(), + suggestions: vec![(generics.where_clause_span, String::new())], + preds: generics.predicates, + ty: ty.take(), }, ); } @@ -1495,20 +1484,36 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { cx.emit_span_lint( TYPE_ALIAS_BOUNDS, inline_spans, - BuiltinTypeAliasBounds::ParamBounds { + BuiltinTypeAliasBounds { + in_where_clause: false, label: label_sp, enable_feat_help, - suggestion: BuiltinTypeAliasParamBoundsSuggestion { - suggestions: inline_sugg, - applicability, - }, - qualify_assoc_tys_sugg, + suggestions: inline_sugg, + preds: generics.predicates, + ty, }, ); } } } +pub(crate) struct ShorthandAssocTyCollector { + pub(crate) qselves: Vec, +} + +impl hir::intravisit::Visitor<'_> for ShorthandAssocTyCollector { + fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) { + // Look for "type-parameter shorthand-associated-types". I.e., paths of the + // form `T::Assoc` with `T` type param. These are reliant on trait bounds. + if let hir::QPath::TypeRelative(qself, _) = qpath + && qself.as_generic_param().is_some() + { + self.qselves.push(qself.span); + } + hir::intravisit::walk_qpath(self, qpath, id) + } +} + declare_lint! { /// The `trivial_bounds` lint detects trait bounds that don't depend on /// any type parameters. diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 6cf8b9330fc..b833d56c9bc 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2,8 +2,10 @@ #![allow(rustc::untranslatable_diagnostic)] use std::num::NonZero; -use crate::errors::RequestedLevel; +use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; +use crate::errors::{OverruledAttributeSub, RequestedLevel}; use crate::fluent_generated as fluent; +use crate::LateContext; use rustc_errors::{ codes::*, Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp, @@ -22,8 +24,6 @@ use rustc_span::{ Span, Symbol, }; -use crate::{builtin::InitError, errors::OverruledAttributeSub, LateContext}; - // array_into_iter.rs #[derive(LintDiagnostic)] #[diag(lint_shadowed_into_iter)] @@ -268,97 +268,72 @@ pub struct MacroExprFragment2024 { pub suggestion: Span, } -#[derive(LintDiagnostic)] -pub enum BuiltinTypeAliasBounds<'a, 'hir> { - #[diag(lint_builtin_type_alias_bounds_where_clause)] - #[note(lint_builtin_type_alias_bounds_limitation_note)] - WhereClause { - #[label(lint_builtin_type_alias_bounds_label)] - label: Span, - #[help(lint_builtin_type_alias_bounds_enable_feat_help)] - enable_feat_help: Option<()>, - #[suggestion(code = "")] - suggestion: (Span, Applicability), - #[subdiagnostic] - qualify_assoc_tys_sugg: Option>, - }, - #[diag(lint_builtin_type_alias_bounds_param_bounds)] - #[note(lint_builtin_type_alias_bounds_limitation_note)] - ParamBounds { - #[label(lint_builtin_type_alias_bounds_label)] - label: Span, - #[help(lint_builtin_type_alias_bounds_enable_feat_help)] - enable_feat_help: Option<()>, - #[subdiagnostic] - suggestion: BuiltinTypeAliasParamBoundsSuggestion, - #[subdiagnostic] - qualify_assoc_tys_sugg: Option>, - }, -} - -pub struct BuiltinTypeAliasParamBoundsSuggestion { +pub struct BuiltinTypeAliasBounds<'a, 'hir> { + pub in_where_clause: bool, + pub label: Span, + pub enable_feat_help: bool, pub suggestions: Vec<(Span, String)>, - pub applicability: Applicability, + pub preds: &'hir [hir::WherePredicate<'hir>], + pub ty: Option<&'a hir::Ty<'hir>>, } -impl Subdiagnostic for BuiltinTypeAliasParamBoundsSuggestion { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - diag.arg("count", self.suggestions.len()); - diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, self.applicability); - } -} - -pub struct TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir> { - pub ty: &'a hir::Ty<'hir>, -} +impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_, '_> { + fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.primary_message(if self.in_where_clause { + fluent::lint_builtin_type_alias_bounds_where_clause + } else { + fluent::lint_builtin_type_alias_bounds_param_bounds + }); + diag.span_label(self.label, fluent::lint_builtin_type_alias_bounds_label); + diag.note(fluent::lint_builtin_type_alias_bounds_limitation_note); + if self.enable_feat_help { + diag.help(fluent::lint_builtin_type_alias_bounds_enable_feat_help); + } -impl<'a, 'hir> Subdiagnostic for TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir> { - fn add_to_diag_with>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { // We perform the walk in here instead of in `` to // avoid doing throwaway work in case the lint ends up getting suppressed. - - use hir::intravisit::Visitor; - struct ProbeShorthandAssocTys<'a, 'b, G: EmissionGuarantee> { - diag: &'a mut Diag<'b, G>, + let mut collector = ShorthandAssocTyCollector { qselves: Vec::new() }; + if let Some(ty) = self.ty { + hir::intravisit::Visitor::visit_ty(&mut collector, ty); } - impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for ProbeShorthandAssocTys<'a, 'b, G> { - fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) { - // Look for "type-parameter shorthand-associated-types". I.e., paths of the - // form `T::Assoc` with `T` type param. These are reliant on trait bounds. - // Suggest fully qualifying them via `::Assoc`. - // - // Instead of attempting to figure out the necessary trait ref, just use a - // placeholder. Since we don't record type-dependent resolutions for non-body - // items like type aliases, we can't simply deduce the corresp. trait from - // the HIR path alone without rerunning parts of HIR ty lowering here - // (namely `probe_single_ty_param_bound_for_assoc_ty`) which is infeasible. - // - // (We could employ some simple heuristics but that's likely not worth it). - if let hir::QPath::TypeRelative(qself, _) = qpath - && let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = qself.kind - && let hir::def::Res::Def(hir::def::DefKind::TyParam, _) = path.res - { - self.diag.multipart_suggestion( - fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg, - vec![ - (qself.span.shrink_to_lo(), "<".into()), - (qself.span.shrink_to_hi(), " as /* Trait */>".into()), - ], - Applicability::HasPlaceholders, - ); - } - hir::intravisit::walk_qpath(self, qpath, id) - } + + let affect_object_lifetime_defaults = self + .preds + .iter() + .filter(|pred| pred.in_where_clause() == self.in_where_clause) + .any(|pred| TypeAliasBounds::affects_object_lifetime_defaults(pred)); + + // If there are any shorthand assoc tys, then the bounds can't be removed automatically. + // The user first needs to fully qualify the assoc tys. + let applicability = if !collector.qselves.is_empty() || affect_object_lifetime_defaults { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + + diag.arg("count", self.suggestions.len()); + diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, applicability); + + // Suggest fully qualifying paths of the form `T::Assoc` with `T` type param via + // `::Assoc` to remove their reliance on any type param bounds. + // + // Instead of attempting to figure out the necessary trait ref, just use a + // placeholder. Since we don't record type-dependent resolutions for non-body + // items like type aliases, we can't simply deduce the corresp. trait from + // the HIR path alone without rerunning parts of HIR ty lowering here + // (namely `probe_single_ty_param_bound_for_assoc_ty`) which is infeasible. + // + // (We could employ some simple heuristics but that's likely not worth it). + for qself in collector.qselves { + diag.multipart_suggestion( + fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg, + vec![ + (qself.shrink_to_lo(), "<".into()), + (qself.shrink_to_hi(), " as /* Trait */>".into()), + ], + Applicability::HasPlaceholders, + ); } - ProbeShorthandAssocTys { diag }.visit_ty(self.ty); } } -- cgit 1.4.1-3-g733a5 From 5859dff7429436498aed7fc7efed88a73b3eaf4d Mon Sep 17 00:00:00 2001 From: León Orell Valerian Liehr Date: Sun, 16 Jun 2024 15:53:01 +0200 Subject: Update the description of lint type_alias_bounds --- compiler/rustc_lint/src/builtin.rs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 4e1e19e522e..ab0b47d48e5 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1390,20 +1390,32 @@ declare_lint! { /// /// ### Explanation /// - /// The trait bounds in a type alias are currently ignored, and should not - /// be included to avoid confusion. This was previously allowed - /// unintentionally; this may become a hard error in the future. + /// Trait and lifetime bounds on generic parameters and in where clauses of + /// type aliases are not checked at usage sites of the type alias. Moreover, + /// they are not thoroughly checked for correctness at their definition site + /// either similar to the aliased type. + /// + /// This is a known limitation of the type checker that may be lifted in a + /// future edition. Permitting such bounds in light of this was unintentional. + /// + /// While these bounds may have secondary effects such as enabling the use of + /// "shorthand" associated type paths[^1] and affecting the default trait + /// object lifetime[^2] of trait object types passed to the type alias, this + /// should not have been allowed until the aforementioned restrictions of the + /// type checker have been lifted. + /// + /// Using such bounds is highly discouraged as they are actively misleading. + /// + /// [^1]: I.e., paths of the form `T::Assoc` where `T` is a type parameter + /// bounded by trait `Trait` which defines an associated type called `Assoc` + /// as opposed to a fully qualified path of the form `::Assoc`. + /// [^2]: TYPE_ALIAS_BOUNDS, Warn, "bounds in type aliases are not enforced" } -declare_lint_pass!( - /// Lint for trait and lifetime bounds in type aliases being mostly ignored. - /// They are relevant when using associated types, but otherwise neither checked - /// at definition site nor enforced at use site. - TypeAliasBounds => [TYPE_ALIAS_BOUNDS] -); +declare_lint_pass!(TypeAliasBounds => [TYPE_ALIAS_BOUNDS]); impl TypeAliasBounds { pub(crate) fn affects_object_lifetime_defaults(pred: &hir::WherePredicate<'_>) -> bool { -- cgit 1.4.1-3-g733a5 From 2a73553513f393ab00e2da217ff0082403f1985b Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Fri, 23 Feb 2024 16:39:57 +0300 Subject: Support ?Trait bounds in supertraits and dyn Trait under a feature gate --- compiler/rustc_ast_lowering/src/item.rs | 10 +++- compiler/rustc_ast_lowering/src/lib.rs | 28 ++++++------ compiler/rustc_ast_passes/src/ast_validation.rs | 28 +++++++++--- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir/src/hir.rs | 6 ++- compiler/rustc_hir/src/intravisit.rs | 4 +- compiler/rustc_hir_analysis/src/check/wfcheck.rs | 2 +- .../src/collect/resolve_bound_vars.rs | 2 +- .../src/hir_ty_lowering/bounds.rs | 16 +++++-- .../src/hir_ty_lowering/errors.rs | 8 ++-- .../rustc_hir_analysis/src/hir_ty_lowering/lint.rs | 4 +- .../src/hir_ty_lowering/object_safety.rs | 9 ++-- compiler/rustc_hir_pretty/src/lib.rs | 5 +- compiler/rustc_lint/src/non_local_def.rs | 4 +- compiler/rustc_lint/src/traits.rs | 6 ++- compiler/rustc_span/src/symbol.rs | 1 + .../infer/nice_region_error/find_anon_type.rs | 2 +- .../infer/nice_region_error/static_impl_trait.rs | 2 +- .../src/error_reporting/traits/mod.rs | 2 +- .../src/error_reporting/traits/suggestions.rs | 4 +- src/librustdoc/clean/mod.rs | 2 +- src/tools/clippy/clippy_lints/src/lifetimes.rs | 2 +- src/tools/clippy/clippy_lints/src/trait_bounds.rs | 6 +-- .../clippy/clippy_lints/src/types/borrowed_box.rs | 19 ++++---- .../clippy_lints/src/types/type_complexity.rs | 1 + .../feature-gate-more-maybe-bounds.rs | 17 +++++++ .../feature-gate-more-maybe-bounds.stderr | 53 ++++++++++++++++++++++ tests/ui/maybe-bounds.stderr | 15 ++++-- tests/ui/parser/trait-object-trait-parens.stderr | 18 ++++++-- tests/ui/traits/maybe-polarity-pass.rs | 27 +++++++++++ tests/ui/traits/maybe-polarity-pass.stderr | 20 ++++++++ tests/ui/traits/wf-object/maybe-bound.stderr | 26 +++++++++-- tests/ui/traits/wf-object/only-maybe-bound.stderr | 8 +++- tests/ui/unsized/maybe-bounds-where.stderr | 31 ++++++++++--- 34 files changed, 305 insertions(+), 85 deletions(-) create mode 100644 tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs create mode 100644 tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr create mode 100644 tests/ui/traits/maybe-polarity-pass.rs create mode 100644 tests/ui/traits/maybe-polarity-pass.stderr (limited to 'compiler') diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index f990b4ba69b..68745056438 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1525,8 +1525,14 @@ impl<'hir> LoweringContext<'_, 'hir> { continue; } let is_param = *is_param.get_or_insert_with(compute_is_param); - if !is_param { - self.dcx().emit_err(MisplacedRelaxTraitBound { span: bound.span() }); + if !is_param && !self.tcx.features().more_maybe_bounds { + self.tcx + .sess + .create_feature_err( + MisplacedRelaxTraitBound { span: bound.span() }, + sym::more_maybe_bounds, + ) + .emit(); } } } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0f5f4d8023b..218493cb7ea 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1216,6 +1216,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { itctx, TraitBoundModifiers::NONE, ); + let bound = (bound, hir::TraitBoundModifier::None); let bounds = this.arena.alloc_from_iter([bound]); let lifetime_bound = this.elided_dyn_bound(t.span); (bounds, lifetime_bound) @@ -1348,21 +1349,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { // We can safely ignore constness here since AST validation // takes care of rejecting invalid modifier combinations and // const trait bounds in trait object types. - GenericBound::Trait(ty, modifiers) => match modifiers.polarity { - BoundPolarity::Positive | BoundPolarity::Negative(_) => { - Some(this.lower_poly_trait_ref( - ty, - itctx, - // Still, don't pass along the constness here; we don't want to - // synthesize any host effect args, it'd only cause problems. - TraitBoundModifiers { - constness: BoundConstness::Never, - ..*modifiers - }, - )) - } - BoundPolarity::Maybe(_) => None, - }, + GenericBound::Trait(ty, modifiers) => { + // Still, don't pass along the constness here; we don't want to + // synthesize any host effect args, it'd only cause problems. + let modifiers = TraitBoundModifiers { + constness: BoundConstness::Never, + ..*modifiers + }; + let trait_ref = this.lower_poly_trait_ref(ty, itctx, modifiers); + let polarity = this.lower_trait_bound_modifiers(modifiers); + Some((trait_ref, polarity)) + } GenericBound::Outlives(lifetime) => { if lifetime_bound.is_none() { lifetime_bound = Some(this.lower_lifetime(lifetime)); @@ -2688,6 +2685,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { trait_ref: hir::TraitRef { path, hir_ref_id: hir_id }, span: self.lower_span(span), }; + let principal = (principal, hir::TraitBoundModifier::None); // The original ID is taken by the `PolyTraitRef`, // so the `Ty` itself needs a different one. diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 1088db74cc9..9b063a330b7 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1345,14 +1345,28 @@ impl<'a> Visitor<'a> for AstValidator<'a> { match bound { GenericBound::Trait(trait_ref, modifiers) => { match (ctxt, modifiers.constness, modifiers.polarity) { - (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => { - self.dcx().emit_err(errors::OptionalTraitSupertrait { - span: trait_ref.span, - path_str: pprust::path_to_string(&trait_ref.trait_ref.path), - }); + (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) + if !self.features.more_maybe_bounds => + { + self.session + .create_feature_err( + errors::OptionalTraitSupertrait { + span: trait_ref.span, + path_str: pprust::path_to_string(&trait_ref.trait_ref.path), + }, + sym::more_maybe_bounds, + ) + .emit(); } - (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => { - self.dcx().emit_err(errors::OptionalTraitObject { span: trait_ref.span }); + (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) + if !self.features.more_maybe_bounds => + { + self.session + .create_feature_err( + errors::OptionalTraitObject { span: trait_ref.span }, + sym::more_maybe_bounds, + ) + .emit(); } ( BoundKind::TraitObject, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 9b5ed3b0876..d3d07181096 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -205,6 +205,8 @@ declare_features! ( (unstable, lifetime_capture_rules_2024, "1.76.0", None), /// Allows `#[link(..., cfg(..))]`; perma-unstable per #37406 (unstable, link_cfg, "1.14.0", None), + /// Allows using `?Trait` trait bounds in more contexts. + (internal, more_maybe_bounds, "CURRENT_RUSTC_VERSION", None), /// Allows the `multiple_supertrait_upcastable` lint. (unstable, multiple_supertrait_upcastable, "1.69.0", None), /// Allow negative trait bounds. This is an internal-only feature for testing the trait solver! diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 20b15499234..8c8f760bc41 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2832,7 +2832,11 @@ pub enum TyKind<'hir> { OpaqueDef(ItemId, &'hir [GenericArg<'hir>], bool), /// A trait object type `Bound1 + Bound2 + Bound3` /// where `Bound` is a trait or a lifetime. - TraitObject(&'hir [PolyTraitRef<'hir>], &'hir Lifetime, TraitObjectSyntax), + TraitObject( + &'hir [(PolyTraitRef<'hir>, TraitBoundModifier)], + &'hir Lifetime, + TraitObjectSyntax, + ), /// Unused for now. Typeof(&'hir AnonConst), /// `TyKind::Infer` means the type should be inferred instead of it having been diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index c202ee41e31..696f548f1ba 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -902,7 +902,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) -> V::Resul try_visit!(visitor.visit_array_length(length)); } TyKind::TraitObject(bounds, ref lifetime, _syntax) => { - walk_list!(visitor, visit_poly_trait_ref, bounds); + for (bound, _modifier) in bounds { + try_visit!(visitor.visit_poly_trait_ref(bound)); + } try_visit!(visitor.visit_lifetime(lifetime)); } TyKind::Typeof(ref expression) => try_visit!(visitor.visit_anon_const(expression)), diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 0316ef69bf8..456dc4e4e07 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -831,7 +831,7 @@ impl<'tcx> TypeVisitor> for GATArgsCollector<'tcx> { fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool { match ty.kind { - hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments { + hir::TyKind::TraitObject([(trait_ref, _)], ..) => match trait_ref.trait_ref.path.segments { [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()), _ => false, }, diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 349dc9ad00e..02ea95852f0 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -652,7 +652,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { debug!(?bounds, ?lifetime, "TraitObject"); let scope = Scope::TraitRefBoundary { s: self.scope }; self.with(scope, |this| { - for bound in bounds { + for (bound, _) in bounds { this.visit_poly_trait_ref_inner( bound, NonLifetimeBinderAllowed::Deny("trait object types"), diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 6f9c481650b..f05884f8912 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -9,7 +9,7 @@ use rustc_middle::bug; use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; use rustc_span::symbol::Ident; -use rustc_span::{ErrorGuaranteed, Span, Symbol}; +use rustc_span::{sym, ErrorGuaranteed, Span, Symbol}; use rustc_trait_selection::traits; use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor}; use smallvec::SmallVec; @@ -75,10 +75,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - if unbounds.len() > 1 { - self.dcx().emit_err(errors::MultipleRelaxedDefaultBounds { - spans: unbounds.iter().map(|ptr| ptr.span).collect(), - }); + if unbounds.len() > 1 && !tcx.features().more_maybe_bounds { + self.tcx() + .sess + .create_feature_err( + errors::MultipleRelaxedDefaultBounds { + spans: unbounds.iter().map(|ptr| ptr.span).collect(), + }, + sym::more_maybe_bounds, + ) + .emit(); } let mut seen_sized_unbound = false; 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 8ff6ced8b39..e9ec2466089 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -698,7 +698,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, associated_types: FxIndexMap>, potential_assoc_types: Vec, - trait_bounds: &[hir::PolyTraitRef<'_>], + trait_bounds: &[(hir::PolyTraitRef<'_>, hir::TraitBoundModifier)], ) { if associated_types.values().all(|v| v.is_empty()) { return; @@ -744,12 +744,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // related to issue #91997, turbofishes added only when in an expr or pat let mut in_expr_or_pat = false; if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { - let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id)); + let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.0.trait_ref.hir_ref_id)); in_expr_or_pat = match grandparent { Node::Expr(_) | Node::Pat(_) => true, _ => false, }; - match bound.trait_ref.path.segments { + match bound.0.trait_ref.path.segments { // FIXME: `trait_ref.path.span` can point to a full path with multiple // segments, even though `trait_ref.path.segments` is of length `1`. Work // around that bug here, even though it should be fixed elsewhere. @@ -790,7 +790,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // and we can then use their span to indicate this to the user. let bound_names = trait_bounds .iter() - .filter_map(|poly_trait_ref| { + .filter_map(|(poly_trait_ref, _)| { let path = poly_trait_ref.trait_ref.path.segments.last()?; let args = path.args?; 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 29c71c3fa50..e7aad0a29c5 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -34,7 +34,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .ok() .is_some_and(|s| s.trim_end().ends_with('<')); - let is_global = poly_trait_ref.trait_ref.path.is_global(); + let is_global = poly_trait_ref.0.trait_ref.path.is_global(); let mut sugg = vec![( self_ty.span.shrink_to_lo(), @@ -176,7 +176,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut is_downgradable = true; let is_object_safe = match self_ty.kind { hir::TyKind::TraitObject(objects, ..) => { - objects.iter().all(|o| match o.trait_ref.path.res { + objects.iter().all(|(o, _)| match o.trait_ref.path.res { Res::Def(DefKind::Trait, id) => { if Some(id) == owner { // For recursive traits, don't downgrade the error. (#119652) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs index aafadc7f9cb..b3c7a1ff8a8 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs @@ -27,7 +27,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { &self, span: Span, hir_id: hir::HirId, - hir_trait_bounds: &[hir::PolyTraitRef<'tcx>], + hir_trait_bounds: &[(hir::PolyTraitRef<'tcx>, hir::TraitBoundModifier)], lifetime: &hir::Lifetime, borrowed: bool, representation: DynKind, @@ -37,7 +37,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let mut bounds = Bounds::default(); let mut potential_assoc_types = Vec::new(); let dummy_self = self.tcx().types.trait_object_dummy_self; - for trait_bound in hir_trait_bounds.iter().rev() { + for (trait_bound, modifier) in hir_trait_bounds.iter().rev() { + if *modifier == hir::TraitBoundModifier::Maybe { + continue; + } if let GenericArgCountResult { correct: Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }), @@ -249,7 +252,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let args = tcx.mk_args(&args); let span = i.bottom().1; - let empty_generic_args = hir_trait_bounds.iter().any(|hir_bound| { + let empty_generic_args = hir_trait_bounds.iter().any(|(hir_bound, _)| { hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id) && hir_bound.span.contains(span) }); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index db5eba0d9eb..0df9e063904 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -300,13 +300,16 @@ impl<'a> State<'a> { self.word_space("dyn"); } let mut first = true; - for bound in bounds { + for (bound, modifier) in bounds { if first { first = false; } else { self.nbsp(); self.word_space("+"); } + if *modifier == TraitBoundModifier::Maybe { + self.word("?"); + } self.print_poly_trait_ref(bound); } if !lifetime.is_elided() { diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 2f8eea6cd18..93c606a954e 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -429,7 +429,7 @@ fn ty_has_local_parent( path_has_local_parent(ty_path, cx, impl_parent, impl_parent_parent) } TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => path_has_local_parent( - principle_poly_trait_ref.trait_ref.path, + principle_poly_trait_ref.0.trait_ref.path, cx, impl_parent, impl_parent_parent, @@ -527,7 +527,7 @@ fn self_ty_kind_for_diagnostic(ty: &rustc_hir::Ty<'_>, tcx: TyCtxt<'_>) -> (Span .to_string(), ), TyKind::TraitObject([principle_poly_trait_ref, ..], _, _) => { - let path = &principle_poly_trait_ref.trait_ref.path; + let path = &principle_poly_trait_ref.0.trait_ref.path; ( path_span_without_args(path), path.res diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index 6983e7abbd6..552245f0cdd 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -113,9 +113,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx>) { let hir::TyKind::TraitObject(bounds, _lifetime, _syntax) = &ty.kind else { return }; - for bound in &bounds[..] { + for (bound, modifier) in &bounds[..] { let def_id = bound.trait_ref.trait_def_id(); - if cx.tcx.lang_items().drop_trait() == def_id { + if cx.tcx.lang_items().drop_trait() == def_id + && *modifier != hir::TraitBoundModifier::Maybe + { let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else { return }; cx.emit_span_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id }); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 2b30ca8a894..b40a7d17eef 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1229,6 +1229,7 @@ symbols! { modifiers, module, module_path, + more_maybe_bounds, more_qualified_paths, more_struct_aliases, movbe_target_feature, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs index b91b755d683..a892ce58861 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/find_anon_type.rs @@ -84,7 +84,7 @@ impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { } hir::TyKind::TraitObject(bounds, ..) => { - for bound in bounds { + for (bound, _) in bounds { self.current_index.shift_in(1); self.visit_poly_trait_ref(bound); self.current_index.shift_out(1); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs index ce157ff3dc8..e5d97976534 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/static_impl_trait.rs @@ -607,7 +607,7 @@ impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { _, ) = t.kind { - for ptr in poly_trait_refs { + for (ptr, _) in poly_trait_refs { if Some(self.1) == ptr.trait_ref.trait_def_id() { self.0.push(ptr.span); } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs index 87fdc5ddff8..89ae6f4ccab 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs @@ -437,7 +437,7 @@ pub fn report_object_safety_error<'tcx>( if tcx.parent_hir_node(hir_id).fn_sig().is_some() { // Do not suggest `impl Trait` when dealing with things like super-traits. err.span_suggestion_verbose( - ty.span.until(trait_ref.span), + ty.span.until(trait_ref.0.span), "consider using an opaque type instead", "impl ", Applicability::MaybeIncorrect, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 885216e6216..d1381d24764 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -3040,11 +3040,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { match ty.kind { hir::TyKind::TraitObject(traits, _, _) => { let (span, kw) = match traits { - [first, ..] if first.span.lo() == ty.span.lo() => { + [(first, _), ..] if first.span.lo() == ty.span.lo() => { // Missing `dyn` in front of trait object. (ty.span.shrink_to_lo(), "dyn ") } - [first, ..] => (ty.span.until(first.span), ""), + [(first, _), ..] => (ty.span.until(first.span), ""), [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"), }; let needs_parens = traits.len() != 1; diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2cd9b6fcd38..26011926cdd 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1858,7 +1858,7 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T } TyKind::Path(_) => clean_qpath(ty, cx), TyKind::TraitObject(bounds, ref lifetime, _) => { - let bounds = bounds.iter().map(|bound| clean_poly_trait_ref(bound, cx)).collect(); + let bounds = bounds.iter().map(|(bound, _)| clean_poly_trait_ref(bound, cx)).collect(); let lifetime = if !lifetime.is_elided() { Some(clean_lifetime(*lifetime, cx)) } else { None }; DynTrait(bounds, lifetime) diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 443d6189c1f..f83101cee11 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -545,7 +545,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { if !lt.is_elided() { self.unelided_trait_object_lifetime = true; } - for bound in bounds { + for (bound, _) in bounds { self.visit_poly_trait_ref(bound); } }, diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 07390d6f430..41ebf59ae3a 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -181,7 +181,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { // Iterate the bounds and add them to our seen hash // If we haven't yet seen it, add it to the fixed traits - for bound in bounds { + for (bound, _) in bounds { let Some(def_id) = bound.trait_ref.trait_def_id() else { continue; }; @@ -196,9 +196,9 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { // If the number of unique traits isn't the same as the number of traits in the bounds, // there must be 1 or more duplicates if bounds.len() != unique_traits.len() { - let mut bounds_span = bounds[0].span; + let mut bounds_span = bounds[0].0.span; - for bound in bounds.iter().skip(1) { + for (bound, _) in bounds.iter().skip(1) { bounds_span = bounds_span.to(bound.span); } diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs index 801e8862619..83cc9f2d8df 100644 --- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs @@ -81,14 +81,17 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m // Returns true if given type is `Any` trait. fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool { - if let TyKind::TraitObject(traits, ..) = t.kind - && !traits.is_empty() - && let Some(trait_did) = traits[0].trait_ref.trait_def_id() - // Only Send/Sync can be used as additional traits, so it is enough to - // check only the first trait. - && cx.tcx.is_diagnostic_item(sym::Any, trait_did) - { - return true; + if let TyKind::TraitObject(traits, ..) = t.kind { + return traits + .iter() + .any(|(bound, _)| { + if let Some(trait_did) = bound.trait_ref.trait_def_id() + && cx.tcx.is_diagnostic_item(sym::Any, trait_did) + { + return true; + } + false + }); } false diff --git a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs index b6e765d7c39..7fcfd5c8f35 100644 --- a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs @@ -55,6 +55,7 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor { TyKind::TraitObject(param_bounds, _, _) => { let has_lifetime_parameters = param_bounds.iter().any(|bound| { bound + .0 .bound_generic_params .iter() .any(|param| matches!(param.kind, GenericParamKind::Lifetime { .. })) diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs new file mode 100644 index 00000000000..4541499b7c7 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs @@ -0,0 +1,17 @@ +#![feature(auto_traits)] + +trait Trait1 {} +auto trait Trait2 {} +trait Trait3: ?Trait1 {} +//~^ ERROR `?Trait` is not permitted in supertraits +trait Trait4 where Self: ?Trait1 {} +//~^ ERROR ?Trait` bounds are only permitted at the point where a type parameter is declared + +fn foo(_: Box) {} +//~^ ERROR `?Trait` is not permitted in trait object types +fn bar(_: T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr new file mode 100644 index 00000000000..2b7a353020a --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr @@ -0,0 +1,53 @@ +error[E0658]: `?Trait` is not permitted in supertraits + --> $DIR/feature-gate-more-maybe-bounds.rs:5:15 + | +LL | trait Trait3: ?Trait1 {} + | ^^^^^^^ + | + = note: traits are `?Trait1` by default + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `?Trait` is not permitted in trait object types + --> $DIR/feature-gate-more-maybe-bounds.rs:10:28 + | +LL | fn foo(_: Box) {} + | ^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared + --> $DIR/feature-gate-more-maybe-bounds.rs:7:26 + | +LL | trait Trait4 where Self: ?Trait1 {} + | ^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/feature-gate-more-maybe-bounds.rs:12:11 + | +LL | fn bar(_: T) {} + | ^^^^^^^ ^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/feature-gate-more-maybe-bounds.rs:12:11 + | +LL | fn bar(_: T) {} + | ^^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/feature-gate-more-maybe-bounds.rs:12:21 + | +LL | fn bar(_: T) {} + | ^^^^^^^ + +error: aborting due to 4 previous errors; 2 warnings emitted + +Some errors have detailed explanations: E0203, E0658. +For more information about an error, try `rustc --explain E0203`. diff --git a/tests/ui/maybe-bounds.stderr b/tests/ui/maybe-bounds.stderr index 1d823b6acb2..230d11fd0ae 100644 --- a/tests/ui/maybe-bounds.stderr +++ b/tests/ui/maybe-bounds.stderr @@ -1,22 +1,31 @@ -error: `?Trait` is not permitted in supertraits +error[E0658]: `?Trait` is not permitted in supertraits --> $DIR/maybe-bounds.rs:1:11 | LL | trait Tr: ?Sized {} | ^^^^^^ | = note: traits are `?Sized` by default + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bounds.rs:4:20 | LL | type A1 = dyn Tr + (?Sized); | ^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bounds.rs:6:28 | LL | type A2 = dyn for<'a> Tr + (?Sized); | ^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/trait-object-trait-parens.stderr b/tests/ui/parser/trait-object-trait-parens.stderr index 3134746b930..ff32b173d49 100644 --- a/tests/ui/parser/trait-object-trait-parens.stderr +++ b/tests/ui/parser/trait-object-trait-parens.stderr @@ -1,20 +1,29 @@ -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/trait-object-trait-parens.rs:8:24 | LL | let _: Box<(Obj) + (?Sized) + (for<'a> Trait<'a>)>; | ^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/trait-object-trait-parens.rs:13:16 | LL | let _: Box Trait<'a>) + (Obj)>; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/trait-object-trait-parens.rs:18:44 | LL | let _: Box Trait<'a> + (Obj) + (?Sized)>; | ^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: trait objects without an explicit `dyn` are deprecated --> $DIR/trait-object-trait-parens.rs:8:16 @@ -91,4 +100,5 @@ LL | let _: Box Trait<'a> + (Obj) + (?Sized)>; error: aborting due to 6 previous errors; 3 warnings emitted -For more information about this error, try `rustc --explain E0225`. +Some errors have detailed explanations: E0225, E0658. +For more information about an error, try `rustc --explain E0225`. diff --git a/tests/ui/traits/maybe-polarity-pass.rs b/tests/ui/traits/maybe-polarity-pass.rs new file mode 100644 index 00000000000..075a0d8dcac --- /dev/null +++ b/tests/ui/traits/maybe-polarity-pass.rs @@ -0,0 +1,27 @@ +//@ check-pass + +#![feature(auto_traits)] +#![feature(more_maybe_bounds)] +#![feature(negative_impls)] + +trait Trait1 {} +auto trait Trait2 {} + +trait Trait3 : ?Trait1 {} +trait Trait4 where Self: Trait1 {} + +fn foo(_: Box<(dyn Trait3 + ?Trait2)>) {} +fn bar(_: &T) {} +//~^ WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + +struct S; +impl !Trait2 for S {} +impl Trait1 for S {} +impl Trait3 for S {} + +fn main() { + foo(Box::new(S)); + bar(&S); +} diff --git a/tests/ui/traits/maybe-polarity-pass.stderr b/tests/ui/traits/maybe-polarity-pass.stderr new file mode 100644 index 00000000000..09ed52f5b0d --- /dev/null +++ b/tests/ui/traits/maybe-polarity-pass.stderr @@ -0,0 +1,20 @@ +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-pass.rs:14:20 + | +LL | fn bar(_: &T) {} + | ^^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-pass.rs:14:30 + | +LL | fn bar(_: &T) {} + | ^^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-pass.rs:14:40 + | +LL | fn bar(_: &T) {} + | ^^^^^^^ + +warning: 3 warnings emitted + diff --git a/tests/ui/traits/wf-object/maybe-bound.stderr b/tests/ui/traits/wf-object/maybe-bound.stderr index 2fe3f0fc39f..be7afabd0d0 100644 --- a/tests/ui/traits/wf-object/maybe-bound.stderr +++ b/tests/ui/traits/wf-object/maybe-bound.stderr @@ -1,32 +1,48 @@ -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:5:15 | LL | type _0 = dyn ?Sized + Foo; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:8:21 | LL | type _1 = dyn Foo + ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:11:21 | LL | type _2 = dyn Foo + ?Sized + ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:11:30 | LL | type _2 = dyn Foo + ?Sized + ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/maybe-bound.rs:15:15 | LL | type _3 = dyn ?Sized + Foo; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error: aborting due to 5 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/wf-object/only-maybe-bound.stderr b/tests/ui/traits/wf-object/only-maybe-bound.stderr index cbc41feec1e..26269476eaa 100644 --- a/tests/ui/traits/wf-object/only-maybe-bound.stderr +++ b/tests/ui/traits/wf-object/only-maybe-bound.stderr @@ -1,8 +1,11 @@ -error: `?Trait` is not permitted in trait object types +error[E0658]: `?Trait` is not permitted in trait object types --> $DIR/only-maybe-bound.rs:3:15 | LL | type _0 = dyn ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0224]: at least one trait is required for an object type --> $DIR/only-maybe-bound.rs:3:11 @@ -12,4 +15,5 @@ LL | type _0 = dyn ?Sized; error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0224`. +Some errors have detailed explanations: E0224, E0658. +For more information about an error, try `rustc --explain E0224`. diff --git a/tests/ui/unsized/maybe-bounds-where.stderr b/tests/ui/unsized/maybe-bounds-where.stderr index 683bd387bb2..f7851261667 100644 --- a/tests/ui/unsized/maybe-bounds-where.stderr +++ b/tests/ui/unsized/maybe-bounds-where.stderr @@ -1,32 +1,47 @@ -error: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:1:28 | LL | struct S1(T) where (T): ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:4:27 | LL | struct S2(T) where u8: ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:7:35 | LL | struct S3(T) where &'static T: ?Sized; | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:12:34 | LL | struct S4(T) where for<'a> T: ?Trait<'a>; | ^^^^^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: `?Trait` bounds are only permitted at the point where a type parameter is declared +error[E0658]: `?Trait` bounds are only permitted at the point where a type parameter is declared --> $DIR/maybe-bounds-where.rs:21:21 | LL | fn f() where T: ?Sized {} | ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/maybe-bounds-where.rs:12:34 @@ -39,6 +54,9 @@ error[E0203]: type parameter has more than one relaxed default bound, only one i | LL | struct S5(*const T) where T: ?Trait<'static> + ?Sized; | ^^^^^^^^^^^^^^^ ^^^^^^ + | + = help: add `#![feature(more_maybe_bounds)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default --> $DIR/maybe-bounds-where.rs:16:33 @@ -48,4 +66,5 @@ LL | struct S5(*const T) where T: ?Trait<'static> + ?Sized; error: aborting due to 6 previous errors; 2 warnings emitted -For more information about this error, try `rustc --explain E0203`. +Some errors have detailed explanations: E0203, E0658. +For more information about an error, try `rustc --explain E0203`. -- cgit 1.4.1-3-g733a5 From a7d57aa7c8f42dda6d33e0ac349cf834eaf041b8 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Wed, 24 Jul 2024 19:12:50 -0400 Subject: Let InstCombine remove Clone shims inside Clone shims Co-authored-by: scottmcm --- compiler/rustc_middle/src/ty/sty.rs | 6 +++--- compiler/rustc_mir_transform/src/shim.rs | 3 ++- library/rtstartup/rsbegin.rs | 2 ++ library/rtstartup/rsend.rs | 2 ++ tests/assembly/simd-intrinsic-mask-load.rs | 1 + tests/assembly/simd-intrinsic-mask-store.rs | 1 + tests/codegen/avr/avr-func-addrspace.rs | 1 + tests/codegen/clone-shims.rs | 15 +++++++++++++++ tests/codegen/emcripten-catch-unwind.rs | 2 ++ .../riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs | 1 + .../kcfi/emit-type-metadata-trait-objects.rs | 1 + tests/ui/lang-items/missing-clone-for-suggestion.rs | 20 -------------------- .../lang-items/missing-clone-for-suggestion.stderr | 21 --------------------- 13 files changed, 31 insertions(+), 45 deletions(-) create mode 100644 tests/codegen/clone-shims.rs delete mode 100644 tests/ui/lang-items/missing-clone-for-suggestion.rs delete mode 100644 tests/ui/lang-items/missing-clone-for-suggestion.stderr (limited to 'compiler') diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index d2b444a066b..da98e3b9f46 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -1864,9 +1864,9 @@ impl<'tcx> Ty<'tcx> { // Definitely absolutely not copy. ty::Ref(_, _, hir::Mutability::Mut) => false, - // Thin pointers & thin shared references are pure-clone-copy, but for - // anything with custom metadata it might be more complicated. - ty::Ref(_, _, hir::Mutability::Not) | ty::RawPtr(..) => false, + // The standard library has a blanket Copy impl for shared references and raw pointers, + // for all unsized types. + ty::Ref(_, _, hir::Mutability::Not) | ty::RawPtr(..) => true, ty::Coroutine(..) | ty::CoroutineWitness(..) => false, diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 6835a39cf36..19e3bf5a599 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -17,7 +17,7 @@ use std::iter; use crate::{ abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, - mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify, + instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify, }; use rustc_middle::mir::patch::MirPatch; use rustc_mir_dataflow::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle}; @@ -154,6 +154,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< &deref_separator::Derefer, &remove_noop_landing_pads::RemoveNoopLandingPads, &simplify::SimplifyCfg::MakeShim, + &instsimplify::InstSimplify, &abort_unwinding_calls::AbortUnwindingCalls, &add_call_guards::CriticalCallEdges, ], diff --git a/library/rtstartup/rsbegin.rs b/library/rtstartup/rsbegin.rs index 14bce2bbeee..9a3d95bd8dd 100644 --- a/library/rtstartup/rsbegin.rs +++ b/library/rtstartup/rsbegin.rs @@ -29,6 +29,8 @@ trait Copy {} #[lang = "freeze"] auto trait Freeze {} +impl Copy for *mut T {} + #[lang = "drop_in_place"] #[inline] #[allow(unconditional_recursion)] diff --git a/library/rtstartup/rsend.rs b/library/rtstartup/rsend.rs index 714643c8386..2514eb00344 100644 --- a/library/rtstartup/rsend.rs +++ b/library/rtstartup/rsend.rs @@ -17,6 +17,8 @@ trait Copy {} #[lang = "freeze"] auto trait Freeze {} +impl Copy for *mut T {} + #[lang = "drop_in_place"] #[inline] #[allow(unconditional_recursion)] diff --git a/tests/assembly/simd-intrinsic-mask-load.rs b/tests/assembly/simd-intrinsic-mask-load.rs index d537c143d36..b650e1cee30 100644 --- a/tests/assembly/simd-intrinsic-mask-load.rs +++ b/tests/assembly/simd-intrinsic-mask-load.rs @@ -18,6 +18,7 @@ pub trait Sized {} #[lang = "copy"] trait Copy {} +impl Copy for *const T {} #[repr(simd)] pub struct i8x16([i8; 16]); diff --git a/tests/assembly/simd-intrinsic-mask-store.rs b/tests/assembly/simd-intrinsic-mask-store.rs index 5d4c00c3823..95a3b28b967 100644 --- a/tests/assembly/simd-intrinsic-mask-store.rs +++ b/tests/assembly/simd-intrinsic-mask-store.rs @@ -18,6 +18,7 @@ pub trait Sized {} #[lang = "copy"] trait Copy {} +impl Copy for *mut T {} #[repr(simd)] pub struct i8x16([i8; 16]); diff --git a/tests/codegen/avr/avr-func-addrspace.rs b/tests/codegen/avr/avr-func-addrspace.rs index 70834707564..7f9a7e6e811 100644 --- a/tests/codegen/avr/avr-func-addrspace.rs +++ b/tests/codegen/avr/avr-func-addrspace.rs @@ -17,6 +17,7 @@ pub trait Sized {} #[lang = "copy"] pub trait Copy {} +impl Copy for *const T {} #[lang = "receiver"] pub trait Receiver {} #[lang = "tuple_trait"] diff --git a/tests/codegen/clone-shims.rs b/tests/codegen/clone-shims.rs new file mode 100644 index 00000000000..06c959f9ee7 --- /dev/null +++ b/tests/codegen/clone-shims.rs @@ -0,0 +1,15 @@ +// Clone shims for aggregates are generated by just calling the Clone shims for all their members. +// Those calls generate a lot of unnecessary IR if the members are Copy. This test ensures that we +// optimize away those inner calls without needing to inline them. + +//@ compile-flags: -Cno-prepopulate-passes -Csymbol-mangling-version=v0 -Zinline-mir=no +#![crate_type = "lib"] + +pub type Test = (i32, i32, *const i32); +pub static TEST: fn(&Test) -> Test = ::clone; + +// CHECK-NOT: call ::clone +// CHECK-NOT: call <*const i32 as core::clone::Clone>::clone +// CHECK: ; <(i32, i32, *const i32) as core::clone::Clone>::clone +// CHECK-NOT: call ::clone +// CHECK-NOT: call <*const i32 as core::clone::Clone>::clone diff --git a/tests/codegen/emcripten-catch-unwind.rs b/tests/codegen/emcripten-catch-unwind.rs index 6cda8c6799f..35444db9558 100644 --- a/tests/codegen/emcripten-catch-unwind.rs +++ b/tests/codegen/emcripten-catch-unwind.rs @@ -16,6 +16,8 @@ trait Freeze {} #[lang = "copy"] trait Copy {} +impl Copy for *mut T {} + #[rustc_intrinsic] fn size_of() -> usize { loop {} diff --git a/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs b/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs index ed0af90aaaf..520192b5d59 100644 --- a/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs +++ b/tests/codegen/riscv-abi/riscv64-lp64-lp64f-lp64d-abi.rs @@ -18,6 +18,7 @@ impl Copy for i64 {} impl Copy for u64 {} impl Copy for f32 {} impl Copy for f64 {} +impl Copy for *mut T {} // CHECK: define void @f_void() #[no_mangle] diff --git a/tests/codegen/sanitizer/kcfi/emit-type-metadata-trait-objects.rs b/tests/codegen/sanitizer/kcfi/emit-type-metadata-trait-objects.rs index fc4d570dc2e..c1967e55e75 100644 --- a/tests/codegen/sanitizer/kcfi/emit-type-metadata-trait-objects.rs +++ b/tests/codegen/sanitizer/kcfi/emit-type-metadata-trait-objects.rs @@ -15,6 +15,7 @@ trait Sized {} #[lang = "copy"] trait Copy {} +impl Copy for &T {} #[lang = "receiver"] trait Receiver {} #[lang = "dispatch_from_dyn"] diff --git a/tests/ui/lang-items/missing-clone-for-suggestion.rs b/tests/ui/lang-items/missing-clone-for-suggestion.rs deleted file mode 100644 index e8290c0098a..00000000000 --- a/tests/ui/lang-items/missing-clone-for-suggestion.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Avoid panicking if the Clone trait is not found while building error suggestions -// See #104870 - -#![feature(no_core, lang_items)] -#![no_core] - -#[lang = "sized"] -trait Sized {} - -#[lang = "copy"] -trait Copy {} - -fn g(x: T) {} - -fn f(x: *mut u8) { - g(x); - g(x); //~ ERROR use of moved value: `x` -} - -fn main() {} diff --git a/tests/ui/lang-items/missing-clone-for-suggestion.stderr b/tests/ui/lang-items/missing-clone-for-suggestion.stderr deleted file mode 100644 index 0187f965b5c..00000000000 --- a/tests/ui/lang-items/missing-clone-for-suggestion.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0382]: use of moved value: `x` - --> $DIR/missing-clone-for-suggestion.rs:17:7 - | -LL | fn f(x: *mut u8) { - | - move occurs because `x` has type `*mut u8`, which does not implement the `Copy` trait -LL | g(x); - | - value moved here -LL | g(x); - | ^ value used here after move - | -note: consider changing this parameter type in function `g` to borrow instead if owning the value isn't necessary - --> $DIR/missing-clone-for-suggestion.rs:13:12 - | -LL | fn g(x: T) {} - | - ^ this parameter takes ownership of the value - | | - | in this function - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0382`. -- cgit 1.4.1-3-g733a5 From f4f57bfccb98522f4aaa2942baaa5a381c7d952d Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Wed, 24 Jul 2024 22:43:12 -0400 Subject: Make Clone::clone a lang item --- compiler/rustc_hir/src/lang_items.rs | 1 + compiler/rustc_mir_transform/src/instsimplify.rs | 4 ++-- compiler/rustc_span/src/symbol.rs | 1 + library/core/src/clone.rs | 3 +++ 4 files changed, 7 insertions(+), 2 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 58cc0f62111..1821387e85f 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -162,6 +162,7 @@ language_item_table! { StructuralPeq, sym::structural_peq, structural_peq_trait, Target::Trait, GenericRequirement::None; Copy, sym::copy, copy_trait, Target::Trait, GenericRequirement::Exact(0); Clone, sym::clone, clone_trait, Target::Trait, GenericRequirement::None; + CloneFn, sym::clone_fn, clone_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; Sync, sym::sync, sync_trait, Target::Trait, GenericRequirement::Exact(0); DiscriminantKind, sym::discriminant_kind, discriminant_kind_trait, Target::Trait, GenericRequirement::None; /// The associated item of the `DiscriminantKind` trait. diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 8209e5e2711..58fdc2d9e45 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -3,6 +3,7 @@ use crate::simplify::simplify_duplicate_switch_targets; use crate::take_array; use rustc_ast::attr; +use rustc_hir::LangItem; use rustc_middle::bug; use rustc_middle::mir::*; use rustc_middle::ty::layout; @@ -271,8 +272,7 @@ impl<'tcx> InstSimplifyContext<'tcx, '_> { return; } - let trait_def_id = self.tcx.trait_of_item(fn_def_id); - if trait_def_id.is_none() || trait_def_id != self.tcx.lang_items().clone_trait() { + if !self.tcx.is_lang_item(fn_def_id, LangItem::CloneFn) { return; } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 5ae01389116..18bc514fc98 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -557,6 +557,7 @@ symbols! { clobber_abi, clone, clone_closures, + clone_fn, clone_from, closure, closure_lifetime_binder, diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 939b2be6dfa..76a89eaaff8 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -160,6 +160,9 @@ pub trait Clone: Sized { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[must_use = "cloning is often expensive and is not expected to have side effects"] + // Clone::clone is special because the compiler generates MIR to implement it for some types. + // See InstanceKind::CloneShim. + #[cfg_attr(not(bootstrap), lang = "clone_fn")] fn clone(&self) -> Self; /// Performs copy-assignment from `source`. -- cgit 1.4.1-3-g733a5 From caee195bdd922c25946276625993b93a5bc0eb41 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 08:42:43 +1000 Subject: Invert early exit conditions in `collect_tokens_trailing_token`. This has been bugging me for a while. I find complex "if any of these are true" conditions easier to think about than complex "if all of these are true" conditions, because you can stop as soon as one is true. --- compiler/rustc_parse/src/parser/attr_wrapper.rs | 58 ++++++++++++------------- 1 file changed, 29 insertions(+), 29 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index dc5f98f7be8..6413aa09241 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -199,20 +199,20 @@ impl<'a> Parser<'a> { force_collect: ForceCollect, f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, (R, bool)>, ) -> PResult<'a, R> { - // Skip collection when nothing could observe the collected tokens, i.e. - // all of the following conditions hold. - // - We are not force collecting tokens (because force collection - // requires tokens by definition). - if matches!(force_collect, ForceCollect::No) - // - None of our outer attributes require tokens. - && attrs.is_complete() - // - Our target doesn't support custom inner attributes (custom + // We must collect if anything could observe the collected tokens, i.e. + // if any of the following conditions hold. + // - We are force collecting tokens (because force collection requires + // tokens by definition). + let needs_collection = matches!(force_collect, ForceCollect::Yes) + // - Any of our outer attributes require tokens. + || !attrs.is_complete() + // - Our target supports custom inner attributes (custom // inner attribute invocation might require token capturing). - && !R::SUPPORTS_CUSTOM_INNER_ATTRS - // - We are not in `capture_cfg` mode (which requires tokens if + || R::SUPPORTS_CUSTOM_INNER_ATTRS + // - We are in `capture_cfg` mode (which requires tokens if // the parsed node has `#[cfg]` or `#[cfg_attr]` attributes). - && !self.capture_cfg - { + || self.capture_cfg; + if !needs_collection { return Ok(f(self, attrs.attrs)?.0); } @@ -250,28 +250,28 @@ impl<'a> Parser<'a> { return Ok(ret); } - // This is similar to the "skip collection" check at the start of this - // function, but now that we've parsed an AST node we have more + // This is similar to the `needs_collection` check at the start of this + // function, but now that we've parsed an AST node we have complete // information available. (If we return early here that means the // setup, such as cloning the token cursor, was unnecessary. That's // hard to avoid.) // - // Skip collection when nothing could observe the collected tokens, i.e. - // all of the following conditions hold. - // - We are not force collecting tokens. - if matches!(force_collect, ForceCollect::No) - // - None of our outer *or* inner attributes require tokens. + // We must collect if anything could observe the collected tokens, i.e. + // if any of the following conditions hold. + // - We are force collecting tokens. + let needs_collection = matches!(force_collect, ForceCollect::Yes) + // - Any of our outer *or* inner attributes require tokens. // (`attrs` was just outer attributes, but `ret.attrs()` is outer - // and inner attributes. That makes this check more precise than - // `attrs.is_complete()` at the start of the function, and we can - // skip the subsequent check on `R::SUPPORTS_CUSTOM_INNER_ATTRS`. - && crate::parser::attr::is_complete(ret.attrs()) - // - We are not in `capture_cfg` mode, or we are but there are no - // `#[cfg]` or `#[cfg_attr]` attributes. (During normal - // non-`capture_cfg` parsing, we don't need any special capturing - // for those attributes, because they're builtin.) - && (!self.capture_cfg || !has_cfg_or_cfg_attr(ret.attrs())) - { + // and inner attributes. So this check is more precise than the + // earlier `attrs.is_complete()` check, and we don't need to + // check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.) + || !crate::parser::attr::is_complete(ret.attrs()) + // - We are in `capture_cfg` mode and there are `#[cfg]` or + // `#[cfg_attr]` attributes. (During normal non-`capture_cfg` + // parsing, we don't need any special capturing for those + // attributes, because they're builtin.) + || (self.capture_cfg && has_cfg_or_cfg_attr(ret.attrs())); + if !needs_collection { return Ok(ret); } -- cgit 1.4.1-3-g733a5 From 4288edb219fb288af524b490bd3830fc7cfafe02 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 09:00:17 +1000 Subject: Inline and remove `AttrWrapper::is_complete`. It has a single call site. This change makes the two `needs_collect` conditions more similar to each other, and therefore easier to understand. --- compiler/rustc_parse/src/parser/attr_wrapper.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 6413aa09241..27b899300e7 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -60,10 +60,6 @@ impl AttrWrapper { pub fn is_empty(&self) -> bool { self.attrs.is_empty() } - - pub fn is_complete(&self) -> bool { - crate::parser::attr::is_complete(&self.attrs) - } } /// Returns `true` if `attrs` contains a `cfg` or `cfg_attr` attribute @@ -205,7 +201,7 @@ impl<'a> Parser<'a> { // tokens by definition). let needs_collection = matches!(force_collect, ForceCollect::Yes) // - Any of our outer attributes require tokens. - || !attrs.is_complete() + || !crate::parser::attr::is_complete(&attrs.attrs) // - Our target supports custom inner attributes (custom // inner attribute invocation might require token capturing). || R::SUPPORTS_CUSTOM_INNER_ATTRS @@ -261,9 +257,9 @@ impl<'a> Parser<'a> { // - We are force collecting tokens. let needs_collection = matches!(force_collect, ForceCollect::Yes) // - Any of our outer *or* inner attributes require tokens. - // (`attrs` was just outer attributes, but `ret.attrs()` is outer - // and inner attributes. So this check is more precise than the - // earlier `attrs.is_complete()` check, and we don't need to + // (`attr.attrs` was just outer attributes, but `ret.attrs()` is + // outer and inner attributes. So this check is more precise than + // the earlier `is_complete()` check, and we don't need to // check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.) || !crate::parser::attr::is_complete(ret.attrs()) // - We are in `capture_cfg` mode and there are `#[cfg]` or -- cgit 1.4.1-3-g733a5 From 3d363c3d99a5e27fa0b604aae4ca745b4cb4c43a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 09:03:30 +1000 Subject: Move `is_complete` to the module that uses it. And make it non-`pub`. --- compiler/rustc_parse/src/parser/attr.rs | 11 ----------- compiler/rustc_parse/src/parser/attr_wrapper.rs | 15 +++++++++++++-- 2 files changed, 13 insertions(+), 13 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 0b2c3044039..535b53a836e 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -457,14 +457,3 @@ impl<'a> Parser<'a> { Err(self.dcx().create_err(err)) } } - -/// The attributes are complete if all attributes are either a doc comment or a -/// builtin attribute other than `cfg_attr`. -pub fn is_complete(attrs: &[ast::Attribute]) -> bool { - attrs.iter().all(|attr| { - attr.is_doc_comment() - || attr.ident().is_some_and(|ident| { - ident.name != sym::cfg_attr && rustc_feature::is_builtin_attr_name(ident.name) - }) - }) -} diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 27b899300e7..82fd2257481 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -201,7 +201,7 @@ impl<'a> Parser<'a> { // tokens by definition). let needs_collection = matches!(force_collect, ForceCollect::Yes) // - Any of our outer attributes require tokens. - || !crate::parser::attr::is_complete(&attrs.attrs) + || !is_complete(&attrs.attrs) // - Our target supports custom inner attributes (custom // inner attribute invocation might require token capturing). || R::SUPPORTS_CUSTOM_INNER_ATTRS @@ -261,7 +261,7 @@ impl<'a> Parser<'a> { // outer and inner attributes. So this check is more precise than // the earlier `is_complete()` check, and we don't need to // check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.) - || !crate::parser::attr::is_complete(ret.attrs()) + || !is_complete(ret.attrs()) // - We are in `capture_cfg` mode and there are `#[cfg]` or // `#[cfg_attr]` attributes. (During normal non-`capture_cfg` // parsing, we don't need any special capturing for those @@ -457,6 +457,17 @@ fn make_attr_token_stream( AttrTokenStream::new(stack_top.inner) } +/// The attributes are complete if all attributes are either a doc comment or a +/// builtin attribute other than `cfg_attr`. +fn is_complete(attrs: &[ast::Attribute]) -> bool { + attrs.iter().all(|attr| { + attr.is_doc_comment() + || attr.ident().is_some_and(|ident| { + ident.name != sym::cfg_attr && rustc_feature::is_builtin_attr_name(ident.name) + }) + }) +} + // Some types are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { -- cgit 1.4.1-3-g733a5 From e631b1ebfa273fc4703fa2fd60fde281340d4909 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 09:06:19 +1000 Subject: Invert the sense of `is_complete` and rename it as `needs_tokens`. I have always found `is_complete` an unhelpful name. The new name (and inverted sense) fits in better with the conditions at its call sites. --- compiler/rustc_parse/src/parser/attr_wrapper.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 82fd2257481..1bc41e68cf3 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -201,7 +201,7 @@ impl<'a> Parser<'a> { // tokens by definition). let needs_collection = matches!(force_collect, ForceCollect::Yes) // - Any of our outer attributes require tokens. - || !is_complete(&attrs.attrs) + || needs_tokens(&attrs.attrs) // - Our target supports custom inner attributes (custom // inner attribute invocation might require token capturing). || R::SUPPORTS_CUSTOM_INNER_ATTRS @@ -259,9 +259,9 @@ impl<'a> Parser<'a> { // - Any of our outer *or* inner attributes require tokens. // (`attr.attrs` was just outer attributes, but `ret.attrs()` is // outer and inner attributes. So this check is more precise than - // the earlier `is_complete()` check, and we don't need to + // the earlier `needs_tokens` check, and we don't need to // check `R::SUPPORTS_CUSTOM_INNER_ATTRS`.) - || !is_complete(ret.attrs()) + || needs_tokens(ret.attrs()) // - We are in `capture_cfg` mode and there are `#[cfg]` or // `#[cfg_attr]` attributes. (During normal non-`capture_cfg` // parsing, we don't need any special capturing for those @@ -457,14 +457,16 @@ fn make_attr_token_stream( AttrTokenStream::new(stack_top.inner) } -/// The attributes are complete if all attributes are either a doc comment or a -/// builtin attribute other than `cfg_attr`. -fn is_complete(attrs: &[ast::Attribute]) -> bool { - attrs.iter().all(|attr| { - attr.is_doc_comment() - || attr.ident().is_some_and(|ident| { - ident.name != sym::cfg_attr && rustc_feature::is_builtin_attr_name(ident.name) - }) +/// Tokens are needed if: +/// - any non-single-segment attributes (other than doc comments) are present; or +/// - any `cfg_attr` attributes are present; +/// - any single-segment, non-builtin attributes are present. +fn needs_tokens(attrs: &[ast::Attribute]) -> bool { + attrs.iter().any(|attr| match attr.ident() { + None => !attr.is_doc_comment(), + Some(ident) => { + ident.name == sym::cfg_attr || !rustc_feature::is_builtin_attr_name(ident.name) + } }) } -- cgit 1.4.1-3-g733a5 From 2fca4ea317f2f45a9a6f8272e52661807b100ca3 Mon Sep 17 00:00:00 2001 From: Lin Yihai Date: Wed, 10 Jul 2024 10:59:33 +0800 Subject: Add a label to point to the lacking macro name definition --- compiler/rustc_resolve/src/diagnostics.rs | 5 ++++- compiler/rustc_resolve/src/errors.rs | 2 +- tests/ui/macros/issue-118786.rs | 1 + tests/ui/macros/issue-118786.stderr | 6 +++--- tests/ui/resolve/issue-118295.rs | 6 ++++++ tests/ui/resolve/issue-118295.stderr | 18 +++++++++++++++--- 6 files changed, 30 insertions(+), 8 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index ffd495aa985..28a3698c080 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1439,7 +1439,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { ); if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules { - err.subdiagnostic(MaybeMissingMacroRulesName { span: ident.span }); + let label_span = ident.span.shrink_to_hi(); + let mut spans = MultiSpan::from_span(label_span); + spans.push_span_label(label_span, "put a macro name here"); + err.subdiagnostic(MaybeMissingMacroRulesName { spans: spans }); return; } diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 0620f3d709e..5bcb2badd95 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -667,7 +667,7 @@ pub(crate) struct MacroSuggMovePosition { #[note(resolve_missing_macro_rules_name)] pub(crate) struct MaybeMissingMacroRulesName { #[primary_span] - pub(crate) span: Span, + pub(crate) spans: MultiSpan, } #[derive(Subdiagnostic)] diff --git a/tests/ui/macros/issue-118786.rs b/tests/ui/macros/issue-118786.rs index 97454c9de07..a41372e4ea8 100644 --- a/tests/ui/macros/issue-118786.rs +++ b/tests/ui/macros/issue-118786.rs @@ -7,6 +7,7 @@ macro_rules! make_macro { macro_rules! $macro_name { //~^ ERROR macro expansion ignores token `{` and any following //~| ERROR cannot find macro `macro_rules` in this scope + //~| put a macro name here () => {} } } diff --git a/tests/ui/macros/issue-118786.stderr b/tests/ui/macros/issue-118786.stderr index 03e65c94ba7..256b742ee16 100644 --- a/tests/ui/macros/issue-118786.stderr +++ b/tests/ui/macros/issue-118786.stderr @@ -1,5 +1,5 @@ error: macros that expand to items must be delimited with braces or followed by a semicolon - --> $DIR/issue-118786.rs:15:13 + --> $DIR/issue-118786.rs:16:13 | LL | make_macro!((meow)); | ^^^^^^ @@ -34,10 +34,10 @@ LL | make_macro!((meow)); | ------------------- in this macro invocation | note: maybe you have forgotten to define a name for this `macro_rules!` - --> $DIR/issue-118786.rs:7:9 + --> $DIR/issue-118786.rs:7:20 | LL | macro_rules! $macro_name { - | ^^^^^^^^^^^ + | ^ put a macro name here ... LL | make_macro!((meow)); | ------------------- in this macro invocation diff --git a/tests/ui/resolve/issue-118295.rs b/tests/ui/resolve/issue-118295.rs index b97681d9563..37a49baee80 100644 --- a/tests/ui/resolve/issue-118295.rs +++ b/tests/ui/resolve/issue-118295.rs @@ -1,5 +1,11 @@ macro_rules! {} //~^ ERROR cannot find macro `macro_rules` in this scope //~| NOTE maybe you have forgotten to define a name for this `macro_rules!` +//~| put a macro name here + +macro_rules!{} +//~^ ERROR cannot find macro `macro_rules` in this scope +//~| NOTE maybe you have forgotten to define a name for this `macro_rules!` +//~| put a macro name here fn main() {} diff --git a/tests/ui/resolve/issue-118295.stderr b/tests/ui/resolve/issue-118295.stderr index d60d7d9185d..06a37e81d6c 100644 --- a/tests/ui/resolve/issue-118295.stderr +++ b/tests/ui/resolve/issue-118295.stderr @@ -1,14 +1,26 @@ error: cannot find macro `macro_rules` in this scope - --> $DIR/issue-118295.rs:1:1 + --> $DIR/issue-118295.rs:6:1 | -LL | macro_rules! {} +LL | macro_rules!{} | ^^^^^^^^^^^ | note: maybe you have forgotten to define a name for this `macro_rules!` + --> $DIR/issue-118295.rs:6:12 + | +LL | macro_rules!{} + | ^ put a macro name here + +error: cannot find macro `macro_rules` in this scope --> $DIR/issue-118295.rs:1:1 | LL | macro_rules! {} | ^^^^^^^^^^^ + | +note: maybe you have forgotten to define a name for this `macro_rules!` + --> $DIR/issue-118295.rs:1:12 + | +LL | macro_rules! {} + | ^ put a macro name here -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -- cgit 1.4.1-3-g733a5 From a560810a69a09452b4cd0c3173b6af98496dba35 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 10:54:56 +1000 Subject: Don't include inner attribute ranges in `CaptureState`. The current code is this: ``` self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target))); self.capture_state.replace_ranges.extend(inner_attr_replace_ranges); ``` What's not obvious is that every range in `inner_attr_replace_ranges` must be a strict sub-range of `start_pos..end_pos`. Which means, in `LazyAttrTokenStreamImpl::to_attr_token_stream`, they will be done first, and then the `start_pos..end_pos` replacement will just overwrite them. So they aren't needed. --- compiler/rustc_parse/src/parser/attr_wrapper.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index dc5f98f7be8..1fd0654e0ab 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -337,8 +337,7 @@ impl<'a> Parser<'a> { // When parsing `m`: // - `start_pos..end_pos` is `0..34` (`mod m`, excluding the `#[cfg_eval]` attribute). // - `inner_attr_replace_ranges` is empty. - // - `replace_range_start..replace_ranges_end` has two entries. - // - One to delete the inner attribute (`17..27`), obtained when parsing `g` (see above). + // - `replace_range_start..replace_ranges_end` has one entry. // - One `AttrsTarget` (added below when parsing `g`) to replace all of `g` (`3..33`, // including its outer attribute), with: // - `attrs`: includes the outer and the inner attr. @@ -369,12 +368,10 @@ impl<'a> Parser<'a> { // What is the status here when parsing the example code at the top of this method? // - // When parsing `g`, we add two entries: + // When parsing `g`, we add one entry: // - The `start_pos..end_pos` (`3..33`) entry has a new `AttrsTarget` with: // - `attrs`: includes the outer and the inner attr. // - `tokens`: lazy tokens for `g` (with its inner attr deleted). - // - `inner_attr_replace_ranges` contains the one entry to delete the inner attr's - // tokens (`17..27`). // // When parsing `m`, we do nothing here. @@ -384,7 +381,6 @@ impl<'a> Parser<'a> { let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos }; let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens }; self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target))); - self.capture_state.replace_ranges.extend(inner_attr_replace_ranges); } else if matches!(self.capture_state.capturing, Capturing::No) { // Only clear the ranges once we've finished capturing entirely, i.e. we've finished // the outermost call to this method. -- cgit 1.4.1-3-g733a5 From 6e87858f26bcb9fadbd60f9e8ee24816a55faa80 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 14:02:15 +1000 Subject: Fix a comment. Imagine you have replace ranges (2..20,X) and (5..15,Y), and these tokens: ``` a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x ``` If we replace (5..15,Y) first, then (2..20,X) we get this sequence ``` a,b,c,d,e,Y,_,_,_,_,_,_,_,_,_,p,q,r,s,t,u,v,w,x a,b,X,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,_,u,v,w,x ``` which is what we want. If we do it in the other order, we get this: ``` a,b,X,_,_,_,_,_,_,_,_,_,_,_,_,p,q,r,s,t,u,v,w,x a,b,X,_,_,Y,_,_,_,_,_,_,_,_,_,_,_,_,_,_,u,v,w,x ``` which is wrong. So it's true that we need the `.rev()` but the comment is wrong about why. --- compiler/rustc_parse/src/parser/attr_wrapper.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 1fd0654e0ab..a90bfb574e2 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -137,9 +137,9 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` // // By starting processing from the replace range with the greatest - // start position, we ensure that any replace range which encloses - // another replace range will capture the *replaced* tokens for the inner - // range, not the original tokens. + // start position, we ensure that any (outer) replace range which + // encloses another (inner) replace range will fully overwrite the + // inner range's replacement. for (range, target) in replace_ranges.into_iter().rev() { assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}"); -- cgit 1.4.1-3-g733a5 From 6ea2da5a28580dba214b32831d0a6952e2ebce91 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 13:20:27 +1000 Subject: Tweak a loop. A fully imperative style is easier to read than a half-iterator, half-imperative style. Also, rename `inner_attr` as `attr` because it might be an outer attribute. --- compiler/rustc_parse/src/parser/attr_wrapper.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index a90bfb574e2..abee668cc6c 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -297,11 +297,13 @@ impl<'a> Parser<'a> { // with `None`, which means the relevant tokens will be removed. (More // details below.) let mut inner_attr_replace_ranges = Vec::new(); - for inner_attr in ret.attrs().iter().filter(|a| a.style == ast::AttrStyle::Inner) { - if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) { - inner_attr_replace_ranges.push((attr_range, None)); - } else { - self.dcx().span_delayed_bug(inner_attr.span, "Missing token range for attribute"); + for attr in ret.attrs() { + if attr.style == ast::AttrStyle::Inner { + if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&attr.id) { + inner_attr_replace_ranges.push((attr_range, None)); + } else { + self.dcx().span_delayed_bug(attr.span, "Missing token range for attribute"); + } } } -- cgit 1.4.1-3-g733a5 From 55d37ae711b1a1ab82ef02d0594f92167e18af90 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 26 Jul 2024 14:00:29 +1000 Subject: Remove an unnecessary block. --- compiler/rustc_parse/src/parser/attr_wrapper.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index abee668cc6c..389e4a6267d 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -114,17 +114,15 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { replace_ranges.sort_by_key(|(range, _)| range.start); #[cfg(debug_assertions)] - { - for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() { - assert!( - range.end <= next_range.start || range.end >= next_range.end, - "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", - range, - tokens, - next_range, - next_tokens, - ); - } + for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() { + assert!( + range.end <= next_range.start || range.end >= next_range.end, + "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", + range, + tokens, + next_range, + next_tokens, + ); } // Process the replace ranges, starting from the highest start -- cgit 1.4.1-3-g733a5 From 33b98bf2566d1ac768fe979cb9c5ff5b30c6d9b6 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 26 Jul 2024 10:19:31 +0000 Subject: Remove redundant option that was just encoding that a slice was empty --- compiler/rustc_ast_lowering/src/item.rs | 6 +++--- compiler/rustc_ast_lowering/src/lib.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index f990b4ba69b..f0186e36c42 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -172,7 +172,7 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, hir_id: hir::HirId, ident: &mut Ident, - attrs: Option<&'hir [Attribute]>, + attrs: &'hir [Attribute], vis_span: Span, i: &ItemKind, ) -> hir::ItemKind<'hir> { @@ -488,7 +488,7 @@ impl<'hir> LoweringContext<'_, 'hir> { id: NodeId, vis_span: Span, ident: &mut Ident, - attrs: Option<&'hir [Attribute]>, + attrs: &'hir [Attribute], ) -> hir::ItemKind<'hir> { let path = &tree.prefix; let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect(); @@ -566,7 +566,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // `ItemLocalId` and the new owner. (See `lower_node_id`) let kind = this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs); - if let Some(attrs) = attrs { + if !attrs.is_empty() { this.attrs.insert(hir::ItemLocalId::ZERO, attrs); } diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0f5f4d8023b..01ba57e97b2 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -913,15 +913,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ret } - fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> Option<&'hir [Attribute]> { + fn lower_attrs(&mut self, id: HirId, attrs: &[Attribute]) -> &'hir [Attribute] { if attrs.is_empty() { - None + &[] } else { debug_assert_eq!(id.owner, self.current_hir_id_owner); let ret = self.arena.alloc_from_iter(attrs.iter().map(|a| self.lower_attr(a))); debug_assert!(!ret.is_empty()); self.attrs.insert(id.local_id, ret); - Some(ret) + ret } } -- cgit 1.4.1-3-g733a5 From 3fdc99193e95a29b6fd2228b0acc5f8b4a81feda Mon Sep 17 00:00:00 2001 From: Tamme Dittrich Date: Fri, 26 Jul 2024 15:14:05 +0200 Subject: Improve error message for `extern "C" unsafe fn()` This was handled correctly already for `extern unsafe fn()`. Co-authored-by: Folkert --- compiler/rustc_parse/src/parser/item.rs | 9 ++++++--- tests/ui/parser/issues/issue-19398.rs | 6 +++++- tests/ui/parser/issues/issue-19398.stderr | 14 +++++++------- .../issues/issue-87217-keyword-order/wrong-unsafe-abi.rs | 6 +++++- .../issue-87217-keyword-order/wrong-unsafe-abi.stderr | 9 +++++++-- 5 files changed, 30 insertions(+), 14 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9aaf4b99243..112855e6d1f 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2483,12 +2483,15 @@ impl<'a> Parser<'a> { /// `check_pub` adds additional `pub` to the checks in case users place it /// wrongly, can be used to ensure `pub` never comes after `default`. pub(super) fn check_fn_front_matter(&mut self, check_pub: bool, case: Case) -> bool { + const ALL_QUALS: &[Symbol] = + &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern]; + // We use an over-approximation here. // `const const`, `fn const` won't parse, but we're not stepping over other syntax either. // `pub` is added in case users got confused with the ordering like `async pub fn`, // only if it wasn't preceded by `default` as `default pub` is invalid. let quals: &[Symbol] = if check_pub { - &[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern] + ALL_QUALS } else { &[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Safe, kw::Extern] }; @@ -2518,9 +2521,9 @@ impl<'a> Parser<'a> { || self.check_keyword_case(kw::Extern, case) && self.look_ahead(1, |t| t.can_begin_string_literal()) && (self.look_ahead(2, |t| t.is_keyword_case(kw::Fn, case)) || - // this branch is only for better diagnostic in later, `pub` is not allowed here + // this branch is only for better diagnostics; `pub`, `unsafe`, etc. are not allowed here (self.may_recover() - && self.look_ahead(2, |t| t.is_keyword(kw::Pub)) + && self.look_ahead(2, |t| ALL_QUALS.iter().any(|&kw| t.is_keyword(kw))) && self.look_ahead(3, |t| t.is_keyword_case(kw::Fn, case)))) } diff --git a/tests/ui/parser/issues/issue-19398.rs b/tests/ui/parser/issues/issue-19398.rs index 46eb320a172..358f65f1da5 100644 --- a/tests/ui/parser/issues/issue-19398.rs +++ b/tests/ui/parser/issues/issue-19398.rs @@ -1,6 +1,10 @@ trait T { extern "Rust" unsafe fn foo(); - //~^ ERROR expected `{`, found keyword `unsafe` + //~^ ERROR expected `fn`, found keyword `unsafe` + //~| NOTE expected `fn` + //~| HELP `unsafe` must come before `extern "Rust"` + //~| SUGGESTION unsafe extern "Rust" + //~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` } fn main() {} diff --git a/tests/ui/parser/issues/issue-19398.stderr b/tests/ui/parser/issues/issue-19398.stderr index 236fac673b6..2b97ec50c91 100644 --- a/tests/ui/parser/issues/issue-19398.stderr +++ b/tests/ui/parser/issues/issue-19398.stderr @@ -1,13 +1,13 @@ -error: expected `{`, found keyword `unsafe` +error: expected `fn`, found keyword `unsafe` --> $DIR/issue-19398.rs:2:19 | -LL | trait T { - | - while parsing this item list starting here LL | extern "Rust" unsafe fn foo(); - | ^^^^^^ expected `{` -LL | -LL | } - | - the item list ends here + | --------------^^^^^^ + | | | + | | expected `fn` + | help: `unsafe` must come before `extern "Rust"`: `unsafe extern "Rust"` + | + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs index c97425d8ade..794ac635c76 100644 --- a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.rs @@ -7,6 +7,10 @@ // Visibilities are tested elsewhere. extern "C" unsafe fn test() {} -//~^ ERROR +//~^ ERROR expected `fn`, found keyword `unsafe` +//~| NOTE expected `fn` +//~| HELP `unsafe` must come before `extern "C"` +//~| SUGGESTION unsafe extern "C" +//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` fn main() {} diff --git a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr index 42c9764cc1e..8ed037869c8 100644 --- a/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr +++ b/tests/ui/parser/issues/issue-87217-keyword-order/wrong-unsafe-abi.stderr @@ -1,8 +1,13 @@ -error: expected `{`, found keyword `unsafe` +error: expected `fn`, found keyword `unsafe` --> $DIR/wrong-unsafe-abi.rs:9:12 | LL | extern "C" unsafe fn test() {} - | ^^^^^^ expected `{` + | -----------^^^^^^ + | | | + | | expected `fn` + | help: `unsafe` must come before `extern "C"`: `unsafe extern "C"` + | + = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern` error: aborting due to 1 previous error -- cgit 1.4.1-3-g733a5 From fd9d0bfbacc2d5af183ba394ca16cb2581dab02d Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Fri, 26 Jul 2024 16:33:30 +0300 Subject: Forbid `?Trait` bounds repetitions --- .../src/hir_ty_lowering/bounds.rs | 26 +++++++++++++--------- .../feature-gate-more-maybe-bounds.rs | 7 ++++++ .../feature-gate-more-maybe-bounds.stderr | 20 ++++++++++++++++- tests/ui/traits/maybe-polarity-repeated.rs | 9 ++++++++ tests/ui/traits/maybe-polarity-repeated.stderr | 21 +++++++++++++++++ 5 files changed, 72 insertions(+), 11 deletions(-) create mode 100644 tests/ui/traits/maybe-polarity-repeated.rs create mode 100644 tests/ui/traits/maybe-polarity-repeated.stderr (limited to 'compiler') diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index f05884f8912..9d839908559 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -75,16 +75,22 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } - if unbounds.len() > 1 && !tcx.features().more_maybe_bounds { - self.tcx() - .sess - .create_feature_err( - errors::MultipleRelaxedDefaultBounds { - spans: unbounds.iter().map(|ptr| ptr.span).collect(), - }, - sym::more_maybe_bounds, - ) - .emit(); + let mut unique_bounds = FxIndexSet::default(); + let mut seen_repeat = false; + for unbound in &unbounds { + if let Res::Def(DefKind::Trait, unbound_def_id) = unbound.trait_ref.path.res { + seen_repeat |= !unique_bounds.insert(unbound_def_id); + } + } + if unbounds.len() > 1 { + let err = errors::MultipleRelaxedDefaultBounds { + spans: unbounds.iter().map(|ptr| ptr.span).collect(), + }; + if seen_repeat { + self.dcx().emit_err(err); + } else if !tcx.features().more_maybe_bounds { + self.tcx().sess.create_feature_err(err, sym::more_maybe_bounds).emit(); + }; } let mut seen_sized_unbound = false; diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs index 4541499b7c7..debe143b091 100644 --- a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs +++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.rs @@ -14,4 +14,11 @@ fn bar(_: T) {} //~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default //~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +trait Trait {} +// Do not suggest `#![feature(more_maybe_bounds)]` for repetitions +fn baz(_ : T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr index 2b7a353020a..e6d65e05997 100644 --- a/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr +++ b/tests/ui/feature-gates/feature-gate-more-maybe-bounds.stderr @@ -47,7 +47,25 @@ warning: relaxing a default bound only does something for `?Sized`; all other tr LL | fn bar(_: T) {} | ^^^^^^^ -error: aborting due to 4 previous errors; 2 warnings emitted +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/feature-gate-more-maybe-bounds.rs:19:11 + | +LL | fn baz(_ : T) {} + | ^^^^^^ ^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/feature-gate-more-maybe-bounds.rs:19:11 + | +LL | fn baz(_ : T) {} + | ^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/feature-gate-more-maybe-bounds.rs:19:20 + | +LL | fn baz(_ : T) {} + | ^^^^^^ + +error: aborting due to 5 previous errors; 4 warnings emitted Some errors have detailed explanations: E0203, E0658. For more information about an error, try `rustc --explain E0203`. diff --git a/tests/ui/traits/maybe-polarity-repeated.rs b/tests/ui/traits/maybe-polarity-repeated.rs new file mode 100644 index 00000000000..4b5ec83fffa --- /dev/null +++ b/tests/ui/traits/maybe-polarity-repeated.rs @@ -0,0 +1,9 @@ +#![feature(more_maybe_bounds)] + +trait Trait {} +fn foo(_: T) {} +//~^ ERROR type parameter has more than one relaxed default bound, only one is supported +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default +//~| WARN relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + +fn main() {} diff --git a/tests/ui/traits/maybe-polarity-repeated.stderr b/tests/ui/traits/maybe-polarity-repeated.stderr new file mode 100644 index 00000000000..610c484fbec --- /dev/null +++ b/tests/ui/traits/maybe-polarity-repeated.stderr @@ -0,0 +1,21 @@ +error[E0203]: type parameter has more than one relaxed default bound, only one is supported + --> $DIR/maybe-polarity-repeated.rs:4:11 + | +LL | fn foo(_: T) {} + | ^^^^^^ ^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-repeated.rs:4:11 + | +LL | fn foo(_: T) {} + | ^^^^^^ + +warning: relaxing a default bound only does something for `?Sized`; all other traits are not bound by default + --> $DIR/maybe-polarity-repeated.rs:4:20 + | +LL | fn foo(_: T) {} + | ^^^^^^ + +error: aborting due to 1 previous error; 2 warnings emitted + +For more information about this error, try `rustc --explain E0203`. -- cgit 1.4.1-3-g733a5 From d5656059a1e517d9c7b228d21a4134e097e20e00 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 25 Jul 2024 14:02:33 -0400 Subject: Make coroutine-closures possible to be cloned --- compiler/rustc_mir_transform/src/shim.rs | 3 ++ .../src/solve/assembly/structural_traits.rs | 5 ++- .../rustc_trait_selection/src/traits/select/mod.rs | 17 ++++++++-- .../ui/async-await/async-closures/clone-closure.rs | 24 +++++++++++++++ .../async-closures/clone-closure.run.stdout | 2 ++ .../async-closures/move-consuming-capture.stderr | 9 ++++++ .../async-closures/not-clone-closure.rs | 36 ++++++++++++++++++++++ .../async-closures/not-clone-closure.stderr | 20 ++++++++++++ .../without-precise-captures-we-are-powerless.rs | 2 +- ...ithout-precise-captures-we-are-powerless.stderr | 23 ++++---------- 10 files changed, 120 insertions(+), 21 deletions(-) create mode 100644 tests/ui/async-await/async-closures/clone-closure.rs create mode 100644 tests/ui/async-await/async-closures/clone-closure.run.stdout create mode 100644 tests/ui/async-await/async-closures/not-clone-closure.rs create mode 100644 tests/ui/async-await/async-closures/not-clone-closure.stderr (limited to 'compiler') diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 19e3bf5a599..d2f50040821 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -435,6 +435,9 @@ fn build_clone_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) - match self_ty.kind() { ty::FnDef(..) | ty::FnPtr(_) => builder.copy_shim(), ty::Closure(_, args) => builder.tuple_like_shim(dest, src, args.as_closure().upvar_tys()), + ty::CoroutineClosure(_, args) => { + builder.tuple_like_shim(dest, src, args.as_coroutine_closure().upvar_tys()) + } ty::Tuple(..) => builder.tuple_like_shim(dest, src, self_ty.tuple_fields()), ty::Coroutine(coroutine_def_id, args) => { assert_eq!(tcx.coroutine_movability(*coroutine_def_id), hir::Movability::Movable); diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs index 770ac9a929e..60beaa0df84 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs @@ -217,7 +217,10 @@ where // impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]), - ty::CoroutineClosure(..) => Err(NoSolution), + // impl Copy/Clone for CoroutineClosure where Self::TupledUpvars: Copy/Clone + ty::CoroutineClosure(_, args) => { + Ok(vec![ty::Binder::dummy(args.as_coroutine_closure().tupled_upvars_ty())]) + } // only when `coroutine_clone` is enabled and the coroutine is movable // impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index c007cd5314a..699c05466bd 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2262,8 +2262,21 @@ impl<'tcx> SelectionContext<'_, 'tcx> { } } - // FIXME(async_closures): These are never clone, for now. - ty::CoroutineClosure(_, _) => None, + ty::CoroutineClosure(_, args) => { + // (*) binder moved here + let ty = self.infcx.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty()); + if let ty::Infer(ty::TyVar(_)) = ty.kind() { + // Not yet resolved. + Ambiguous + } else { + Where( + obligation + .predicate + .rebind(args.as_coroutine_closure().upvar_tys().to_vec()), + ) + } + } + // `Copy` and `Clone` are automatically implemented for an anonymous adt // if all of its fields are `Copy` and `Clone` ty::Adt(adt, args) if adt.is_anonymous() => { diff --git a/tests/ui/async-await/async-closures/clone-closure.rs b/tests/ui/async-await/async-closures/clone-closure.rs new file mode 100644 index 00000000000..807897e3e03 --- /dev/null +++ b/tests/ui/async-await/async-closures/clone-closure.rs @@ -0,0 +1,24 @@ +//@ aux-build:block-on.rs +//@ edition:2021 +//@ run-pass +//@ check-run-results + +#![feature(async_closure)] + +extern crate block_on; + +async fn for_each(f: impl async FnOnce(&str) + Clone) { + f.clone()("world").await; + f.clone()("world2").await; +} + +fn main() { + block_on::block_on(async_main()); +} + +async fn async_main() { + let x = String::from("Hello,"); + for_each(async move |s| { + println!("{x} {s}"); + }).await; +} diff --git a/tests/ui/async-await/async-closures/clone-closure.run.stdout b/tests/ui/async-await/async-closures/clone-closure.run.stdout new file mode 100644 index 00000000000..0cfcf1923da --- /dev/null +++ b/tests/ui/async-await/async-closures/clone-closure.run.stdout @@ -0,0 +1,2 @@ +Hello, world +Hello, world2 diff --git a/tests/ui/async-await/async-closures/move-consuming-capture.stderr b/tests/ui/async-await/async-closures/move-consuming-capture.stderr index 45c1eac8f8f..4ce71ec49d6 100644 --- a/tests/ui/async-await/async-closures/move-consuming-capture.stderr +++ b/tests/ui/async-await/async-closures/move-consuming-capture.stderr @@ -11,6 +11,15 @@ LL | x().await; | note: `async_call_once` takes ownership of the receiver `self`, which moves `x` --> $SRC_DIR/core/src/ops/async_function.rs:LL:COL +help: you could `clone` the value and consume it, if the `NoCopy: Clone` trait bound could be satisfied + | +LL | x.clone()().await; + | ++++++++ +help: consider annotating `NoCopy` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NoCopy; + | error: aborting due to 1 previous error diff --git a/tests/ui/async-await/async-closures/not-clone-closure.rs b/tests/ui/async-await/async-closures/not-clone-closure.rs new file mode 100644 index 00000000000..2776ce4690f --- /dev/null +++ b/tests/ui/async-await/async-closures/not-clone-closure.rs @@ -0,0 +1,36 @@ +//@ edition: 2021 + +#![feature(async_closure)] + +struct NotClonableArg; +#[derive(Default)] +struct NotClonableReturnType; + +// Verify that the only components that we care about are the upvars, not the signature. +fn we_are_okay_with_not_clonable_signature() { + let x = async |x: NotClonableArg| -> NotClonableReturnType { Default::default() }; + x.clone(); // Okay +} + +#[derive(Debug)] +struct NotClonableUpvar; + +fn we_only_care_about_clonable_upvars() { + let x = NotClonableUpvar; + // Notably, this is clone because we capture `&x`. + let yes_clone = async || { + println!("{x:?}"); + }; + yes_clone.clone(); // Okay + + let z = NotClonableUpvar; + // However, this is not because the closure captures `z` by move. + // (Even though the future that is lent out captures `z by ref!) + let not_clone = async move || { + println!("{z:?}"); + }; + not_clone.clone(); + //~^ ERROR the trait bound `NotClonableUpvar: Clone` is not satisfied +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/not-clone-closure.stderr b/tests/ui/async-await/async-closures/not-clone-closure.stderr new file mode 100644 index 00000000000..aea48a455c2 --- /dev/null +++ b/tests/ui/async-await/async-closures/not-clone-closure.stderr @@ -0,0 +1,20 @@ +error[E0277]: the trait bound `NotClonableUpvar: Clone` is not satisfied in `{async closure@$DIR/not-clone-closure.rs:29:21: 29:34}` + --> $DIR/not-clone-closure.rs:32:15 + | +LL | not_clone.clone(); + | ^^^^^ within `{async closure@$DIR/not-clone-closure.rs:29:21: 29:34}`, the trait `Clone` is not implemented for `NotClonableUpvar`, which is required by `{async closure@$DIR/not-clone-closure.rs:29:21: 29:34}: Clone` + | +note: required because it's used within this closure + --> $DIR/not-clone-closure.rs:29:21 + | +LL | let not_clone = async move || { + | ^^^^^^^^^^^^^ +help: consider annotating `NotClonableUpvar` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClonableUpvar; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs index 17681161e20..18f16ca4b2d 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.rs @@ -19,7 +19,7 @@ fn simple<'a>(x: &'a i32) { let c = async move || { println!("{}", *x); }; outlives::<'a>(c()); //~ ERROR `c` does not live long enough - outlives::<'a>(call_once(c)); //~ ERROR cannot move out of `c` + outlives::<'a>(call_once(c)); } struct S<'a>(&'a i32); diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr index 569028934cb..ed32a53e807 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr @@ -29,22 +29,6 @@ LL | outlives::<'a>(call_once(c)); LL | } | - `c` dropped here while still borrowed -error[E0505]: cannot move out of `c` because it is borrowed - --> $DIR/without-precise-captures-we-are-powerless.rs:22:30 - | -LL | fn simple<'a>(x: &'a i32) { - | -- lifetime `'a` defined here -... -LL | let c = async move || { println!("{}", *x); }; - | - binding `c` declared here -LL | outlives::<'a>(c()); - | --- - | | - | borrow of `c` occurs here - | argument requires that `c` is borrowed for `'a` -LL | outlives::<'a>(call_once(c)); - | ^ move out of `c` occurs here - error[E0597]: `x` does not live long enough --> $DIR/without-precise-captures-we-are-powerless.rs:28:13 | @@ -72,6 +56,11 @@ LL | outlives::<'a>(call_once(c)); LL | LL | let c = async move || { println!("{}", *x.0); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `x` occurs here + | +help: consider cloning the value if the performance cost is acceptable + | +LL | let c = async || { println!("{}", *x.0); }.clone(); + | ++++++++ error[E0597]: `c` does not live long enough --> $DIR/without-precise-captures-we-are-powerless.rs:33:20 @@ -146,7 +135,7 @@ LL | // outlives::<'a>(call_once(c)); // FIXME(async_closures): Figure out w LL | } | - `c` dropped here while still borrowed -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0505, E0597, E0621. For more information about an error, try `rustc --explain E0505`. -- cgit 1.4.1-3-g733a5 From 5a9959fd9daa17e77d609f3c2db4d18554484447 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 25 Jul 2024 14:38:55 -0400 Subject: Suppress useless clone suggestion --- compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs | 5 +++++ .../async-closures/without-precise-captures-we-are-powerless.stderr | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 2d9bc45ebc8..e5bc4b38748 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -1442,9 +1442,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> { // See `tests/ui/moves/needs-clone-through-deref.rs` return false; } + // We don't want to suggest `.clone()` in a move closure, since the value has already been captured. if self.in_move_closure(expr) { return false; } + // We also don't want to suggest cloning a closure itself, since the value has already been captured. + if let hir::ExprKind::Closure(_) = expr.kind { + return false; + } // Try to find predicates on *generic params* that would allow copying `ty` let mut suggestion = if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) { diff --git a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr index ed32a53e807..1df5abdbb18 100644 --- a/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr +++ b/tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr @@ -56,11 +56,6 @@ LL | outlives::<'a>(call_once(c)); LL | LL | let c = async move || { println!("{}", *x.0); }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `x` occurs here - | -help: consider cloning the value if the performance cost is acceptable - | -LL | let c = async || { println!("{}", *x.0); }.clone(); - | ++++++++ error[E0597]: `c` does not live long enough --> $DIR/without-precise-captures-we-are-powerless.rs:33:20 -- cgit 1.4.1-3-g733a5