diff --git a/Cargo.lock b/Cargo.lock index 98567f858e9f..539f66ffb3bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1140,9 +1140,9 @@ version = "0.1.96" [[package]] name = "derive-where" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" +checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 662bcc5d61e7..f3b2a64df853 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -1013,9 +1013,10 @@ # its historical default, but when compiling the compiler itself, we skip it by # default since we know it's safe to do so in that case. # -# On Windows platforms, packed debuginfo is the only supported option, -# producing a `.pdb` file. -#split-debuginfo = if linux { off } else if windows { packed } else if apple { unpacked } +# On Windows MSVC platforms, packed debuginfo is the only supported option, +# producing a `.pdb` file. On Windows GNU rustc doesn't support splitting debuginfo, +# and enabling it causes issues. +#split-debuginfo = if linux || windows-gnu { off } else if windows-msvc { packed } else if apple { unpacked } # Path to the `llvm-config` binary of the installation of a custom LLVM to link # against. Note that if this is specified we don't compile LLVM at all for this diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index a7bf1c99a7e2..51994c2e92f9 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -23,7 +23,7 @@ AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, ParamMode, ResolverAstLoweringExt, }; -impl<'a, 'hir> LoweringContext<'a, 'hir> { +impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { pub(crate) fn lower_inline_asm( &mut self, sp: Span, diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index cf3f331a701b..94839485c603 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -4,9 +4,9 @@ use rustc_span::sym; use smallvec::SmallVec; -use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext}; +use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext, ResolverAstLoweringExt}; -impl<'a, 'hir> LoweringContext<'a, 'hir> { +impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { pub(super) fn lower_block( &mut self, b: &Block, diff --git a/compiler/rustc_ast_lowering/src/contract.rs b/compiler/rustc_ast_lowering/src/contract.rs index 6cffd8e119b7..fd05ed0c78e4 100644 --- a/compiler/rustc_ast_lowering/src/contract.rs +++ b/compiler/rustc_ast_lowering/src/contract.rs @@ -2,9 +2,9 @@ use thin_vec::thin_vec; -use crate::LoweringContext; +use crate::{LoweringContext, ResolverAstLoweringExt}; -impl<'a, 'hir> LoweringContext<'a, 'hir> { +impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { /// Lowered contracts are guarded with the `contract_checks` compiler flag, /// i.e. the flag turns into a boolean guard in the lowered HIR. The reason /// for not eliminating the contract code entirely when the `contract_checks` diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 050e72505abb..0044913721f1 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -37,6 +37,7 @@ //! also be emitted during HIR ty lowering. use std::iter; +use std::marker::PhantomData; use ast::visit::Visitor; use hir::def::{DefKind, PartialRes, Res}; @@ -44,16 +45,15 @@ use rustc_abi::ExternAbi; use rustc_ast as ast; use rustc_ast::*; -use rustc_attr_parsing::{AttributeParser, ShouldEmit}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::ErrorGuaranteed; use rustc_hir as hir; use rustc_hir::attrs::{AttributeKind, InlineAttr}; -use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::def_id::DefId; use rustc_middle::span_bug; -use rustc_middle::ty::{Asyncness, DelegationAttrs, DelegationFnSigAttrs, ResolverAstLowering}; +use rustc_middle::ty::Asyncness; use rustc_span::symbol::kw; -use rustc_span::{DUMMY_SP, Ident, Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use smallvec::SmallVec; use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults}; @@ -79,7 +79,7 @@ struct AttrAdditionInfo { enum AttrAdditionKind { Default { factory: fn(Span) -> hir::Attribute }, - Inherit { flag: DelegationFnSigAttrs, factory: fn(Span, &hir::Attribute) -> hir::Attribute }, + Inherit { factory: fn(Span, &hir::Attribute) -> hir::Attribute }, } const PARENT_ID: hir::ItemLocalId = hir::ItemLocalId::ZERO; @@ -96,7 +96,6 @@ enum AttrAdditionKind { hir::Attribute::Parsed(AttributeKind::MustUse { span, reason }) }, - flag: DelegationFnSigAttrs::MUST_USE, }, }, AttrAdditionInfo { @@ -107,45 +106,11 @@ enum AttrAdditionKind { }, ]; -type DelegationIdsVec = SmallVec<[DefId; 1]>; - -// As delegations can now refer to another delegation, we have a delegation path -// of the following type: reuse (current delegation) <- reuse (delegee_id) <- ... <- reuse <- function (root_function_id). -// In its most basic and widely used form: reuse (current delegation) <- function (delegee_id, root_function_id) -struct DelegationIds { - path: DelegationIdsVec, -} - -impl DelegationIds { - fn new(path: DelegationIdsVec) -> Self { - assert!(!path.is_empty()); - Self { path } - } - - // Id of the first function in (non)local crate that is being reused - fn root_function_id(&self) -> DefId { - *self.path.last().expect("Ids vector can't be empty") - } - - // Id of the first definition which is being reused, - // can be either function, in this case `root_id == delegee_id`, or other delegation - fn delegee_id(&self) -> DefId { - *self.path.first().expect("Ids vector can't be empty") - } -} - -impl<'hir> LoweringContext<'_, 'hir> { +impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { fn is_method(&self, def_id: DefId, span: Span) -> bool { match self.tcx.def_kind(def_id) { DefKind::Fn => false, - DefKind::AssocFn => match def_id.as_local() { - Some(local_def_id) => self - .resolver - .delegation_fn_sigs - .get(&local_def_id) - .is_some_and(|sig| sig.has_self), - None => self.tcx.associated_item(def_id).is_method(), - }, + DefKind::AssocFn => self.tcx.associated_item(def_id).is_method(), _ => span_bug!(span, "unexpected DefKind for delegation item"), } } @@ -158,10 +123,10 @@ pub(crate) fn lower_delegation( let span = self.lower_span(delegation.path.segments.last().unwrap().ident.span); // Delegation can be unresolved in illegal places such as function bodies in extern blocks (see #151356) - let ids = if let Some(delegation_info) = - self.resolver.delegation_infos.get(&self.local_def_id(item_id)) + let sig_id = if let Some(delegation_info) = + self.resolver.delegation_info(self.local_def_id(item_id)) { - self.get_delegation_ids(delegation_info.resolution_node, span) + self.get_sig_id(delegation_info.resolution_node, span) } else { return self.generate_delegation_error( self.dcx().span_delayed_bug( @@ -173,28 +138,16 @@ pub(crate) fn lower_delegation( ); }; - match ids { - Ok(ids) => { - self.add_attrs_if_needed(span, &ids); + match sig_id { + Ok(sig_id) => { + self.add_attrs_if_needed(span, sig_id); - let delegee_id = ids.delegee_id(); - let root_function_id = ids.root_function_id(); + let is_method = self.is_method(sig_id, span); - // `is_method` is used to choose the name of the first parameter (`self` or `arg0`), - // if the original function is not a method (without `self`), then it can not be added - // during chain of reuses, so we use `root_function_id` here - let is_method = self.is_method(root_function_id, span); + let (param_count, c_variadic) = self.param_count(sig_id); - // Here we use `root_function_id` as we can not get params information out of potential delegation reuse, - // we need a function to extract this information - let (param_count, c_variadic) = self.param_count(root_function_id); - - let mut generics = self.lower_delegation_generics( - delegation, - ids.root_function_id(), - item_id, - span, - ); + let mut generics = + self.lower_delegation_generics(delegation, sig_id, item_id, span); let body_id = self.lower_delegation_body( delegation, @@ -205,20 +158,10 @@ pub(crate) fn lower_delegation( span, ); - // Here we use `delegee_id`, as this id will then be used to calculate parent for generics - // inheritance, and we want this id to point on a delegee, not on the original - // function (see https://github.com/rust-lang/rust/issues/150152#issuecomment-3674834654) - let decl = self.lower_delegation_decl( - delegee_id, - param_count, - c_variadic, - span, - &generics, - ); + let decl = + self.lower_delegation_decl(sig_id, param_count, c_variadic, span, &generics); - // Here we pass `root_function_id` as we want to inherit signature (including consts, async) - // from the root function that started delegation - let sig = self.lower_delegation_sig(root_function_id, decl, span); + let sig = self.lower_delegation_sig(sig_id, decl, span); let ident = self.lower_ident(delegation.ident); let generics = self.arena.alloc(hir::Generics { @@ -237,9 +180,9 @@ pub(crate) fn lower_delegation( } } - fn add_attrs_if_needed(&mut self, span: Span, ids: &DelegationIds) { + fn add_attrs_if_needed(&mut self, span: Span, sig_id: DefId) { let new_attrs = - self.create_new_attrs(ATTRS_ADDITIONS, span, ids, self.attrs.get(&PARENT_ID)); + self.create_new_attrs(ATTRS_ADDITIONS, span, sig_id, self.attrs.get(&PARENT_ID)); if new_attrs.is_empty() { return; @@ -259,15 +202,9 @@ fn create_new_attrs( &self, candidate_additions: &[AttrAdditionInfo], span: Span, - ids: &DelegationIds, + sig_id: DefId, existing_attrs: Option<&&[hir::Attribute]>, ) -> Vec { - let defs_orig_attrs = ids - .path - .iter() - .map(|def_id| (*def_id, self.parse_local_original_attrs(*def_id))) - .collect::>(); - candidate_additions .iter() .filter_map(|addition_info| { @@ -281,83 +218,22 @@ fn create_new_attrs( match addition_info.kind { AttrAdditionKind::Default { factory } => Some(factory(span)), - AttrAdditionKind::Inherit { flag, factory } => { - for (def_id, orig_attrs) in &defs_orig_attrs { - let original_attr = match def_id.as_local() { - Some(local_id) => self - .get_attrs(local_id) - .flags - .contains(flag) - .then(|| { - orig_attrs - .as_ref() - .map(|attrs| { - attrs.iter().find(|base_attr| { - (addition_info.equals)(base_attr) - }) - }) - .flatten() - }) - .flatten(), - None => - { - #[allow(deprecated)] - self.tcx - .get_all_attrs(*def_id) - .iter() - .find(|base_attr| (addition_info.equals)(base_attr)) - } - }; - - if let Some(original_attr) = original_attr { - return Some(factory(span, original_attr)); - } - } - - None + AttrAdditionKind::Inherit { factory, .. } => + { + #[allow(deprecated)] + self.tcx + .get_all_attrs(sig_id) + .iter() + .find_map(|a| (addition_info.equals)(a).then(|| factory(span, a))) } } }) .collect::>() } - fn parse_local_original_attrs(&self, def_id: DefId) -> Option> { - if let Some(local_id) = def_id.as_local() { - let attrs = &self.get_attrs(local_id).to_inherit; - - if !attrs.is_empty() { - return Some(AttributeParser::parse_limited_all( - self.tcx.sess, - attrs, - None, - hir::Target::Fn, - DUMMY_SP, - DUMMY_NODE_ID, - Some(self.tcx.features()), - ShouldEmit::Nothing, - )); - } - } - - None - } - - fn get_attrs(&self, local_id: LocalDefId) -> &DelegationAttrs { - // local_id can correspond either to a function or other delegation - if let Some(fn_sig) = self.resolver.delegation_fn_sigs.get(&local_id) { - &fn_sig.attrs - } else { - &self.resolver.delegation_infos[&local_id].attrs - } - } - - fn get_delegation_ids( - &self, - mut node_id: NodeId, - span: Span, - ) -> Result { + fn get_sig_id(&self, mut node_id: NodeId, span: Span) -> Result { let mut visited: FxHashSet = Default::default(); - let mut path: DelegationIdsVec = Default::default(); + let mut path: SmallVec<[DefId; 1]> = Default::default(); loop { visited.insert(node_id); @@ -378,7 +254,7 @@ fn get_delegation_ids( // it means that we refer to another delegation as a callee, so in order to obtain // a signature DefId we obtain NodeId of the callee delegation and try to get signature from it. if let Some(local_id) = def_id.as_local() - && let Some(delegation_info) = self.resolver.delegation_infos.get(&local_id) + && let Some(delegation_info) = self.resolver.delegation_info(local_id) { node_id = delegation_info.resolution_node; if visited.contains(&node_id) { @@ -390,7 +266,7 @@ fn get_delegation_ids( }); } } else { - return Ok(DelegationIds::new(path)); + return Ok(path[0]); } } } @@ -401,15 +277,8 @@ fn get_resolution_id(&self, node_id: NodeId) -> Option { // Function parameter count, including C variadic `...` if present. fn param_count(&self, def_id: DefId) -> (usize, bool /*c_variadic*/) { - if let Some(local_sig_id) = def_id.as_local() { - match self.resolver.delegation_fn_sigs.get(&local_sig_id) { - Some(sig) => (sig.param_count, sig.c_variadic), - None => (0, false), - } - } else { - let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder(); - (sig.inputs().len() + usize::from(sig.c_variadic), sig.c_variadic) - } + let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder(); + (sig.inputs().len() + usize::from(sig.c_variadic), sig.c_variadic) } fn lower_delegation_decl( @@ -456,41 +325,21 @@ fn lower_delegation_sig( decl: &'hir hir::FnDecl<'hir>, span: Span, ) -> hir::FnSig<'hir> { - let header = if let Some(local_sig_id) = sig_id.as_local() { - match self.resolver.delegation_fn_sigs.get(&local_sig_id) { - Some(sig) => { - let parent = self.tcx.parent(sig_id); - // HACK: we override the default safety instead of generating attributes from the ether. - // We are not forwarding the attributes, as the delegation fn sigs are collected on the ast, - // and here we need the hir attributes. - let default_safety = - if sig.attrs.flags.contains(DelegationFnSigAttrs::TARGET_FEATURE) - || self.tcx.def_kind(parent) == DefKind::ForeignMod - { - hir::Safety::Unsafe - } else { - hir::Safety::Safe - }; - self.lower_fn_header(sig.header, default_safety, &[]) - } - None => self.generate_header_error(), - } - } else { - let sig = self.tcx.fn_sig(sig_id).skip_binder().skip_binder(); - let asyncness = match self.tcx.asyncness(sig_id) { - Asyncness::Yes => hir::IsAsync::Async(span), - Asyncness::No => hir::IsAsync::NotAsync, - }; - hir::FnHeader { - safety: if self.tcx.codegen_fn_attrs(sig_id).safe_target_features { - hir::HeaderSafety::SafeTargetFeatures - } else { - hir::HeaderSafety::Normal(sig.safety) - }, - constness: self.tcx.constness(sig_id), - asyncness, - abi: sig.abi, - } + let sig = self.tcx.fn_sig(sig_id).skip_binder().skip_binder(); + let asyncness = match self.tcx.asyncness(sig_id) { + Asyncness::Yes => hir::IsAsync::Async(span), + Asyncness::No => hir::IsAsync::NotAsync, + }; + + let header = hir::FnHeader { + safety: if self.tcx.codegen_fn_attrs(sig_id).safe_target_features { + hir::HeaderSafety::SafeTargetFeatures + } else { + hir::HeaderSafety::Normal(sig.safety) + }, + constness: self.tcx.constness(sig_id), + asyncness, + abi: sig.abi, }; hir::FnSig { decl, header, span } @@ -573,6 +422,7 @@ fn lower_delegation_body( resolver: this.resolver, path_id: delegation.id, self_param_id: pat_node_id, + phantom: PhantomData, }; self_resolver.visit_block(block); // Target expr needs to lower `self` path. @@ -818,25 +668,26 @@ fn mk_expr(&mut self, kind: hir::ExprKind<'hir>, span: Span) -> hir::Expr<'hir> } } -struct SelfResolver<'a, 'tcx> { - resolver: &'a mut ResolverAstLowering<'tcx>, +struct SelfResolver<'a, 'tcx, R> { + resolver: &'a mut R, path_id: NodeId, self_param_id: NodeId, + phantom: PhantomData<&'tcx ()>, } -impl SelfResolver<'_, '_> { +impl<'tcx, R: ResolverAstLoweringExt<'tcx>> SelfResolver<'_, 'tcx, R> { fn try_replace_id(&mut self, id: NodeId) { - if let Some(res) = self.resolver.partial_res_map.get(&id) + if let Some(res) = self.resolver.get_partial_res(id) && let Some(Res::Local(sig_id)) = res.full_res() && sig_id == self.path_id { let new_res = PartialRes::new(Res::Local(self.self_param_id)); - self.resolver.partial_res_map.insert(id, new_res); + self.resolver.insert_partial_res(id, new_res); } } } -impl<'ast, 'a> Visitor<'ast> for SelfResolver<'a, '_> { +impl<'ast, 'a, 'tcx, R: ResolverAstLoweringExt<'tcx>> Visitor<'ast> for SelfResolver<'a, 'tcx, R> { fn visit_id(&mut self, id: NodeId) { self.try_replace_id(id); } diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index 6bfe73fb0d8f..d6d8819d8c47 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -7,10 +7,10 @@ use rustc_middle::{bug, ty}; use rustc_span::sym::{self}; use rustc_span::symbol::kw; -use rustc_span::{DUMMY_SP, Ident, Span}; +use rustc_span::{Ident, Span}; use thin_vec::{ThinVec, thin_vec}; -use crate::{AstOwner, LoweringContext}; +use crate::{LoweringContext, ResolverAstLoweringExt}; pub(super) enum DelegationGenerics { /// User-specified args are present: `reuse foo::;`. @@ -81,7 +81,7 @@ fn args_propagation_details(&self) -> GenericArgsPropagationDetails { impl<'hir> HirOrAstGenerics<'hir> { pub(super) fn into_hir_generics( &mut self, - ctx: &mut LoweringContext<'_, 'hir>, + ctx: &mut LoweringContext<'_, 'hir, impl ResolverAstLoweringExt<'hir>>, item_id: NodeId, span: Span, ) -> &mut HirOrAstGenerics<'hir> { @@ -128,7 +128,7 @@ fn hir_generics_or_empty(&self) -> &'hir hir::Generics<'hir> { pub(super) fn into_generic_args( &self, - ctx: &mut LoweringContext<'_, 'hir>, + ctx: &mut LoweringContext<'_, 'hir, impl ResolverAstLoweringExt<'hir>>, add_lifetimes: bool, span: Span, ) -> Option<&'hir hir::GenericArgs<'hir>> { @@ -169,7 +169,7 @@ pub(super) fn all_params( &mut self, item_id: NodeId, span: Span, - ctx: &mut LoweringContext<'_, 'hir>, + ctx: &mut LoweringContext<'_, 'hir, impl ResolverAstLoweringExt<'hir>>, ) -> impl Iterator> { // Now we always call `into_hir_generics` both on child and parent, // however in future we would not do that, when scenarios like @@ -211,7 +211,7 @@ pub(super) fn all_predicates( &mut self, item_id: NodeId, span: Span, - ctx: &mut LoweringContext<'_, 'hir>, + ctx: &mut LoweringContext<'_, 'hir, impl ResolverAstLoweringExt<'hir>>, ) -> impl Iterator> { // Now we always call `into_hir_generics` both on child and parent, // however in future we would not do that, when scenarios like @@ -236,11 +236,11 @@ pub(super) fn all_predicates( } } -impl<'hir> LoweringContext<'_, 'hir> { +impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { pub(super) fn lower_delegation_generics( &mut self, delegation: &Delegation, - root_fn_id: DefId, + sig_id: DefId, item_id: NodeId, span: Span, ) -> GenericsGenerationResults<'hir> { @@ -258,7 +258,7 @@ pub(super) fn lower_delegation_generics( // we will take those args that are in trait impl header trait ref. let parent = GenericsGenerationResult::new(DelegationGenerics::Default(None)); - let generics = self.get_fn_like_generics(root_fn_id, span); + let generics = self.get_external_generics(sig_id, false, span); let child = DelegationGenerics::TraitImpl(generics, child_user_specified); let child = GenericsGenerationResult::new(child); @@ -269,17 +269,12 @@ pub(super) fn lower_delegation_generics( !matches!(delegation_parent_kind, DefKind::Trait | DefKind::Impl { .. }); let root_function_in_trait = - matches!(self.tcx.def_kind(self.tcx.parent(root_fn_id)), DefKind::Trait); + matches!(self.tcx.def_kind(self.tcx.parent(sig_id)), DefKind::Trait); let generate_self = delegation_in_free_ctx && root_function_in_trait; let parent_generics_factory = |this: &mut Self, user_specified: bool| { - this.get_parent_generics( - this.tcx.parent(root_fn_id), - generate_self, - user_specified, - span, - ) + this.get_parent_generics(this.tcx.parent(sig_id), generate_self, user_specified, span) }; let can_add_generics_to_parent = len >= 2 @@ -304,7 +299,7 @@ pub(super) fn lower_delegation_generics( let child_generics = if child_user_specified { DelegationGenerics::UserSpecified } else { - DelegationGenerics::Default(self.get_fn_like_generics(root_fn_id, span)) + DelegationGenerics::Default(self.get_external_generics(sig_id, false, span)) }; GenericsGenerationResults { @@ -334,11 +329,11 @@ fn lower_delegation_generic_params( // Note that we use self.disambiguator here, if we will create new every time // we will get ICE if params have the same name. - self.resolver.node_id_to_def_id.insert( + self.resolver.insert_new_def_id( p.id, self.tcx .create_def( - self.resolver.node_id_to_def_id[&item_id], + self.local_def_id(item_id), Some(p.ident.name), match p.kind { GenericParamKind::Lifetime => DefKind::LifetimeParam, @@ -363,11 +358,9 @@ fn lower_delegation_generic_params( // FIXME(fn_delegation): proper support for late bound lifetimes. self.arena.alloc(hir::Generics { params, - predicates: self.arena.alloc_from_iter( - params - .iter() - .filter_map(|p| p.is_lifetime().then(|| self.generate_lifetime_predicate(p))), - ), + predicates: self.arena.alloc_from_iter(params.iter().filter_map(|p| { + p.is_lifetime().then(|| self.generate_lifetime_predicate(p, span)) + })), has_where_clause_predicates: false, where_clause_span: span, span, @@ -377,6 +370,7 @@ fn lower_delegation_generic_params( fn generate_lifetime_predicate( &mut self, p: &hir::GenericParam<'hir>, + span: Span, ) -> hir::WherePredicate<'hir> { let create_lifetime = |this: &mut Self| -> &'hir hir::Lifetime { this.arena.alloc(hir::Lifetime { @@ -392,7 +386,7 @@ fn generate_lifetime_predicate( hir::WherePredicate { hir_id: self.next_id(), - span: DUMMY_SP, + span, kind: self.arena.alloc(hir::WherePredicateKind::RegionPredicate( hir::WhereRegionPredicate { in_where_clause: true, @@ -479,22 +473,6 @@ fn create_generics_args_from_params( }) } - fn get_fn_like_generics(&mut self, id: DefId, span: Span) -> Option { - if let Some(local_id) = id.as_local() { - match self.ast_index.get(local_id) { - Some(AstOwner::Item(item)) if let ItemKind::Fn(f) = &item.kind => { - Some(f.generics.clone()) - } - Some(AstOwner::AssocItem(item, _)) if let AssocItemKind::Fn(f) = &item.kind => { - Some(f.generics.clone()) - } - _ => None, - } - } else { - self.get_external_generics(id, false, span) - } - } - fn get_external_generics( &mut self, id: DefId, @@ -519,7 +497,7 @@ fn get_external_generics( bounds: Default::default(), colon_span: None, id: self.next_node_id(), - ident: Ident::with_dummy_span(p.name), + ident: Ident::new(p.name, span), is_placeholder: false, kind: match p.kind { GenericParamDefKind::Lifetime => GenericParamKind::Lifetime, @@ -531,7 +509,7 @@ fn get_external_generics( }) .collect(), where_clause: Default::default(), - span: DUMMY_SP, + span, }) } @@ -554,7 +532,7 @@ fn map_const_kind(&mut self, p: &ty::GenericParamDef, span: Span) -> GenericPara let node_id = self.next_node_id(); - self.resolver.partial_res_map.insert(node_id, hir::def::PartialRes::new(res)); + self.resolver.insert_partial_res(node_id, hir::def::PartialRes::new(res)); GenericParamKind::Const { ty: Box::new(Ty { @@ -563,18 +541,18 @@ fn map_const_kind(&mut self, p: &ty::GenericParamDef, span: Span) -> GenericPara None, Path { segments: thin_vec![PathSegment { - ident: Ident::with_dummy_span(type_symbol), + ident: Ident::new(type_symbol, span), id: self.next_node_id(), args: None }], - span: DUMMY_SP, + span, tokens: None, }, ), - span: DUMMY_SP, + span, tokens: None, }), - span: DUMMY_SP, + span, default: None, } } @@ -587,28 +565,15 @@ fn get_parent_generics( span: Span, ) -> Option { // If args are user-specified we still maybe need to add self. - let mut generics = if user_specified { - None - } else { - if let Some(local_id) = id.as_local() { - if let Some(AstOwner::Item(item)) = self.ast_index.get(local_id) - && matches!(item.kind, ItemKind::Trait(..)) - { - item.opt_generics().cloned() - } else { - None - } - } else { - self.get_external_generics(id, true, span) - } - }; + let mut generics = + if user_specified { None } else { self.get_external_generics(id, true, span) }; if add_self { generics.get_or_insert_default().params.insert( 0, GenericParam { id: self.next_node_id(), - ident: Ident::new(kw::SelfUpper, DUMMY_SP), + ident: Ident::new(kw::SelfUpper, span), attrs: Default::default(), bounds: vec![], is_placeholder: false, diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 5032d7d6fbb6..920ca8a6d06c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -52,7 +52,7 @@ fn visit_expr(&mut self, ex: &'v Expr) -> Self::Result { } } -impl<'hir> LoweringContext<'_, 'hir> { +impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { fn lower_exprs(&mut self, exprs: &[Box]) -> &'hir [hir::Expr<'hir>] { self.arena.alloc_from_iter(exprs.iter().map(|x| self.lower_expr_mut(x))) } @@ -1244,7 +1244,10 @@ fn lower_expr_assign( whole_span: Span, ) -> hir::ExprKind<'hir> { // Return early in case of an ordinary assignment. - fn is_ordinary(lower_ctx: &mut LoweringContext<'_, '_>, lhs: &Expr) -> bool { + fn is_ordinary<'hir>( + lower_ctx: &mut LoweringContext<'_, 'hir, impl ResolverAstLoweringExt<'hir>>, + lhs: &Expr, + ) -> bool { match &lhs.kind { ExprKind::Array(..) | ExprKind::Struct(..) diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index 602635af1324..1f1f86f7edfd 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -7,8 +7,9 @@ use rustc_span::{ByteSymbol, DesugaringKind, Ident, Span, Symbol, sym}; use super::LoweringContext; +use crate::ResolverAstLoweringExt; -impl<'hir> LoweringContext<'_, 'hir> { +impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> { // Never call the const constructor of `fmt::Arguments` if the // format_args!() had any arguments _before_ flattening/inlining. @@ -230,7 +231,7 @@ enum ArgumentType { /// ::new_…(arg) /// ``` fn make_argument<'hir>( - ctx: &mut LoweringContext<'_, 'hir>, + ctx: &mut LoweringContext<'_, 'hir, impl ResolverAstLoweringExt<'hir>>, sp: Span, arg: &'hir hir::Expr<'hir>, ty: ArgumentType, @@ -277,7 +278,7 @@ fn make_count( } fn expand_format_args<'hir>( - ctx: &mut LoweringContext<'_, 'hir>, + ctx: &mut LoweringContext<'_, 'hir, impl ResolverAstLoweringExt<'hir>>, macsp: Span, fmt: &FormatArgs, allow_const: bool, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index e2ac38caf295..fa103099e643 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -3,6 +3,7 @@ use rustc_abi::ExternAbi; use rustc_ast::visit::AssocCtxt; use rustc_ast::*; +use rustc_data_structures::fx::FxIndexMap; use rustc_errors::{E0570, ErrorGuaranteed, struct_span_code_err}; use rustc_hir::attrs::{AttributeKind, EiiImplResolution}; use rustc_hir::def::{DefKind, PerNS, Res}; @@ -12,7 +13,7 @@ }; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::span_bug; -use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; +use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym}; @@ -27,11 +28,31 @@ RelaxedBoundForbiddenReason, RelaxedBoundPolicy, ResolverAstLoweringExt, }; -pub(super) struct ItemLowerer<'a, 'hir> { +/// Wraps either IndexVec (during `hir_crate`), which acts like a primary +/// storage for most of the MaybeOwners, or FxIndexMap during delayed AST -> HIR +/// lowering of delegations (`lower_delayed_owner`), +/// in this case we can not modify already created IndexVec, so we use other map. +pub(super) enum Owners<'a, 'hir> { + IndexVec(&'a mut IndexVec>), + Map(&'a mut FxIndexMap>), +} + +impl<'hir> Owners<'_, 'hir> { + fn get_or_insert_mut(&mut self, def_id: LocalDefId) -> &mut hir::MaybeOwner<'hir> { + match self { + Owners::IndexVec(index_vec) => { + index_vec.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom) + } + Owners::Map(map) => map.entry(def_id).or_insert(hir::MaybeOwner::Phantom), + } + } +} + +pub(super) struct ItemLowerer<'a, 'hir, R> { pub(super) tcx: TyCtxt<'hir>, - pub(super) resolver: &'a mut ResolverAstLowering<'hir>, + pub(super) resolver: &'a mut R, pub(super) ast_index: &'a IndexSlice>, - pub(super) owners: &'a mut IndexVec>, + pub(super) owners: Owners<'a, 'hir>, } /// When we have a ty alias we *may* have two where clauses. To give the best diagnostics, we set the span @@ -53,17 +74,17 @@ fn add_ty_alias_where_clause( if before.0 || !after.0 { before } else { after }; } -impl<'a, 'hir> ItemLowerer<'a, 'hir> { +impl<'hir, R: ResolverAstLoweringExt<'hir>> ItemLowerer<'_, 'hir, R> { fn with_lctx( &mut self, owner: NodeId, - f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>, + f: impl FnOnce(&mut LoweringContext<'_, 'hir, R>) -> hir::OwnerNode<'hir>, ) { - let mut lctx = LoweringContext::new(self.tcx, self.ast_index, self.resolver); + let mut lctx = LoweringContext::new(self.tcx, self.resolver); lctx.with_hir_id_owner(owner, |lctx| f(lctx)); for (def_id, info) in lctx.children { - let owner = self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom); + let owner = self.owners.get_or_insert_mut(def_id); assert!( matches!(owner, hir::MaybeOwner::Phantom), "duplicate copy of {def_id:?} in lctx.children" @@ -73,13 +94,13 @@ fn with_lctx( } pub(super) fn lower_node(&mut self, def_id: LocalDefId) { - let owner = self.owners.ensure_contains_elem(def_id, || hir::MaybeOwner::Phantom); + let owner = self.owners.get_or_insert_mut(def_id); if let hir::MaybeOwner::Phantom = owner { let node = self.ast_index[def_id]; match node { AstOwner::NonOwner => {} AstOwner::Crate(c) => { - assert_eq!(self.resolver.node_id_to_def_id[&CRATE_NODE_ID], CRATE_DEF_ID); + assert_eq!(self.resolver.local_def_id(CRATE_NODE_ID), CRATE_DEF_ID); self.with_lctx(CRATE_NODE_ID, |lctx| { let module = lctx.lower_mod(&c.items, &c.spans); // FIXME(jdonszelman): is dummy span ever a problem here? @@ -101,7 +122,7 @@ pub(super) fn lower_node(&mut self, def_id: LocalDefId) { } } -impl<'hir> LoweringContext<'_, 'hir> { +impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { pub(super) fn lower_mod( &mut self, items: &[Box], @@ -1491,7 +1512,7 @@ fn lower_maybe_coroutine_body( pub(crate) fn lower_coroutine_body_with_moved_arguments( &mut self, decl: &FnDecl, - lower_body: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::Expr<'hir>, + lower_body: impl FnOnce(&mut LoweringContext<'_, 'hir, R>) -> hir::Expr<'hir>, fn_decl_span: Span, body_span: Span, coroutine_kind: CoroutineKind, @@ -1628,7 +1649,7 @@ pub(crate) fn lower_coroutine_body_with_moved_arguments( parameters.push(new_parameter); } - let mkbody = |this: &mut LoweringContext<'_, 'hir>| { + let mkbody = |this: &mut LoweringContext<'_, 'hir, R>| { // Create a block from the user's function body: let user_body = lower_body(this); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index e8092b540ec8..a2be51cfa31b 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -42,9 +42,10 @@ use rustc_ast::{self as ast, *}; use rustc_attr_parsing::{AttributeParser, Late, OmitDoc}; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; -use rustc_data_structures::sync::spawn; +use rustc_data_structures::steal::Steal; use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle}; use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res}; @@ -57,8 +58,9 @@ }; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_macros::extension; +use rustc_middle::hir::{self as mid_hir}; use rustc_middle::span_bug; -use rustc_middle::ty::{ResolverAstLowering, TyCtxt}; +use rustc_middle::ty::{DelegationInfo, ResolverAstLowering, TyCtxt}; use rustc_session::parse::add_feature_diagnostics; use rustc_span::symbol::{Ident, Symbol, kw, sym}; use rustc_span::{DUMMY_SP, DesugaringKind, Span}; @@ -67,6 +69,7 @@ use tracing::{debug, instrument, trace}; use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait}; +use crate::item::Owners; macro_rules! arena_vec { ($this:expr; $($x:expr),*) => ( @@ -87,18 +90,9 @@ macro_rules! arena_vec { mod path; pub mod stability; -struct LoweringContext<'a, 'hir> { +struct LoweringContext<'a, 'hir, R> { tcx: TyCtxt<'hir>, - - // During lowering of delegation we need to access AST of other functions - // in order to properly propagate generics, we could have done it at resolve - // stage, however it will require either to firstly identify functions that - // are being reused and store their generics, or to store generics of all functions - // in resolver. This approach helps with those problems, as functions that are reused - // will be in AST index. - ast_index: &'a IndexSlice>, - - resolver: &'a mut ResolverAstLowering<'hir>, + resolver: &'a mut R, disambiguator: DisambiguatorState, /// Used to allocate HIR nodes. @@ -158,17 +152,11 @@ struct LoweringContext<'a, 'hir> { attribute_parser: AttributeParser<'hir>, } -impl<'a, 'hir> LoweringContext<'a, 'hir> { - fn new( - tcx: TyCtxt<'hir>, - ast_index: &'a IndexSlice>, - resolver: &'a mut ResolverAstLowering<'hir>, - ) -> Self { +impl<'a, 'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'a, 'hir, R> { + fn new(tcx: TyCtxt<'hir>, resolver: &'a mut R) -> Self { let registered_tools = tcx.registered_tools(()).iter().map(|x| x.name).collect(); Self { - // Pseudo-globals. tcx, - ast_index, resolver, disambiguator: DisambiguatorState::new(), arena: tcx.hir_arena, @@ -248,9 +236,84 @@ fn lower(&self, span: Span) -> Span { } } -#[extension(trait ResolverAstLoweringExt)] -impl ResolverAstLowering<'_> { - fn legacy_const_generic_args(&self, expr: &Expr, tcx: TyCtxt<'_>) -> Option> { +struct ResolverDelayedAstLowering<'a, 'tcx> { + node_id_to_def_id: NodeMap, + partial_res_map: NodeMap, + next_node_id: NodeId, + base: &'a ResolverAstLowering<'tcx>, +} + +// FIXME(fn_delegation): delegate this trait impl to `self.base` +impl<'a, 'tcx> ResolverAstLoweringExt<'tcx> for ResolverDelayedAstLowering<'a, 'tcx> { + fn legacy_const_generic_args(&self, expr: &Expr, tcx: TyCtxt<'tcx>) -> Option> { + self.base.legacy_const_generic_args(expr, tcx) + } + + fn get_partial_res(&self, id: NodeId) -> Option { + self.partial_res_map.get(&id).copied().or_else(|| self.base.get_partial_res(id)) + } + + fn get_import_res(&self, id: NodeId) -> PerNS>> { + self.base.get_import_res(id) + } + + fn get_label_res(&self, id: NodeId) -> Option { + self.base.get_label_res(id) + } + + fn get_lifetime_res(&self, id: NodeId) -> Option { + self.base.get_lifetime_res(id) + } + + fn extra_lifetime_params(&self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> { + self.base.extra_lifetime_params(id) + } + + fn delegation_info(&self, id: LocalDefId) -> Option<&DelegationInfo> { + self.base.delegation_info(id) + } + + fn opt_local_def_id(&self, id: NodeId) -> Option { + self.node_id_to_def_id.get(&id).copied().or_else(|| self.base.opt_local_def_id(id)) + } + + fn local_def_id(&self, id: NodeId) -> LocalDefId { + self.opt_local_def_id(id).expect("must have def_id") + } + + fn lifetime_elision_allowed(&self, id: NodeId) -> bool { + self.base.lifetime_elision_allowed(id) + } + + fn insert_new_def_id(&mut self, node_id: NodeId, def_id: LocalDefId) { + self.node_id_to_def_id.insert(node_id, def_id); + } + + fn insert_partial_res(&mut self, node_id: NodeId, res: PartialRes) { + self.partial_res_map.insert(node_id, res); + } + + fn trait_candidates(&self, node_id: NodeId) -> Option<&'tcx [hir::TraitCandidate<'tcx>]> { + self.base.trait_candidates(node_id) + } + + #[inline] + fn next_node_id(&mut self) -> NodeId { + next_node_id(&mut self.next_node_id) + } +} + +fn next_node_id(current_id: &mut NodeId) -> NodeId { + let start = *current_id; + let next = start.as_u32().checked_add(1).expect("input too large; ran out of NodeIds"); + *current_id = ast::NodeId::from_u32(next); + + start +} + +#[extension(trait ResolverAstLoweringExt<'tcx>)] +impl<'tcx> ResolverAstLowering<'tcx> { + fn legacy_const_generic_args(&self, expr: &Expr, tcx: TyCtxt<'tcx>) -> Option> { let ExprKind::Path(None, path) = &expr.kind else { return None; }; @@ -261,7 +324,7 @@ fn legacy_const_generic_args(&self, expr: &Expr, tcx: TyCtxt<'_>) -> Option Option { /// /// The extra lifetimes that appear from the parenthesized `Fn`-trait desugaring /// should appear at the enclosing `PolyTraitRef`. - fn extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> { + fn extra_lifetime_params(&self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> { self.extra_lifetime_params_map.get(&id).cloned().unwrap_or_default() } + + fn delegation_info(&self, id: LocalDefId) -> Option<&DelegationInfo> { + self.delegation_infos.get(&id) + } + + fn opt_local_def_id(&self, id: NodeId) -> Option { + self.node_id_to_def_id.get(&id).copied() + } + + fn local_def_id(&self, id: NodeId) -> LocalDefId { + self.opt_local_def_id(id).expect("must have def_id") + } + + fn lifetime_elision_allowed(&self, id: NodeId) -> bool { + self.lifetime_elision_allowed.contains(&id) + } + + fn insert_new_def_id(&mut self, node_id: NodeId, def_id: LocalDefId) { + self.node_id_to_def_id.insert(node_id, def_id); + } + + fn insert_partial_res(&mut self, node_id: NodeId, res: PartialRes) { + self.partial_res_map.insert(node_id, res); + } + + fn trait_candidates(&self, node_id: NodeId) -> Option<&'tcx [hir::TraitCandidate<'tcx>]> { + self.trait_map.get(&node_id).copied() + } + + #[inline] + fn next_node_id(&mut self) -> NodeId { + next_node_id(&mut self.next_node_id) + } } /// How relaxed bounds `?Trait` should be treated. @@ -446,42 +542,43 @@ enum TryBlockScope { Heterogeneous(HirId), } -fn index_crate<'a>( - node_id_to_def_id: &NodeMap, +fn index_crate<'a, 'b>( + resolver: &'b impl ResolverAstLoweringExt<'a>, krate: &'a Crate, ) -> IndexVec> { - let mut indexer = Indexer { node_id_to_def_id, index: IndexVec::new() }; + let mut indexer = Indexer { resolver, index: IndexVec::new() }; *indexer.index.ensure_contains_elem(CRATE_DEF_ID, || AstOwner::NonOwner) = AstOwner::Crate(krate); visit::walk_crate(&mut indexer, krate); + return indexer.index; - struct Indexer<'s, 'a> { - node_id_to_def_id: &'s NodeMap, + struct Indexer<'a, 'b, R> { + resolver: &'b R, index: IndexVec>, } - impl<'a> visit::Visitor<'a> for Indexer<'_, 'a> { + impl<'a, 'b, R: ResolverAstLoweringExt<'a>> visit::Visitor<'a> for Indexer<'a, 'b, R> { fn visit_attribute(&mut self, _: &'a Attribute) { // We do not want to lower expressions that appear in attributes, // as they are not accessible to the rest of the HIR. } fn visit_item(&mut self, item: &'a ast::Item) { - let def_id = self.node_id_to_def_id[&item.id]; + let def_id = self.resolver.local_def_id(item.id); *self.index.ensure_contains_elem(def_id, || AstOwner::NonOwner) = AstOwner::Item(item); visit::walk_item(self, item) } fn visit_assoc_item(&mut self, item: &'a ast::AssocItem, ctxt: visit::AssocCtxt) { - let def_id = self.node_id_to_def_id[&item.id]; + let def_id = self.resolver.local_def_id(item.id); *self.index.ensure_contains_elem(def_id, || AstOwner::NonOwner) = AstOwner::AssocItem(item, ctxt); visit::walk_assoc_item(self, item, ctxt); } fn visit_foreign_item(&mut self, item: &'a ast::ForeignItem) { - let def_id = self.node_id_to_def_id[&item.id]; + let def_id = self.resolver.local_def_id(item.id); *self.index.ensure_contains_elem(def_id, || AstOwner::NonOwner) = AstOwner::ForeignItem(item); visit::walk_item(self, item); @@ -512,8 +609,7 @@ fn compute_hir_hash( }) } -pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> { - let sess = tcx.sess; +pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> mid_hir::Crate<'_> { // Queries that borrow `resolver_for_lowering`. tcx.ensure_done().output_filenames(()); tcx.ensure_done().early_lint_checks(()); @@ -521,7 +617,7 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> { tcx.ensure_done().get_lang_items(()); let (mut resolver, krate) = tcx.resolver_for_lowering().steal(); - let ast_index = index_crate(&resolver.node_id_to_def_id, &krate); + let ast_index = index_crate(&resolver, &krate); let mut owners = IndexVec::from_fn_n( |_| hir::MaybeOwner::Phantom, tcx.definitions_untracked().def_index_count(), @@ -531,25 +627,60 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> { tcx, resolver: &mut resolver, ast_index: &ast_index, - owners: &mut owners, + owners: Owners::IndexVec(&mut owners), }; + + let mut delayed_ids: FxIndexSet = Default::default(); + for def_id in ast_index.indices() { - lowerer.lower_node(def_id); + match &ast_index[def_id] { + AstOwner::Item(Item { kind: ItemKind::Delegation { .. }, .. }) + | AstOwner::AssocItem(Item { kind: AssocItemKind::Delegation { .. }, .. }, _) => { + delayed_ids.insert(def_id); + } + _ => lowerer.lower_node(def_id), + }; } - drop(ast_index); - - // Drop AST to free memory. It can be expensive so try to drop it on a separate thread. - let prof = sess.prof.clone(); - spawn(move || { - let _timer = prof.verbose_generic_activity("drop_ast"); - drop(krate); - }); - // Don't hash unless necessary, because it's expensive. let opt_hir_hash = if tcx.needs_crate_hash() { Some(compute_hir_hash(tcx, &owners)) } else { None }; - hir::Crate { owners, opt_hir_hash } + + let delayed_resolver = Steal::new((resolver, krate)); + mid_hir::Crate::new(owners, delayed_ids, delayed_resolver, opt_hir_hash) +} + +/// Lowers an AST owner corresponding to `def_id`, now only delegations are lowered this way. +pub fn lower_delayed_owner(tcx: TyCtxt<'_>, def_id: LocalDefId) { + let krate = tcx.hir_crate(()); + + let (resolver, krate) = &*krate.delayed_resolver.borrow(); + + // FIXME!!!(fn_delegation): make ast index lifetime same as resolver, + // as it is too bad to reindex whole crate on each delegation lowering. + let ast_index = index_crate(resolver, krate); + + let mut resolver = ResolverDelayedAstLowering { + next_node_id: resolver.next_node_id, + partial_res_map: Default::default(), + node_id_to_def_id: Default::default(), + base: resolver, + }; + + let mut map = Default::default(); + + let mut lowerer = item::ItemLowerer { + tcx, + resolver: &mut resolver, + ast_index: &ast_index, + owners: Owners::Map(&mut map), + }; + + lowerer.lower_node(def_id); + + for (&child_def_id, &owner) in &map { + tcx.feed_delayed_owner(child_def_id, owner); + } } #[derive(Copy, Clone, PartialEq, Debug)] @@ -579,7 +710,7 @@ enum GenericArgsMode { Silence, } -impl<'a, 'hir> LoweringContext<'a, 'hir> { +impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { fn create_def( &mut self, node_id: ast::NodeId, @@ -605,22 +736,19 @@ fn create_def( .def_id(); debug!("create_def: def_id_to_node_id[{:?}] <-> {:?}", def_id, node_id); - self.resolver.node_id_to_def_id.insert(node_id, def_id); + self.resolver.insert_new_def_id(node_id, def_id); def_id } fn next_node_id(&mut self) -> NodeId { - let start = self.resolver.next_node_id; - let next = start.as_u32().checked_add(1).expect("input too large; ran out of NodeIds"); - self.resolver.next_node_id = ast::NodeId::from_u32(next); - start + self.resolver.next_node_id() } /// Given the id of some node in the AST, finds the `LocalDefId` associated with it by the name /// resolver (if any). fn opt_local_def_id(&self, node: NodeId) -> Option { - self.resolver.node_id_to_def_id.get(&node).copied() + self.resolver.opt_local_def_id(node) } fn local_def_id(&self, node: NodeId) -> LocalDefId { @@ -749,7 +877,7 @@ fn lower_node_id(&mut self, ast_node_id: NodeId) -> HirId { self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id))); } - if let Some(&traits) = self.resolver.trait_map.get(&ast_node_id) { + if let Some(traits) = self.resolver.trait_candidates(ast_node_id) { self.trait_map.insert(hir_id.local_id, traits); } @@ -1269,9 +1397,13 @@ fn lower_generic_arg( } GenericArg::Type(self.lower_ty_alloc(ty, itctx).try_as_ambig_ty().unwrap()) } - ast::GenericArg::Const(ct) => GenericArg::Const( - self.lower_anon_const_to_const_arg_and_alloc(ct).try_as_ambig_ct().unwrap(), - ), + ast::GenericArg::Const(ct) => { + let ct = self.lower_anon_const_to_const_arg_and_alloc(ct); + match ct.try_as_ambig_ct() { + Some(ct) => GenericArg::Const(ct), + None => GenericArg::Infer(hir::InferArg { hir_id: ct.hir_id, span: ct.span }), + } + } } } @@ -1766,7 +1898,7 @@ fn lower_fn_decl( inputs, output, c_variadic, - lifetime_elision_allowed: self.resolver.lifetime_elision_allowed.contains(&fn_node_id), + lifetime_elision_allowed: self.resolver.lifetime_elision_allowed(fn_node_id), implicit_self: decl.inputs.get(0).map_or(hir::ImplicitSelfKind::None, |arg| { let is_mutable_pat = matches!( arg.pat.kind, @@ -2953,7 +3085,10 @@ fn is_empty(&self) -> bool { && self.parenthesized == hir::GenericArgsParentheses::No } - fn into_generic_args(self, this: &LoweringContext<'_, 'hir>) -> &'hir hir::GenericArgs<'hir> { + fn into_generic_args( + self, + this: &LoweringContext<'_, 'hir, impl ResolverAstLoweringExt<'hir>>, + ) -> &'hir hir::GenericArgs<'hir> { let ga = hir::GenericArgs { args: this.arena.alloc_from_iter(self.args), constraints: self.constraints, diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index c1c13977e103..aece4552dad4 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -14,7 +14,7 @@ use super::{ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt}; use crate::{AllowReturnTypeNotation, ImplTraitPosition}; -impl<'a, 'hir> LoweringContext<'a, 'hir> { +impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { pub(crate) fn lower_pat(&mut self, pattern: &Pat) -> &'hir hir::Pat<'hir> { self.arena.alloc(self.lower_pat_mut(pattern)) } diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index fe85302c2177..139140af3e03 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -20,7 +20,7 @@ LifetimeRes, LoweringContext, ParamMode, ResolverAstLoweringExt, }; -impl<'a, 'hir> LoweringContext<'a, 'hir> { +impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { #[instrument(level = "trace", skip(self))] pub(crate) fn lower_qpath( &mut self, diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 4ba5dc541342..a72ed842b8e3 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -329,6 +329,19 @@ fn print_crate_inner<'a>( /// - #63896: `#[allow(unused,` must be printed rather than `#[allow(unused ,` /// - #73345: `#[allow(unused)]` must be printed rather than `# [allow(unused)]` /// +/// Returns `true` if both token trees are identifier-like tokens that would +/// merge into a single token if printed without a space between them. +/// E.g. `ident` + `where` would merge into `identwhere`. +fn idents_would_merge(tt1: &TokenTree, tt2: &TokenTree) -> bool { + fn is_ident_like(tt: &TokenTree) -> bool { + matches!( + tt, + TokenTree::Token(Token { kind: token::Ident(..) | token::NtIdent(..), .. }, _,) + ) + } + is_ident_like(tt1) && is_ident_like(tt2) +} + fn space_between(tt1: &TokenTree, tt2: &TokenTree) -> bool { use Delimiter::*; use TokenTree::{Delimited as Del, Token as Tok}; @@ -737,6 +750,23 @@ fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) -> Spacing { TokenTree::Token(token, spacing) => { let token_str = self.token_to_string_ext(token, convert_dollar_crate); self.word(token_str); + // Emit hygiene annotations for identity-bearing tokens, + // matching how print_ident() and print_lifetime() call ann_post(). + match token.kind { + token::Ident(name, _) => { + self.ann_post(Ident::new(name, token.span)); + } + token::NtIdent(ident, _) => { + self.ann_post(ident); + } + token::Lifetime(name, _) => { + self.ann_post(Ident::new(name, token.span)); + } + token::NtLifetime(ident, _) => { + self.ann_post(ident); + } + _ => {} + } if let token::DocComment(..) = token.kind { self.hardbreak() } @@ -794,6 +824,13 @@ fn print_tts(&mut self, tts: &TokenStream, convert_dollar_crate: bool) { if let Some(next) = iter.peek() { if spacing == Spacing::Alone && space_between(tt, next) { self.space(); + } else if spacing != Spacing::Alone && idents_would_merge(tt, next) { + // When tokens from macro `tt` captures preserve their + // original `Joint`/`JointHidden` spacing, adjacent + // identifier-like tokens can be concatenated without a + // space (e.g. `$x:identwhere`). Insert a space to + // prevent this. + self.space(); } } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 9b4ff2b63bd4..0c5928765bfa 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -260,12 +260,15 @@ fn print_expr_method_call( // // loop { break x; }.method(); // - self.print_expr_cond_paren( - receiver, - receiver.precedence() < ExprPrecedence::Unambiguous, - fixup.leftmost_subexpression_with_dot(), - ); + let needs_paren = receiver.precedence() < ExprPrecedence::Unambiguous; + self.print_expr_cond_paren(receiver, needs_paren, fixup.leftmost_subexpression_with_dot()); + // If the receiver is an unsuffixed float literal like `0.`, insert + // a space so the `.` of the method call doesn't merge with the + // trailing dot: `0. .method()` instead of `0..method()`. + if !needs_paren && expr_ends_with_dot(receiver) { + self.word(" "); + } self.word("."); self.print_ident(segment.ident); if let Some(args) = &segment.args { @@ -658,11 +661,15 @@ pub(super) fn print_expr_outer_attr_style( ); } ast::ExprKind::Field(expr, ident) => { + let needs_paren = expr.precedence() < ExprPrecedence::Unambiguous; self.print_expr_cond_paren( expr, - expr.precedence() < ExprPrecedence::Unambiguous, + needs_paren, fixup.leftmost_subexpression_with_dot(), ); + if !needs_paren && expr_ends_with_dot(expr) { + self.word(" "); + } self.word("."); self.print_ident(*ident); } @@ -685,11 +692,15 @@ pub(super) fn print_expr_outer_attr_style( let fake_prec = ExprPrecedence::LOr; if let Some(e) = start { let start_fixup = fixup.leftmost_subexpression_with_operator(true); - self.print_expr_cond_paren( - e, - start_fixup.precedence(e) < fake_prec, - start_fixup, - ); + let needs_paren = start_fixup.precedence(e) < fake_prec; + self.print_expr_cond_paren(e, needs_paren, start_fixup); + // If the start expression is a float literal ending with + // `.`, we need a space before `..` or `..=` so that the + // dots don't merge. E.g. `0. ..45.` must not become + // `0...45.`. + if !needs_paren && expr_ends_with_dot(e) { + self.word(" "); + } } match limits { ast::RangeLimits::HalfOpen => self.word(".."), @@ -1025,3 +1036,18 @@ fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> String template.push('"'); template } + +/// Returns `true` if the printed representation of this expression ends with +/// a `.` character — specifically, an unsuffixed float literal like `0.` or +/// `45.`. This is used to insert whitespace before range operators (`..`, +/// `..=`) so that the dots don't merge (e.g. `0. ..45.` instead of `0...45.`). +fn expr_ends_with_dot(expr: &ast::Expr) -> bool { + match &expr.kind { + ast::ExprKind::Lit(token_lit) => { + token_lit.kind == token::Float + && token_lit.suffix.is_none() + && token_lit.symbol.as_str().ends_with('.') + } + _ => false, + } +} diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 3407feb3dcc3..e997fdf49820 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -881,7 +881,13 @@ fn print_use_tree(&mut self, tree: &ast::UseTree) { } if items.is_empty() { self.word("{}"); - } else if let [(item, _)] = items.as_slice() { + } else if let [(item, _)] = items.as_slice() + && !item + .prefix + .segments + .first() + .is_some_and(|seg| seg.ident.name == rustc_span::symbol::kw::SelfLower) + { self.print_use_tree(item); } else { let cb = self.cbox(INDENT_UNIT); diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index d2f743f6c5d8..badf696606e9 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -20,7 +20,9 @@ use thin_vec::ThinVec; use crate::context::{AcceptContext, ShouldEmit, Stage}; -use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser}; +use crate::parser::{ + AllowExprMetavar, ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser, +}; use crate::session_diagnostics::{ AttributeParseError, AttributeParseErrorReason, CfgAttrBadDelim, MetaBadDelimSugg, ParsedDescription, @@ -363,6 +365,7 @@ fn parse_cfg_attr_internal<'a>( let meta = MetaItemOrLitParser::parse_single( parser, ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, + AllowExprMetavar::Yes, )?; let pred_span = pred_start.with_hi(parser.token.span.hi()); diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index 7377159be370..f3612afe69e5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -12,7 +12,7 @@ use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES; use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; -use crate::parser::MetaItemOrLitParser; +use crate::parser::{AllowExprMetavar, MetaItemOrLitParser}; use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry}; #[derive(Clone)] @@ -94,6 +94,7 @@ pub fn parse_cfg_select( let meta = MetaItemOrLitParser::parse_single( p, ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, + AllowExprMetavar::Yes, ) .map_err(|diag| diag.emit())?; let cfg_span = meta.span(); diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index a8db114129ff..652a783de322 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -169,7 +169,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option, args: &ArgParser) -> Option( @@ -113,6 +116,13 @@ fn parse_directive_items<'p, S: Stage>( span, ); } + Mode::DiagnosticOnMove => { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MalformedOnMoveAttr { span }, + span, + ); + } } continue; }} @@ -132,7 +142,7 @@ fn parse_directive_items<'p, S: Stage>( Mode::RustcOnUnimplemented => { cx.emit_err(NoValueInOnUnimplemented { span: item.span() }); } - Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst => { + Mode::DiagnosticOnUnimplemented |Mode::DiagnosticOnConst | Mode::DiagnosticOnMove => { cx.emit_lint( MALFORMED_DIAGNOSTIC_ATTRIBUTES, AttributeLintKind::IgnoredDiagnosticOption { @@ -460,11 +470,12 @@ fn parse_filter(input: Symbol) -> FilterFormatString { // if the integer type has been resolved, to allow targeting all integers. // `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet, // from the `Display` impl of `InferTy` to be precise. + // `"{union|enum|struct}"` is used as a special selector for ADTs. // // Don't try to format these later! - Position::ArgumentNamed(arg @ ("integer" | "integral" | "float")) => { - LitOrArg::Lit(Symbol::intern(&format!("{{{arg}}}"))) - } + Position::ArgumentNamed( + arg @ ("integer" | "integral" | "float" | "union" | "enum" | "struct"), + ) => LitOrArg::Lit(Symbol::intern(&format!("{{{arg}}}"))), Position::ArgumentNamed(arg) => LitOrArg::Arg(Symbol::intern(arg)), Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(sym::empty_braces), diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs new file mode 100644 index 000000000000..006b3b66658e --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/on_move.rs @@ -0,0 +1,72 @@ +use rustc_feature::template; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::lints::AttributeLintKind; +use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES; +use rustc_span::sym; + +use crate::attributes::diagnostic::*; +use crate::attributes::prelude::*; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; +use crate::target_checking::{ALL_TARGETS, AllowedTargets}; + +#[derive(Default)] +pub(crate) struct OnMoveParser { + span: Option, + directive: Option<(Span, Directive)>, +} + +impl OnMoveParser { + fn parse<'sess, S: Stage>( + &mut self, + cx: &mut AcceptContext<'_, 'sess, S>, + args: &ArgParser, + mode: Mode, + ) { + if !cx.features().diagnostic_on_move() { + return; + } + + let span = cx.attr_span; + self.span = Some(span); + let Some(list) = args.list() else { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::MissingOptionsForOnMove, + span, + ); + return; + }; + + if list.is_empty() { + cx.emit_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + AttributeLintKind::OnMoveMalformedAttrExpectedLiteralOrDelimiter, + list.span, + ); + return; + } + + if let Some(directive) = parse_directive_items(cx, mode, list.mixed(), true) { + merge_directives(cx, &mut self.directive, (span, directive)); + } + } +} +impl AttributeParser for OnMoveParser { + const ATTRIBUTES: AcceptMapping = &[( + &[sym::diagnostic, sym::on_move], + template!(List: &[r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#]), + |this, cx, args| { + this.parse(cx, args, Mode::DiagnosticOnMove); + }, + )]; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + if let Some(span) = self.span { + Some(AttributeKind::OnMove { span, directive: self.directive.map(|d| Box::new(d.1)) }) + } else { + None + } + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs index ac50fe33839d..8eb902be34e2 100644 --- a/compiler/rustc_attr_parsing/src/attributes/prototype.rs +++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs @@ -9,6 +9,7 @@ use crate::attributes::SingleAttributeParser; use crate::context::{AcceptContext, Stage}; use crate::parser::ArgParser; +use crate::session_diagnostics; use crate::target_checking::AllowedTargets; use crate::target_checking::Policy::Allow; @@ -57,6 +58,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option( Some((phase, span)) } + +fn check_custom_mir( + cx: &mut AcceptContext<'_, '_, S>, + dialect: Option<(MirDialect, Span)>, + phase: Option<(MirPhase, Span)>, + failed: &mut bool, +) { + let attr_span = cx.attr_span; + let Some((dialect, dialect_span)) = dialect else { + if let Some((_, phase_span)) = phase { + *failed = true; + cx.emit_err(session_diagnostics::CustomMirPhaseRequiresDialect { + attr_span, + phase_span, + }); + } + return; + }; + + match dialect { + MirDialect::Analysis => { + if let Some((MirPhase::Optimized, phase_span)) = phase { + *failed = true; + cx.emit_err(session_diagnostics::CustomMirIncompatibleDialectAndPhase { + dialect, + phase: MirPhase::Optimized, + attr_span, + dialect_span, + phase_span, + }); + } + } + + MirDialect::Built => { + if let Some((phase, phase_span)) = phase { + *failed = true; + cx.emit_err(session_diagnostics::CustomMirIncompatibleDialectAndPhase { + dialect, + phase, + attr_span, + dialect_span, + phase_span, + }); + } + } + MirDialect::Runtime => {} + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 6ddd9f2c1fb5..25b295c162aa 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -1,6 +1,7 @@ use std::num::NonZero; use rustc_errors::ErrorGuaranteed; +use rustc_feature::ACCEPTED_LANG_FEATURES; use rustc_hir::target::GenericParamKind; use rustc_hir::{ DefaultBodyStability, MethodKind, PartialConstStability, Stability, StabilityLevel, @@ -366,7 +367,7 @@ pub(crate) fn parse_stability( } } -// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable` +/// Read the content of a `unstable`/`rustc_const_unstable`/`rustc_default_body_unstable` /// attribute, and return the feature name and its stability information. pub(crate) fn parse_unstability( cx: &AcceptContext<'_, '_, S>, @@ -451,6 +452,16 @@ pub(crate) fn parse_unstability( match (feature, issue) { (Ok(feature), Ok(_)) => { + // Stable *language* features shouldn't be used as unstable library features. + // (Not doing this for stable library features is checked by tidy.) + if ACCEPTED_LANG_FEATURES.iter().any(|f| f.name == feature) { + cx.emit_err(session_diagnostics::UnstableAttrForAlreadyStableFeature { + attr_span: cx.attr_span, + item_span: cx.target_span, + }); + return None; + } + let level = StabilityLevel::Unstable { reason: UnstableReason::from_opt_reason(reason), issue: issue_num, diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 190568bed508..259a73de5985 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -29,6 +29,7 @@ use crate::attributes::deprecation::*; use crate::attributes::diagnostic::do_not_recommend::*; use crate::attributes::diagnostic::on_const::*; +use crate::attributes::diagnostic::on_move::*; use crate::attributes::diagnostic::on_unimplemented::*; use crate::attributes::doc::*; use crate::attributes::dummy::*; @@ -149,6 +150,7 @@ mod late { MacroUseParser, NakedParser, OnConstParser, + OnMoveParser, OnUnimplementedParser, RustcAlignParser, RustcAlignStaticParser, diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index f75f63a0e811..fa66dec6a156 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -14,7 +14,7 @@ use crate::context::{AcceptContext, FinalizeContext, FinalizeFn, SharedContext, Stage}; use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState}; -use crate::parser::{ArgParser, PathParser, RefPathParser}; +use crate::parser::{AllowExprMetavar, ArgParser, PathParser, RefPathParser}; use crate::session_diagnostics::ParsedDescription; use crate::{Early, Late, OmitDoc, ShouldEmit}; @@ -139,6 +139,7 @@ pub fn parse_single( emit_errors: ShouldEmit, parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser) -> Option, template: &AttributeTemplate, + allow_expr_metavar: AllowExprMetavar, ) -> Option { let ast::AttrKind::Normal(normal_attr) = &attr.kind else { panic!("parse_single called on a doc attr") @@ -152,6 +153,7 @@ pub fn parse_single( &parts, &sess.psess, emit_errors, + allow_expr_metavar, )?; Self::parse_single_args( sess, @@ -333,6 +335,7 @@ pub fn parse_attribute_list( &parts, &self.sess.psess, self.stage.should_emit(), + AllowExprMetavar::No, ) else { continue; }; diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 07f246319dec..6e8a50096736 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -109,6 +109,7 @@ pub fn from_attr_args<'sess>( parts: &[Symbol], psess: &'sess ParseSess, should_emit: ShouldEmit, + allow_expr_metavar: AllowExprMetavar, ) -> Option { Some(match value { AttrArgs::Empty => Self::NoArgs, @@ -122,6 +123,7 @@ pub fn from_attr_args<'sess>( args.dspan.entire(), psess, ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden }, + allow_expr_metavar, ) { Ok(p) => return Some(ArgParser::List(p)), Err(e) => { @@ -147,9 +149,15 @@ pub fn from_attr_args<'sess>( } Self::List( - MetaItemListParser::new(&args.tokens, args.dspan.entire(), psess, should_emit) - .map_err(|e| should_emit.emit_err(e)) - .ok()?, + MetaItemListParser::new( + &args.tokens, + args.dspan.entire(), + psess, + should_emit, + allow_expr_metavar, + ) + .map_err(|e| should_emit.emit_err(e)) + .ok()?, ) } AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser { @@ -217,8 +225,9 @@ impl MetaItemOrLitParser { pub fn parse_single<'sess>( parser: &mut Parser<'sess>, should_emit: ShouldEmit, + allow_expr_metavar: AllowExprMetavar, ) -> PResult<'sess, MetaItemOrLitParser> { - let mut this = MetaItemListParserContext { parser, should_emit }; + let mut this = MetaItemListParserContext { parser, should_emit, allow_expr_metavar }; this.parse_meta_item_inner() } @@ -404,9 +413,19 @@ fn expr_to_lit<'sess>( } } +/// Whether expansions of `expr` metavariables from decrarative macros +/// are permitted. Used when parsing meta items; currently, only `cfg` predicates +/// enable this option +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum AllowExprMetavar { + No, + Yes, +} + struct MetaItemListParserContext<'a, 'sess> { parser: &'a mut Parser<'sess>, should_emit: ShouldEmit, + allow_expr_metavar: AllowExprMetavar, } impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { @@ -447,20 +466,44 @@ fn unsuffixed_meta_item_from_lit( Ok(lit) } - fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser> { - if let Some(MetaVarKind::Meta { has_meta_form }) = self.parser.token.is_metavar_seq() { - return if has_meta_form { - let attr_item = self - .parser - .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| { - MetaItemListParserContext { parser: this, should_emit: self.should_emit } - .parse_attr_item() - }) - .unwrap(); - Ok(attr_item) - } else { - self.parser.unexpected_any() - }; + fn parse_meta_item(&mut self) -> PResult<'sess, MetaItemParser> { + if let Some(metavar) = self.parser.token.is_metavar_seq() { + match (metavar, self.allow_expr_metavar) { + (kind @ MetaVarKind::Expr { .. }, AllowExprMetavar::Yes) => { + return self + .parser + .eat_metavar_seq(kind, |this| { + MetaItemListParserContext { + parser: this, + should_emit: self.should_emit, + allow_expr_metavar: AllowExprMetavar::Yes, + } + .parse_meta_item() + }) + .ok_or_else(|| { + self.parser.unexpected_any::().unwrap_err() + }); + } + (MetaVarKind::Meta { has_meta_form }, _) => { + return if has_meta_form { + let attr_item = self + .parser + .eat_metavar_seq(MetaVarKind::Meta { has_meta_form: true }, |this| { + MetaItemListParserContext { + parser: this, + should_emit: self.should_emit, + allow_expr_metavar: self.allow_expr_metavar, + } + .parse_meta_item() + }) + .unwrap(); + Ok(attr_item) + } else { + self.parser.unexpected_any() + }; + } + _ => {} + } } let path = self.parser.parse_path(PathStyle::Mod)?; @@ -469,8 +512,12 @@ fn parse_attr_item(&mut self) -> PResult<'sess, MetaItemParser> { let args = if self.parser.check(exp!(OpenParen)) { let start = self.parser.token.span; let (sub_parsers, _) = self.parser.parse_paren_comma_seq(|parser| { - MetaItemListParserContext { parser, should_emit: self.should_emit } - .parse_meta_item_inner() + MetaItemListParserContext { + parser, + should_emit: self.should_emit, + allow_expr_metavar: self.allow_expr_metavar, + } + .parse_meta_item_inner() })?; let end = self.parser.prev_token.span; ArgParser::List(MetaItemListParser { sub_parsers, span: start.with_hi(end.hi()) }) @@ -492,7 +539,7 @@ fn parse_meta_item_inner(&mut self) -> PResult<'sess, MetaItemOrLitParser> { Ok(MetaItemOrLitParser::Lit(self.unsuffixed_meta_item_from_lit(token_lit)?)) } else { let prev_pros = self.parser.approx_token_stream_pos(); - match self.parse_attr_item() { + match self.parse_meta_item() { Ok(item) => Ok(MetaItemOrLitParser::MetaItemParser(item)), Err(err) => { // If `parse_attr_item` made any progress, it likely has a more precise error we should prefer @@ -580,13 +627,15 @@ fn parse( psess: &'sess ParseSess, span: Span, should_emit: ShouldEmit, + allow_expr_metavar: AllowExprMetavar, ) -> PResult<'sess, MetaItemListParser> { let mut parser = Parser::new(psess, tokens, None); if let ShouldEmit::ErrorsAndLints { recovery } = should_emit { parser = parser.recovery(recovery); } - let mut this = MetaItemListParserContext { parser: &mut parser, should_emit }; + let mut this = + MetaItemListParserContext { parser: &mut parser, should_emit, allow_expr_metavar }; // Presumably, the majority of the time there will only be one attr. let mut sub_parsers = ThinVec::with_capacity(1); @@ -618,8 +667,15 @@ pub(crate) fn new<'sess>( span: Span, psess: &'sess ParseSess, should_emit: ShouldEmit, + allow_expr_metavar: AllowExprMetavar, ) -> Result> { - MetaItemListParserContext::parse(tokens.clone(), psess, span, should_emit) + MetaItemListParserContext::parse( + tokens.clone(), + psess, + span, + should_emit, + allow_expr_metavar, + ) } /// Lets you pick and choose as what you want to parse each element in the list diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 751b8f8646ab..bab830098f1a 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -7,6 +7,7 @@ }; use rustc_feature::AttributeTemplate; use rustc_hir::AttrPath; +use rustc_hir::attrs::{MirDialect, MirPhase}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; use rustc_target::spec::TargetTuple; @@ -1023,3 +1024,36 @@ pub(crate) struct UnsupportedInstructionSet<'a> { pub instruction_set: Symbol, pub current_target: &'a TargetTuple, } + +#[derive(Diagnostic)] +#[diag("`dialect` key required")] +pub(crate) struct CustomMirPhaseRequiresDialect { + #[primary_span] + pub attr_span: Span, + #[label("`phase` argument requires a `dialect` argument")] + pub phase_span: Span, +} + +#[derive(Diagnostic)] +#[diag("the {$dialect} dialect is not compatible with the {$phase} phase")] +pub(crate) struct CustomMirIncompatibleDialectAndPhase { + pub dialect: MirDialect, + pub phase: MirPhase, + #[primary_span] + pub attr_span: Span, + #[label("this dialect...")] + pub dialect_span: Span, + #[label("... is not compatible with this phase")] + pub phase_span: Span, +} + +#[derive(Diagnostic)] +#[diag("can't mark as unstable using an already stable feature")] +pub(crate) struct UnstableAttrForAlreadyStableFeature { + #[primary_span] + #[label("this feature is already stable")] + #[help("consider removing the attribute")] + pub attr_span: Span, + #[label("the stability attribute annotates this item")] + pub item_span: Span, +} diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs index 0d3c554e4176..4fcb5f3b5a94 100644 --- a/compiler/rustc_borrowck/src/borrowck_errors.rs +++ b/compiler/rustc_borrowck/src/borrowck_errors.rs @@ -324,18 +324,23 @@ pub(crate) fn cannot_act_on_moved_value( verb: &str, optional_adverb_for_moved: &str, moved_path: Option, + primary_message: Option, ) -> Diag<'infcx> { - let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default(); + if let Some(primary_message) = primary_message { + struct_span_code_err!(self.dcx(), use_span, E0382, "{}", primary_message) + } else { + let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default(); - struct_span_code_err!( - self.dcx(), - use_span, - E0382, - "{} of {}moved value{}", - verb, - optional_adverb_for_moved, - moved_path, - ) + struct_span_code_err!( + self.dcx(), + use_span, + E0382, + "{} of {}moved value{}", + verb, + optional_adverb_for_moved, + moved_path, + ) + } } pub(crate) fn cannot_borrow_path_as_mutable_because( diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 641121597848..7bbce730c165 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -6,12 +6,16 @@ use either::Either; use hir::{ClosureKind, Path}; use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, MultiSpan, struct_span_code_err}; use rustc_hir as hir; +use rustc_hir::attrs::diagnostic::FormatArgs; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr}; -use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, LangItem, PatField}; +use rustc_hir::{ + CoroutineDesugaring, CoroutineKind, CoroutineSource, LangItem, PatField, find_attr, +}; use rustc_middle::bug; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::{ @@ -138,6 +142,36 @@ pub(crate) fn report_use_of_moved_or_uninitialized( let partial_str = if is_partial_move { "partial " } else { "" }; let partially_str = if is_partial_move { "partially " } else { "" }; + let (on_move_message, on_move_label, on_move_notes) = if let ty::Adt(item_def, args) = + self.body.local_decls[moved_place.local].ty.kind() + && let Some(Some(directive)) = find_attr!(self.infcx.tcx, item_def.did(), OnMove { directive, .. } => directive) + { + let item_name = self.infcx.tcx.item_name(item_def.did()).to_string(); + let mut generic_args: Vec<_> = self + .infcx + .tcx + .generics_of(item_def.did()) + .own_params + .iter() + .filter_map(|param| Some((param.name, args[param.index as usize].to_string()))) + .collect(); + generic_args.push((kw::SelfUpper, item_name)); + + let args = FormatArgs { + this: String::new(), + trait_sugared: String::new(), + item_context: "", + generic_args, + }; + ( + directive.message.as_ref().map(|e| e.1.format(&args)), + directive.label.as_ref().map(|e| e.1.format(&args)), + directive.notes.iter().map(|e| e.format(&args)).collect(), + ) + } else { + (None, None, ThinVec::new()) + }; + let mut err = self.cannot_act_on_moved_value( span, desired_action.as_noun(), @@ -146,8 +180,13 @@ pub(crate) fn report_use_of_moved_or_uninitialized( moved_place, DescribePlaceOpt { including_downcast: true, including_tuple_field: true }, ), + on_move_message, ); + for note in on_move_notes { + err.note(note); + } + let reinit_spans = maybe_reinitialized_locations .iter() .take(3) @@ -275,12 +314,16 @@ pub(crate) fn report_use_of_moved_or_uninitialized( if needs_note { if let Some(local) = place.as_local() { let span = self.body.local_decls[local].source_info.span; - err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { - is_partial_move, - ty, - place: ¬e_msg, - span, - }); + if let Some(on_move_label) = on_move_label { + err.span_label(span, on_move_label); + } else { + err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label { + is_partial_move, + ty, + place: ¬e_msg, + span, + }); + } } else { err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note { is_partial_move, diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 9aa8f7a2067a..47f14dc3df62 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -374,10 +374,6 @@ fn body(&self) -> &Body<'tcx> { self.body } - fn unsized_feature_enabled(&self) -> bool { - self.tcx().features().unsized_fn_params() - } - /// Equate the inferred type and the annotated type for user type annotations #[instrument(skip(self), level = "debug")] fn check_user_type_annotations(&mut self) { @@ -660,7 +656,7 @@ fn visit_statement(&mut self, stmt: &Statement<'tcx>, location: Location) { ); } - if !self.unsized_feature_enabled() { + if !self.tcx().features().unsized_fn_params() { let trait_ref = ty::TraitRef::new( tcx, tcx.require_lang_item(LangItem::Sized, self.last_span), @@ -936,9 +932,10 @@ fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { } } - // When `unsized_fn_params` is enabled, only function calls - // and nullary ops are checked in `check_call_dest`. - if !self.unsized_feature_enabled() { + // When `unsized_fn_params` is enabled, this is checked in `check_call_dest`, + // and `hir_typeck` still forces all non-argument locals to be sized (i.e., we don't + // fully re-check what was already checked on HIR). + if !self.tcx().features().unsized_fn_params() { match self.body.local_kind(local) { LocalKind::ReturnPointer | LocalKind::Arg => { // return values of normal functions are required to be @@ -1953,8 +1950,8 @@ fn check_call_dest( } // When `unsized_fn_params` is not enabled, - // this check is done at `check_local`. - if self.unsized_feature_enabled() { + // this check is done at `visit_local_decl`. + if self.tcx().features().unsized_fn_params() { let span = term.source_info.span; self.ensure_place_sized(dest_ty, span); } diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index e2d684e12a81..152a7674490c 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -616,4 +616,9 @@ fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { } })]); } + + fn try_eagerly_normalize_alias(&mut self, _alias: ty::AliasTy<'tcx>) -> Ty<'tcx> { + // Past hir typeck, so we don't have to worry about type inference anymore. + self.type_checker.infcx.next_ty_var(self.span()) + } } diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 3ebde949b99b..c4a458089f2d 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -4,10 +4,9 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AttrStyle, token}; -use rustc_attr_parsing as attr; -use rustc_attr_parsing::parser::MetaItemOrLitParser; +use rustc_attr_parsing::parser::{AllowExprMetavar, MetaItemOrLitParser}; use rustc_attr_parsing::{ - AttributeParser, CFG_TEMPLATE, ParsedDescription, ShouldEmit, parse_cfg_entry, + self as attr, AttributeParser, CFG_TEMPLATE, ParsedDescription, ShouldEmit, parse_cfg_entry, }; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; use rustc_hir::attrs::CfgEntry; @@ -44,6 +43,7 @@ fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result Float { name: powf, attrs: { @@ -128,6 +128,5 @@ index c61961f8584..d7b4fa20322 100644 f128: #[cfg(all(not(miri), target_has_reliable_f128_math))], }, test { --- +-- 2.50.1 - diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index efd4e55d5a85..ffa582737ca8 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -23,9 +23,7 @@ use rustc_session::Session; use rustc_session::config::{self, Lto, OutputType, Passes, SplitDwarfKind, SwitchWithOptPath}; use rustc_span::{BytePos, InnerSpan, Pos, RemapPathScopeComponents, SpanData, SyntaxContext, sym}; -use rustc_target::spec::{ - Arch, CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel, -}; +use rustc_target::spec::{CodeModel, FloatAbi, RelocModel, SanitizerSet, SplitDebuginfo, TlsModel}; use tracing::{debug, trace}; use crate::back::lto::{Buffer, ModuleBuffer}; @@ -206,13 +204,7 @@ pub(crate) fn target_machine_factory( let reloc_model = to_llvm_relocation_model(sess.relocation_model()); let (opt_level, _) = to_llvm_opt_settings(optlvl); - let float_abi = if sess.target.arch == Arch::Arm && sess.opts.cg.soft_float { - llvm::FloatAbi::Soft - } else { - // `validate_commandline_args_with_session_available` has already warned about this being - // ignored. Let's make sure LLVM doesn't suddenly start using this flag on more targets. - to_llvm_float_abi(sess.target.llvm_floatabi) - }; + let float_abi = to_llvm_float_abi(sess.target.llvm_floatabi); let ffunction_sections = sess.opts.unstable_opts.function_sections.unwrap_or(sess.target.function_sections); diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index 2423880ab869..45e16a735fed 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -394,8 +394,8 @@ fn update_target_reliable_float_cfg(sess: &Session, cfg: &mut TargetConfig) { // ABI bugs et al. (full // list at ) (Arch::PowerPC | Arch::PowerPC64, _) => false, - // ABI unsupported - (Arch::Sparc, _) => false, + // ABI unsupported (fixed in llvm22) + (Arch::Sparc, _) if major < 22 => false, // MinGW ABI bugs (Arch::X86_64, Os::Windows) if *target_env == Env::Gnu && *target_abi != Abi::Llvm => false, // There are no known problems on other platforms, so the only requirement is that symbols diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 1a0f66d31cca..5c14d6f2c093 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -437,7 +437,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( assert_eq!( None, num_untupled.replace(tupled_arg_tys.len()), - "Replaced existing num_tupled" + "Replaced existing num_untupled" ); return LocalRef::Place(place); diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs index e2193a97a0f4..997077ac4402 100644 --- a/compiler/rustc_data_structures/src/marker.rs +++ b/compiler/rustc_data_structures/src/marker.rs @@ -188,53 +188,6 @@ pub fn assert_dyn_send() {} pub fn assert_dyn_send_val(_t: &T) {} pub fn assert_dyn_send_sync_val(_t: &T) {} -#[derive(Copy, Clone)] -pub struct FromDyn(T); - -impl FromDyn { - #[inline(always)] - pub fn from(val: T) -> Self { - // Check that `sync::is_dyn_thread_safe()` is true on creation so we can - // implement `Send` and `Sync` for this structure when `T` - // implements `DynSend` and `DynSync` respectively. - assert!(crate::sync::is_dyn_thread_safe()); - FromDyn(val) - } - - #[inline(always)] - pub fn derive(&self, val: O) -> FromDyn { - // We already did the check for `sync::is_dyn_thread_safe()` when creating `Self` - FromDyn(val) - } - - #[inline(always)] - pub fn into_inner(self) -> T { - self.0 - } -} - -// `FromDyn` is `Send` if `T` is `DynSend`, since it ensures that sync::is_dyn_thread_safe() is true. -unsafe impl Send for FromDyn {} - -// `FromDyn` is `Sync` if `T` is `DynSync`, since it ensures that sync::is_dyn_thread_safe() is true. -unsafe impl Sync for FromDyn {} - -impl std::ops::Deref for FromDyn { - type Target = T; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl std::ops::DerefMut for FromDyn { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - // A wrapper to convert a struct that is already a `Send` or `Sync` into // an instance of `DynSend` and `DynSync`, since the compiler cannot infer // it automatically in some cases. (e.g. Box) diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 327c28fd1389..3d5bc8527828 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -34,7 +34,9 @@ pub use self::freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard}; #[doc(no_inline)] pub use self::lock::{Lock, LockGuard, Mode}; -pub use self::mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; +pub use self::mode::{ + FromDyn, check_dyn_thread_safe, is_dyn_thread_safe, set_dyn_thread_safe_mode, +}; pub use self::parallel::{ broadcast, par_fns, par_for_each_in, par_join, par_map, parallel_guard, spawn, try_par_for_each_in, @@ -64,12 +66,20 @@ mod atomic { mod mode { use std::sync::atomic::{AtomicU8, Ordering}; + use crate::sync::{DynSend, DynSync}; + const UNINITIALIZED: u8 = 0; const DYN_NOT_THREAD_SAFE: u8 = 1; const DYN_THREAD_SAFE: u8 = 2; static DYN_THREAD_SAFE_MODE: AtomicU8 = AtomicU8::new(UNINITIALIZED); + // Whether thread safety is enabled (due to running under multiple threads). + #[inline] + pub fn check_dyn_thread_safe() -> Option> { + is_dyn_thread_safe().then_some(FromDyn(())) + } + // Whether thread safety is enabled (due to running under multiple threads). #[inline] pub fn is_dyn_thread_safe() -> bool { @@ -99,6 +109,44 @@ pub fn set_dyn_thread_safe_mode(mode: bool) { // Check that the mode was either uninitialized or was already set to the requested mode. assert!(previous.is_ok() || previous == Err(set)); } + + #[derive(Copy, Clone)] + pub struct FromDyn(T); + + impl FromDyn { + #[inline(always)] + pub fn derive(&self, val: O) -> FromDyn { + // We already did the check for `sync::is_dyn_thread_safe()` when creating `Self` + FromDyn(val) + } + + #[inline(always)] + pub fn into_inner(self) -> T { + self.0 + } + } + + // `FromDyn` is `Send` if `T` is `DynSend`, since it ensures that sync::is_dyn_thread_safe() is true. + unsafe impl Send for FromDyn {} + + // `FromDyn` is `Sync` if `T` is `DynSync`, since it ensures that sync::is_dyn_thread_safe() is true. + unsafe impl Sync for FromDyn {} + + impl std::ops::Deref for FromDyn { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl std::ops::DerefMut for FromDyn { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } } /// This makes locks panic if they are already held. diff --git a/compiler/rustc_data_structures/src/sync/parallel.rs b/compiler/rustc_data_structures/src/sync/parallel.rs index 2ab4a7f75b6b..b112ecc2fbad 100644 --- a/compiler/rustc_data_structures/src/sync/parallel.rs +++ b/compiler/rustc_data_structures/src/sync/parallel.rs @@ -57,8 +57,8 @@ fn serial_join(oper_a: A, oper_b: B) -> (RA, RB) } pub fn spawn(func: impl FnOnce() + DynSend + 'static) { - if mode::is_dyn_thread_safe() { - let func = FromDyn::from(func); + if let Some(proof) = mode::check_dyn_thread_safe() { + let func = proof.derive(func); rustc_thread_pool::spawn(|| { (func.into_inner())(); }); @@ -73,8 +73,8 @@ pub fn spawn(func: impl FnOnce() + DynSend + 'static) { /// Use that for the longest running function for better scheduling. pub fn par_fns(funcs: &mut [&mut (dyn FnMut() + DynSend)]) { parallel_guard(|guard: &ParallelGuard| { - if mode::is_dyn_thread_safe() { - let funcs = FromDyn::from(funcs); + if let Some(proof) = mode::check_dyn_thread_safe() { + let funcs = proof.derive(funcs); rustc_thread_pool::scope(|s| { let Some((first, rest)) = funcs.into_inner().split_at_mut_checked(1) else { return; @@ -84,7 +84,7 @@ pub fn par_fns(funcs: &mut [&mut (dyn FnMut() + DynSend)]) { // order when using a single thread. This ensures the execution order matches // that of a single threaded rustc. for f in rest.iter_mut().rev() { - let f = FromDyn::from(f); + let f = proof.derive(f); s.spawn(|_| { guard.run(|| (f.into_inner())()); }); @@ -108,13 +108,13 @@ pub fn par_join(oper_a: A, oper_b: B) -> (RA, RB A: FnOnce() -> RA + DynSend, B: FnOnce() -> RB + DynSend, { - if mode::is_dyn_thread_safe() { - let oper_a = FromDyn::from(oper_a); - let oper_b = FromDyn::from(oper_b); + if let Some(proof) = mode::check_dyn_thread_safe() { + let oper_a = proof.derive(oper_a); + let oper_b = proof.derive(oper_b); let (a, b) = parallel_guard(|guard| { rustc_thread_pool::join( - move || guard.run(move || FromDyn::from(oper_a.into_inner()())), - move || guard.run(move || FromDyn::from(oper_b.into_inner()())), + move || guard.run(move || proof.derive(oper_a.into_inner()())), + move || guard.run(move || proof.derive(oper_b.into_inner()())), ) }); (a.unwrap().into_inner(), b.unwrap().into_inner()) @@ -127,13 +127,30 @@ fn par_slice( items: &mut [I], guard: &ParallelGuard, for_each: impl Fn(&mut I) + DynSync + DynSend, + proof: FromDyn<()>, ) { - let for_each = FromDyn::from(for_each); + match items { + [] => return, + [item] => { + guard.run(|| for_each(item)); + return; + } + _ => (), + } + + let for_each = proof.derive(for_each); let mut items = for_each.derive(items); rustc_thread_pool::scope(|s| { let proof = items.derive(()); - let group_size = std::cmp::max(items.len() / 128, 1); - for group in items.chunks_mut(group_size) { + + const MAX_GROUP_COUNT: usize = 128; + let group_size = items.len().div_ceil(MAX_GROUP_COUNT); + let groups = items.chunks_mut(group_size); + + // Reverse the order of the later functions since Rayon executes them in reverse + // order when using a single thread. This ensures the execution order matches + // that of a single threaded rustc. + for group in groups.rev() { let group = proof.derive(group); s.spawn(|_| { let mut group = group; @@ -150,9 +167,9 @@ pub fn par_for_each_in>( for_each: impl Fn(&I) + DynSync + DynSend, ) { parallel_guard(|guard| { - if mode::is_dyn_thread_safe() { + if let Some(proof) = mode::check_dyn_thread_safe() { let mut items: Vec<_> = t.into_iter().collect(); - par_slice(&mut items, guard, |i| for_each(&*i)) + par_slice(&mut items, guard, |i| for_each(&*i), proof) } else { t.into_iter().for_each(|i| { guard.run(|| for_each(&i)); @@ -173,16 +190,21 @@ pub fn try_par_for_each_in( ::Item: DynSend, { parallel_guard(|guard| { - if mode::is_dyn_thread_safe() { + if let Some(proof) = mode::check_dyn_thread_safe() { let mut items: Vec<_> = t.into_iter().collect(); let error = Mutex::new(None); - par_slice(&mut items, guard, |i| { - if let Err(err) = for_each(&*i) { - *error.lock() = Some(err); - } - }); + par_slice( + &mut items, + guard, + |i| { + if let Err(err) = for_each(&*i) { + *error.lock() = Some(err); + } + }, + proof, + ); if let Some(err) = error.into_inner() { Err(err) } else { Ok(()) } } else { @@ -196,15 +218,20 @@ pub fn par_map, R: DynSend, C: FromIterato map: impl Fn(I) -> R + DynSync + DynSend, ) -> C { parallel_guard(|guard| { - if mode::is_dyn_thread_safe() { - let map = FromDyn::from(map); + if let Some(proof) = mode::check_dyn_thread_safe() { + let map = proof.derive(map); let mut items: Vec<(Option, Option)> = t.into_iter().map(|i| (Some(i), None)).collect(); - par_slice(&mut items, guard, |i| { - i.1 = Some(map(i.0.take().unwrap())); - }); + par_slice( + &mut items, + guard, + |i| { + i.1 = Some(map(i.0.take().unwrap())); + }, + proof, + ); items.into_iter().filter_map(|i| i.1).collect() } else { @@ -214,8 +241,8 @@ pub fn par_map, R: DynSend, C: FromIterato } pub fn broadcast(op: impl Fn(usize) -> R + DynSync) -> Vec { - if mode::is_dyn_thread_safe() { - let op = FromDyn::from(op); + if let Some(proof) = mode::check_dyn_thread_safe() { + let op = proof.derive(op); let results = rustc_thread_pool::broadcast(|context| op.derive(op(context.index()))); results.into_iter().map(|r| r.into_inner()).collect() } else { diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 8e4039b32d94..ec5951e50e3a 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -10,9 +10,10 @@ self as ast, AttrItemKind, AttrKind, AttrStyle, Attribute, DUMMY_NODE_ID, EarlyParsedAttribute, HasAttrs, HasTokens, MetaItem, MetaItemInner, NodeId, NormalAttr, }; -use rustc_attr_parsing as attr; +use rustc_attr_parsing::parser::AllowExprMetavar; use rustc_attr_parsing::{ - AttributeParser, CFG_TEMPLATE, EvalConfigResult, ShouldEmit, eval_config_entry, parse_cfg, + self as attr, AttributeParser, CFG_TEMPLATE, EvalConfigResult, ShouldEmit, eval_config_entry, + parse_cfg, }; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_errors::msg; @@ -402,6 +403,7 @@ pub(crate) fn cfg_true(&self, attr: &Attribute, emit_errors: ShouldEmit) -> Eval emit_errors, parse_cfg, &CFG_TEMPLATE, + AllowExprMetavar::Yes, ) else { // Cfg attribute was not parsable, give up return EvalConfigResult::True; diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 76a9a6f9d03d..640d0746fe1a 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -13,6 +13,7 @@ TyKind, token, }; use rustc_ast_pretty::pprust; +use rustc_attr_parsing::parser::AllowExprMetavar; use rustc_attr_parsing::{ AttributeParser, CFG_TEMPLATE, Early, EvalConfigResult, ShouldEmit, eval_config_entry, parse_cfg, validate_attr, @@ -2224,6 +2225,7 @@ fn expand_cfg_true( ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, parse_cfg, &CFG_TEMPLATE, + AllowExprMetavar::Yes, ) else { // Cfg attribute was not parsable, give up return EvalConfigResult::True; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 3a2f548902d1..d74450e67cf1 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -1587,6 +1587,7 @@ pub fn is_stable_diagnostic_attribute(sym: Symbol, features: &Features) -> bool match sym { sym::on_unimplemented | sym::do_not_recommend => true, sym::on_const => features.diagnostic_on_const(), + sym::on_move => features.diagnostic_on_move(), _ => false, } } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index c508344b9cc1..e8ca20d7f5f4 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -472,6 +472,8 @@ pub fn internal(&self, feature: Symbol) -> bool { (unstable, derive_from, "1.91.0", Some(144889)), /// Allows giving non-const impls custom diagnostic messages if attempted to be used as const (unstable, diagnostic_on_const, "1.93.0", Some(143874)), + /// Allows giving on-move borrowck custom diagnostic messages for a type + (unstable, diagnostic_on_move, "CURRENT_RUSTC_VERSION", Some(150935)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), /// Allows `#[doc(masked)]`. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index b5b9da1e8e00..a18ddff94709 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1180,13 +1180,18 @@ pub enum AttributeKind { directive: Option>, }, + /// Represents `#[diagnostic::on_move]` + OnMove { + span: Span, + directive: Option>, + }, + /// Represents `#[rustc_on_unimplemented]` and `#[diagnostic::on_unimplemented]`. OnUnimplemented { span: Span, /// None if the directive was malformed in some way. directive: Option>, }, - /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 27128f699637..c19fc6976c6e 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -77,6 +77,7 @@ pub fn encode_cross_crate(&self) -> EncodeCrossCrate { NoStd(..) => No, NonExhaustive(..) => Yes, // Needed for rustdoc OnConst { .. } => Yes, + OnMove { .. } => Yes, OnUnimplemented { .. } => Yes, Optimize(..) => No, PanicRuntime => No, diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 3959ee7f9412..21abb5506fcc 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -192,7 +192,7 @@ pub enum DefKind { /// These are all represented with the same `ExprKind::Closure` in the AST and HIR, /// which makes it difficult to distinguish these during def collection. Therefore, /// we treat them all the same, and code which needs to distinguish them can match - /// or `hir::ClosureKind` or `type_of`. + /// on `hir::ClosureKind` or `type_of`. Closure, /// The definition of a synthetic coroutine body created by the lowering of a /// coroutine-closure, such as an async closure. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 7cce0eda4dda..9526143fcace 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1667,19 +1667,6 @@ pub fn unwrap(self) -> &'tcx OwnerInfo<'tcx> { } } -/// The top-level data structure that stores the entire contents of -/// the crate currently being compiled. -/// -/// For more details, see the [rustc dev guide]. -/// -/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html -#[derive(Debug)] -pub struct Crate<'hir> { - pub owners: IndexVec>, - // Only present when incr. comp. is enabled. - pub opt_hir_hash: Option, -} - #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct Closure<'hir> { pub def_id: LocalDefId, diff --git a/compiler/rustc_hir/src/stable_hash_impls.rs b/compiler/rustc_hir/src/stable_hash_impls.rs index a3f4415ec343..58649a694880 100644 --- a/compiler/rustc_hir/src/stable_hash_impls.rs +++ b/compiler/rustc_hir/src/stable_hash_impls.rs @@ -3,7 +3,7 @@ use crate::HashIgnoredAttrId; use crate::hir::{ - AttributeMap, BodyId, Crate, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, + AttributeMap, BodyId, ForeignItemId, ImplItemId, ItemId, OwnerNodes, TraitItemId, }; use crate::hir_id::ItemLocalId; use crate::lints::DelayedLints; @@ -94,13 +94,6 @@ fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { } } -impl HashStable for Crate<'_> { - fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { - let Crate { owners: _, opt_hir_hash } = self; - opt_hir_hash.unwrap().hash_stable(hcx, hasher) - } -} - impl HashStable for HashIgnoredAttrId { fn hash_stable(&self, _hcx: &mut HirCtx, _hasher: &mut StableHasher) { /* we don't hash HashIgnoredAttrId, we ignore them */ diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs index dd685c44ec47..07c33eb935f2 100644 --- a/compiler/rustc_hir/src/target.rs +++ b/compiler/rustc_hir/src/target.rs @@ -1,8 +1,4 @@ -//! This module implements some validity checks for attributes. -//! In particular it verifies that `#[inline]` and `#[repr]` attributes are -//! attached to items that actually support them and if there are -//! conflicts between multiple such attributes attached to the same -//! item. +//! This module lists attribute targets, with conversions from other types. use std::fmt::{self, Display}; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 5656c4566d9f..f050fe2978ce 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -998,7 +998,7 @@ fn check_type_defn<'tcx>( item: &hir::Item<'tcx>, all_sized: bool, ) -> Result<(), ErrorGuaranteed> { - let _ = tcx.check_representability(item.owner_id.def_id); + tcx.ensure_ok().check_representability(item.owner_id.def_id); let adt_def = tcx.adt_def(item.owner_id); enter_wf_checking_ctxt(tcx, item.owner_id.def_id, |wfcx| { 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 2bba1b723353..18c9baca051b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2874,6 +2874,9 @@ fn lower_const_arg_literal( span: Span, ) -> Const<'tcx> { let tcx = self.tcx(); + + let ty = if !ty.has_infer() { Some(ty) } else { None }; + if let LitKind::Err(guar) = *kind { return ty::Const::new_error(tcx, guar); } @@ -2905,16 +2908,20 @@ fn try_lower_anon_const_lit( }; let lit_input = match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: lit.node, ty, neg: false }), + hir::ExprKind::Lit(lit) => { + Some(LitToConstInput { lit: lit.node, ty: Some(ty), neg: false }) + } hir::ExprKind::Unary(hir::UnOp::Neg, expr) => match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: lit.node, ty, neg: true }), + hir::ExprKind::Lit(lit) => { + Some(LitToConstInput { lit: lit.node, ty: Some(ty), neg: true }) + } _ => None, }, _ => None, }; lit_input.and_then(|l| { - if const_lit_matches_ty(tcx, &l.lit, l.ty, l.neg) { + if const_lit_matches_ty(tcx, &l.lit, ty, l.neg) { tcx.at(expr.span) .lit_to_const(l) .map(|value| ty::Const::new_value(tcx, value.valtree, value.ty)) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 17c7c4b76b2d..6b77169994a0 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1879,7 +1879,7 @@ fn check_expr_struct_fields( if !ocx.try_evaluate_obligations().is_empty() { return Err(TypeError::Mismatch); } - Ok(self.resolve_vars_if_possible(adt_ty)) + Ok(adt_ty) }) .ok() }); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 9faa75e18480..0471fd965cd8 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -11,7 +11,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath, is_range_literal}; +use rustc_hir::{Expr, ExprKind, FnRetTy, HirId, LangItem, Node, QPath, is_range_literal}; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, PermitVariants}; use rustc_index::IndexVec; @@ -276,12 +276,7 @@ pub(in super::super) fn check_argument_types( // Record all the argument types, with the args // produced from the above subtyping unification. - Ok(Some( - formal_input_tys - .iter() - .map(|&ty| self.resolve_vars_if_possible(ty)) - .collect(), - )) + Ok(Some(formal_input_tys.to_vec())) }) .ok() }) @@ -1587,6 +1582,45 @@ struct MismatchedParam<'a> { } } err.span_note(spans, format!("{} defined here", self.tcx.def_descr(def_id))); + if let DefKind::Fn | DefKind::AssocFn = self.tcx.def_kind(def_id) + && let ty::Param(_) = + self.tcx.fn_sig(def_id).instantiate_identity().skip_binder().output().kind() + && let parent = self.tcx.hir_get_parent_item(call_expr.hir_id).def_id + && let Some((output, body_id)) = match self.tcx.hir_node_by_def_id(parent) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Fn { sig, body, .. }, + .. + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body)), + .. + }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(sig, body), + .. + }) => Some((sig.decl.output, body)), + _ => None, + } + && let expr = self.tcx.hir_body(*body_id).value + && (expr.peel_blocks().span == call_expr.span + || matches!( + self.tcx.parent_hir_node(call_expr.hir_id), + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. }) + )) + { + err.span_label( + output.span(), + match output { + FnRetTy::DefaultReturn(_) => format!( + "this implicit `()` return type influences the call expression's return type" + ), + FnRetTy::Return(_) => { + "this return type influences the call expression's return type" + .to_string() + } + }, + ); + } } else if let Some(hir::Node::Expr(e)) = self.tcx.hir_get_if_local(def_id) && let hir::ExprKind::Closure(hir::Closure { body, .. }) = &e.kind { diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 9d496c66f968..893cd025a089 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -3460,13 +3460,13 @@ fn consider_suggesting_derives_for_ty( let diagnostic_name = self.tcx.get_diagnostic_name(trait_pred.def_id())?; let can_derive = match diagnostic_name { + sym::Copy | sym::Clone => true, + _ if adt.is_union() => false, sym::Default | sym::Eq | sym::PartialEq | sym::Ord | sym::PartialOrd - | sym::Clone - | sym::Copy | sym::Hash | sym::Debug => true, _ => false, diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 70e3d7dc9fef..3487286d5883 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -140,6 +140,9 @@ pub fn sup( ty::Contravariant, actual, self.cause.span, + &mut |alias| { + self.infcx.try_eagerly_normalize_alias(self.param_env, self.cause.span, alias) + }, ) .map(|goals| self.goals_to_obligations(goals)) } else { @@ -173,6 +176,9 @@ pub fn sub( ty::Covariant, actual, self.cause.span, + &mut |alias| { + self.infcx.try_eagerly_normalize_alias(self.param_env, self.cause.span, alias) + }, ) .map(|goals| self.goals_to_obligations(goals)) } else { @@ -225,6 +231,9 @@ pub fn eq_trace( ty::Invariant, actual, self.cause.span, + &mut |alias| { + self.infcx.try_eagerly_normalize_alias(self.param_env, self.cause.span, alias) + }, ) .map(|goals| self.goals_to_obligations(goals)) } else { diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index b57306536260..e7082f961d0b 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1,5 +1,5 @@ use std::cell::{Cell, RefCell}; -use std::fmt; +use std::{fmt, mem}; pub use at::DefineOpaqueTypes; use free_regions::RegionRelations; @@ -21,6 +21,7 @@ use rustc_macros::extension; pub use rustc_macros::{TypeFoldable, TypeVisitable}; use rustc_middle::bug; +use rustc_middle::hooks::TypeErasedInfcx; use rustc_middle::infer::canonical::{CanonicalQueryInput, CanonicalVarValues}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::select; @@ -1250,6 +1251,36 @@ pub fn resolve_vars_if_possible(&self, value: T) -> T value.fold_with(&mut r) } + /// Normally, we shallow-resolve unresolved type variables to their root + /// variables. This is mainly done for performance reasons, and in most + /// cases resolving to the root variable (instead of the variable itself) + /// does not affect type inference. + /// + /// However, there is an exceptional case: *fudging*. Fudging is intended + /// to guide inference rather than impose hard requirements. But our current + /// handling here is somewhat janky. + /// + /// In particular, inference variables that are considered equal within the + /// fudging scope may not remain equal outside of it. This makes it observable + /// which inference variable we resolve to. For backwards compatibility, we + /// avoid resolving to the root variable by using this function inside the + /// fudge instead of [`InferCtxt::resolve_vars_if_possible`]. + /// + /// See #153869 for more details. + pub fn resolve_vars_if_possible_for_fudging(&self, value: T) -> T + where + T: TypeFoldable>, + { + if let Err(guar) = value.error_reported() { + self.set_tainted_by_errors(guar); + } + if !value.has_non_region_infer() { + return value; + } + let mut r = resolve::OpportunisticVarResolver::new_for_fudging(self); + value.fold_with(&mut r) + } + pub fn resolve_numeric_literals_with_default(&self, value: T) -> T where T: TypeFoldable>, @@ -1498,6 +1529,17 @@ pub fn ty_or_const_infer_var_changed(&self, infer_var: TyOrConstInferVar) -> boo } } + pub fn try_eagerly_normalize_alias<'a>( + &'a self, + param_env: ty::ParamEnv<'tcx>, + span: Span, + alias: ty::AliasTy<'tcx>, + ) -> Ty<'tcx> { + let erased = + unsafe { mem::transmute::<&'a InferCtxt<'tcx>, TypeErasedInfcx<'a, 'tcx>>(self) }; + self.tcx.try_eagerly_normalize_alias(erased, param_env, span, alias) + } + /// Attach a callback to be invoked on each root obligation evaluated in the new trait solver. pub fn attach_obligation_inspector(&self, inspector: ObligationInspector<'tcx>) { debug_assert!( diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs index 69c090b662e5..b2d591327fea 100644 --- a/compiler/rustc_infer/src/infer/relate/generalize.rs +++ b/compiler/rustc_infer/src/infer/relate/generalize.rs @@ -51,7 +51,7 @@ impl<'tcx> InferCtxt<'tcx> { /// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all /// other usecases (i.e. setting the value of a type var). #[instrument(level = "debug", skip(self, relation))] - pub fn instantiate_ty_var>>( + pub fn instantiate_ty_var>( &self, relation: &mut R, target_is_expected: bool, @@ -61,29 +61,56 @@ pub fn instantiate_ty_var>>( ) -> RelateResult<'tcx, ()> { debug_assert!(self.inner.borrow_mut().type_variables().probe(target_vid).is_unknown()); - // Generalize `source_ty` depending on the current variance. As an example, assume - // `?target <: &'x ?1`, where `'x` is some free region and `?1` is an inference - // variable. - // - // Then the `generalized_ty` would be `&'?2 ?3`, where `'?2` and `?3` are fresh - // region/type inference variables. - // - // We then relate `generalized_ty <: source_ty`, adding constraints like `'x: '?2` and - // `?1 <: ?3`. - let Generalization { value_may_be_infer: generalized_ty } = self.generalize( - relation.span(), - relation.structurally_relate_aliases(), - target_vid, - instantiation_variance, - source_ty, - )?; + let generalized_ty = if self.next_trait_solver() + && matches!(relation.structurally_relate_aliases(), StructurallyRelateAliases::No) + && let ty::Alias(_, alias) = source_ty.kind() + { + let normalized_alias = relation.try_eagerly_normalize_alias(*alias); - // Constrain `b_vid` to the generalized type `generalized_ty`. - if let &ty::Infer(ty::TyVar(generalized_vid)) = generalized_ty.kind() { - self.inner.borrow_mut().type_variables().equate(target_vid, generalized_vid); + if normalized_alias.is_ty_var() { + normalized_alias + } else { + let Generalization { value_may_be_infer: generalized_ty } = self.generalize( + relation.span(), + GeneralizerState::ShallowStructurallyRelateAliases, + target_vid, + instantiation_variance, + normalized_alias, + &mut |alias| relation.try_eagerly_normalize_alias(alias), + )?; + + // The only way to get a tyvar back is if the outermost type is an alias. + // However, here, though we know it *is* an alias, we initialize the generalizer + // with `ShallowStructurallyRelateAliases` so we treat the outermost alias as rigid, + // ensuring this is never a tyvar. + assert!(!generalized_ty.is_ty_var()); + + generalized_ty + } } else { - self.inner.borrow_mut().type_variables().instantiate(target_vid, generalized_ty); - } + // Generalize `source_ty` depending on the current variance. As an example, assume + // `?target <: &'x ?1`, where `'x` is some free region and `?1` is an inference + // variable. + // + // Then the `generalized_ty` would be `&'?2 ?3`, where `'?2` and `?3` are fresh + // region/type inference variables. + // + // We then relate `generalized_ty <: source_ty`, adding constraints like `'x: '?2` and + // `?1 <: ?3`. + let Generalization { value_may_be_infer: generalized_ty } = self.generalize( + relation.span(), + match relation.structurally_relate_aliases() { + StructurallyRelateAliases::No => GeneralizerState::Default, + StructurallyRelateAliases::Yes => GeneralizerState::StructurallyRelateAliases, + }, + target_vid, + instantiation_variance, + source_ty, + &mut |alias| relation.try_eagerly_normalize_alias(alias), + )?; + + generalized_ty + }; // Finally, relate `generalized_ty` to `source_ty`, as described in previous comment. // @@ -91,7 +118,10 @@ pub fn instantiate_ty_var>>( // relations wind up attributed to the same spans. We need // to associate causes/spans with each of the relations in // the stack to get this right. - if generalized_ty.is_ty_var() { + if let &ty::Infer(ty::TyVar(generalized_vid)) = generalized_ty.kind() { + // Constrain `b_vid` to the generalized type variable. + self.inner.borrow_mut().type_variables().equate(target_vid, generalized_vid); + // This happens for cases like `::Assoc == ?0`. // We can't instantiate `?0` here as that would result in a // cyclic type. We instead delay the unification in case @@ -132,6 +162,9 @@ pub fn instantiate_ty_var>>( } } } else { + // Constrain `b_vid` to the generalized type `generalized_ty`. + self.inner.borrow_mut().type_variables().instantiate(target_vid, generalized_ty); + // NOTE: The `instantiation_variance` is not the same variance as // used by the relation. When instantiating `b`, `target_is_expected` // is flipped and the `instantiation_variance` is also flipped. To @@ -206,10 +239,14 @@ pub(crate) fn instantiate_const_var // constants and generic expressions are not yet handled correctly. let Generalization { value_may_be_infer: generalized_ct } = self.generalize( relation.span(), - relation.structurally_relate_aliases(), + match relation.structurally_relate_aliases() { + StructurallyRelateAliases::No => GeneralizerState::Default, + StructurallyRelateAliases::Yes => GeneralizerState::StructurallyRelateAliases, + }, target_vid, ty::Invariant, source_ct, + &mut |alias| relation.try_eagerly_normalize_alias(alias), )?; debug_assert!(!generalized_ct.is_ct_infer()); @@ -245,10 +282,11 @@ pub(crate) fn instantiate_const_var fn generalize> + Relate>>( &self, span: Span, - structurally_relate_aliases: StructurallyRelateAliases, + initial_state: GeneralizerState, target_vid: impl Into, ambient_variance: ty::Variance, source_term: T, + normalize: &mut dyn FnMut(ty::AliasTy<'tcx>) -> Ty<'tcx>, ) -> RelateResult<'tcx, Generalization> { assert!(!source_term.has_escaping_bound_vars()); let (for_universe, root_vid) = match target_vid.into() { @@ -264,13 +302,13 @@ fn generalize> + Relate>>( let mut generalizer = Generalizer { infcx: self, span, - structurally_relate_aliases, root_vid, for_universe, root_term: source_term.into(), ambient_variance, - in_alias: false, + state: initial_state, cache: Default::default(), + normalize, }; let value_may_be_infer = generalizer.relate(source_term, source_term)?; @@ -317,6 +355,54 @@ fn visit_region(&mut self, r: ty::Region<'tcx>) { } } +/// This state determines how generalization treats aliases. +/// +/// Based on which state we're in, we treat them either as rigid or normalizable, +/// which might change depending on what types the generalization visitor encounters. +/// See `handle_alias_ty` for the logic of how we change states. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +enum GeneralizerState { + /// Treat aliases as potentially normalizable. + /// + /// This is the default state that generalization starts in, unless we're + /// treating aliases as rigid. It also means we're not currently inside an + /// alias, since then we change the state to `IncompletelyRelateAliasArgs`. + Default, + /// We enter this state when we're generalizing the arguments of a + /// potentially normalizeable alias. + /// + /// The behavior here is different between the old and the new solver: + /// + /// In the old solver, the difference between this and `Default` is needed to + /// correctly handle `::Assoc>::Assoc == ?0`. That + /// equality can hold by either normalizing the outer or the inner + /// associated type. In the old solver, we always structurally relate + /// aliases. If we we encounter an occurs check failure, we propagate the + /// failure to the outermost alias, for which we then emit a `Projection` + /// goal instead. + /// + /// In the new solver, we rarely get into this state. + /// When we encounter aliases we instead attempt to normalize them, and treat + /// them as rigid using `ShallowStructurallyRelate`. Only when an alias has + /// escaping bound variables do we continue with similar logic to the old + /// solver, except now we also explicitly relate the type and consts in the + /// arguments of aliases while in this mode. + /// + /// FIXME: Because we relate the type and consts in the arguments of aliases + /// while in this mode, this is incomplete. + IncompletelyRelateAliasArgs, + /// During generalization, when we encounter aliases, we will first attempt + /// to normalize them when we're using the next trait solver. We can now + /// treat the normalized alias as rigid, but only for "one layer", hence + /// shallow. New aliases encountered inside the arguments of the outer alias + /// should once again be related as normal. + ShallowStructurallyRelateAliases, + /// Treat aliases as rigid when relating them. + /// + /// This corresponds to `relation.structurally_relate_aliases()`. + StructurallyRelateAliases, +} + /// The "generalizer" is used when handling inference variables. /// /// The basic strategy for handling a constraint like `?A <: B` is to @@ -335,10 +421,6 @@ struct Generalizer<'me, 'tcx> { span: Span, - /// Whether aliases should be related structurally. If not, we have to - /// be careful when generalizing aliases. - structurally_relate_aliases: StructurallyRelateAliases, - /// The vid of the type variable that is in the process of being /// instantiated. If we find this within the value we are folding, /// that means we would have created a cyclic value. @@ -356,14 +438,17 @@ struct Generalizer<'me, 'tcx> { /// some other type. What will be the variance at this point? ambient_variance: ty::Variance, - /// This is set once we're generalizing the arguments of an alias. + /// This field keeps track of how we treat aliases during generalization. /// - /// This is necessary to correctly handle - /// `::Assoc>::Assoc == ?0`. This equality can - /// hold by either normalizing the outer or the inner associated type. - in_alias: bool, + /// Refer to [`GeneralizerState`]'s docs for more information about the + /// all the possible values this can have, and when we use which. + state: GeneralizerState, - cache: SsoHashMap<(Ty<'tcx>, ty::Variance, bool), Ty<'tcx>>, + cache: SsoHashMap<(Ty<'tcx>, ty::Variance, GeneralizerState), Ty<'tcx>>, + + /// Normalize an alias in the trait solver. + /// If normalization fails, a fresh infer var is returned. + normalize: &'me mut dyn FnMut(ty::AliasTy<'tcx>) -> Ty<'tcx>, } impl<'tcx> Generalizer<'_, 'tcx> { @@ -399,27 +484,51 @@ fn next_ty_var_for_alias(&self) -> Ty<'tcx> { /// continue generalizing the alias. This ends up pulling down the universe of the /// inference variable and is incomplete in case the alias would normalize to a type /// which does not mention that inference variable. - fn generalize_alias_ty( + fn handle_alias_ty( &mut self, + alias_ty: Ty<'tcx>, alias: ty::AliasTy<'tcx>, ) -> Result, TypeError<'tcx>> { - // We do not eagerly replace aliases with inference variables if they have - // escaping bound vars, see the method comment for details. However, when we - // are inside of an alias with escaping bound vars replacing nested aliases - // with inference variables can cause incorrect ambiguity. - // - // cc trait-system-refactor-initiative#110 - if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() && !self.in_alias { - return Ok(self.next_ty_var_for_alias()); + match self.state { + GeneralizerState::ShallowStructurallyRelateAliases => { + // We can switch back to default, we've treated one layer as rigid by doing this operation. + self.state = GeneralizerState::Default; + let res = relate::structurally_relate_tys(self, alias_ty, alias_ty); + self.state = GeneralizerState::ShallowStructurallyRelateAliases; + return res; + } + GeneralizerState::StructurallyRelateAliases => { + return relate::structurally_relate_tys(self, alias_ty, alias_ty); + } + GeneralizerState::Default + if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() => + { + // We do not eagerly replace aliases with inference variables if they have + // escaping bound vars, see the method comment for details. However, when we + // are inside of an alias with escaping bound vars replacing nested aliases + // with inference variables can cause incorrect ambiguity. + // + // cc trait-system-refactor-initiative#110 + let normalized_alias = (self.normalize)(alias); + + self.state = GeneralizerState::ShallowStructurallyRelateAliases; + // recursively generalize, treat the outer alias as rigid to avoid infinite recursion + let res = self.relate(normalized_alias, normalized_alias); + + // only one way to get here + self.state = GeneralizerState::Default; + + return res; + } + GeneralizerState::Default | GeneralizerState::IncompletelyRelateAliasArgs => {} } - let is_nested_alias = mem::replace(&mut self.in_alias, true); + let previous_state = + mem::replace(&mut self.state, GeneralizerState::IncompletelyRelateAliasArgs); let result = match self.relate(alias, alias) { Ok(alias) => Ok(alias.to_ty(self.cx())), - Err(e) => { - if is_nested_alias { - return Err(e); - } else { + Err(e) => match previous_state { + GeneralizerState::Default => { let mut visitor = MaxUniverse::new(); alias.visit_with(&mut visitor); let infer_replacement_is_complete = @@ -432,9 +541,14 @@ fn generalize_alias_ty( debug!("generalization failure in alias"); Ok(self.next_ty_var_for_alias()) } - } + GeneralizerState::IncompletelyRelateAliasArgs => return Err(e), + + // Early return. + GeneralizerState::ShallowStructurallyRelateAliases + | GeneralizerState::StructurallyRelateAliases => unreachable!(), + }, }; - self.in_alias = is_nested_alias; + self.state = previous_state; result } } @@ -488,7 +602,7 @@ fn relate_with_variance>>( fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { assert_eq!(t, t2); // we are misusing TypeRelation here; both LHS and RHS ought to be == - if let Some(&result) = self.cache.get(&(t, self.ambient_variance, self.in_alias)) { + if let Some(&result) = self.cache.get(&(t, self.ambient_variance, self.state)) { return Ok(result); } @@ -536,6 +650,7 @@ fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { // of each other. This is currently only used for diagnostics. // To see why, see the docs in the `type_variables` module. inner.type_variables().sub_unify(vid, new_var_id); + // If we're in the new solver and create a new inference // variable inside of an alias we eagerly constrain that // inference variable to prevent unexpected ambiguity errors. @@ -553,9 +668,15 @@ fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { // cc trait-system-refactor-initiative#108 if self.infcx.next_trait_solver() && !matches!(self.infcx.typing_mode(), TypingMode::Coherence) - && self.in_alias { - inner.type_variables().equate(vid, new_var_id); + match self.state { + GeneralizerState::IncompletelyRelateAliasArgs => { + inner.type_variables().equate(vid, new_var_id); + } + GeneralizerState::Default + | GeneralizerState::ShallowStructurallyRelateAliases + | GeneralizerState::StructurallyRelateAliases => {} + } } debug!("replacing original vid={:?} with new={:?}", vid, new_var_id); @@ -584,15 +705,12 @@ fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { } } - ty::Alias(_, data) => match self.structurally_relate_aliases { - StructurallyRelateAliases::No => self.generalize_alias_ty(data), - StructurallyRelateAliases::Yes => relate::structurally_relate_tys(self, t, t), - }, + ty::Alias(_, data) => self.handle_alias_ty(t, data), _ => relate::structurally_relate_tys(self, t, t), }?; - self.cache.insert((t, self.ambient_variance, self.in_alias), g); + self.cache.insert((t, self.ambient_variance, self.state), g); Ok(g) } @@ -683,9 +801,15 @@ fn consts( // for more details. if self.infcx.next_trait_solver() && !matches!(self.infcx.typing_mode(), TypingMode::Coherence) - && self.in_alias { - variable_table.union(vid, new_var_id); + match self.state { + GeneralizerState::IncompletelyRelateAliasArgs => { + variable_table.union(vid, new_var_id); + } + GeneralizerState::Default + | GeneralizerState::ShallowStructurallyRelateAliases + | GeneralizerState::StructurallyRelateAliases => {} + } } Ok(ty::Const::new_var(self.cx(), new_var_id)) } diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs index a05e2d40e829..7e480df7dda6 100644 --- a/compiler/rustc_infer/src/infer/relate/lattice.rs +++ b/compiler/rustc_infer/src/infer/relate/lattice.rs @@ -299,4 +299,8 @@ fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { ty::AliasRelationDirection::Equate, ))]); } + + fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTy<'tcx>) -> Ty<'tcx> { + self.infcx.try_eagerly_normalize_alias(self.param_env(), self.span(), alias) + } } diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs index 96a0375f5fba..67f9dc69a4a6 100644 --- a/compiler/rustc_infer/src/infer/relate/type_relating.rs +++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs @@ -396,4 +396,13 @@ fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) { } })]); } + + fn try_eagerly_normalize_alias( + &mut self, + _alias: rustc_type_ir::AliasTy< as rustc_type_ir::InferCtxtLike>::Interner>, + ) -> < as rustc_type_ir::InferCtxtLike>::Interner as rustc_type_ir::Interner>::Ty + { + // We only try to eagerly normalize aliases if we're using the new solver. + unreachable!() + } } diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs index 13df23a39b96..917d7b6653e0 100644 --- a/compiler/rustc_infer/src/infer/resolve.rs +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -17,6 +17,9 @@ /// points for correctness. pub struct OpportunisticVarResolver<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, + /// If true, we don't resolve ty/const vars to their roots. + /// See comments on [`InferCtxt::resolve_vars_if_possible_for_fudging`] + for_fudging: bool, /// We're able to use a cache here as the folder does /// not have any mutable state. cache: DelayedMap, Ty<'tcx>>, @@ -25,7 +28,12 @@ pub struct OpportunisticVarResolver<'a, 'tcx> { impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> { #[inline] pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self { - OpportunisticVarResolver { infcx, cache: Default::default() } + OpportunisticVarResolver { infcx, for_fudging: false, cache: Default::default() } + } + + #[inline] + pub fn new_for_fudging(infcx: &'a InferCtxt<'tcx>) -> Self { + OpportunisticVarResolver { infcx, for_fudging: true, cache: Default::default() } } } @@ -43,6 +51,7 @@ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { } else { let shallow = self.infcx.shallow_resolve(t); let res = shallow.super_fold_with(self); + let res = if self.for_fudging && res.is_ty_var() { t } else { res }; assert!(self.cache.insert(t, res)); res } @@ -52,8 +61,11 @@ fn fold_const(&mut self, ct: Const<'tcx>) -> Const<'tcx> { if !ct.has_non_region_infer() { ct // micro-optimize -- if there is nothing in this const that this fold affects... } else { - let ct = self.infcx.shallow_resolve_const(ct); - ct.super_fold_with(self) + let res = self.infcx.shallow_resolve_const(ct); + if self.for_fudging && res.is_ct_infer() { + return ct; + }; + res.super_fold_with(self) } } diff --git a/compiler/rustc_infer/src/infer/snapshot/fudge.rs b/compiler/rustc_infer/src/infer/snapshot/fudge.rs index 6709c822dc7b..56cae2c1e396 100644 --- a/compiler/rustc_infer/src/infer/snapshot/fudge.rs +++ b/compiler/rustc_infer/src/infer/snapshot/fudge.rs @@ -101,7 +101,7 @@ pub fn fudge_inference_if_ok(&self, f: F) -> Result // going to be popped, so we will have to // eliminate any references to them. let snapshot_vars = SnapshotVarData::new(self, variable_lengths); - Ok((snapshot_vars, self.resolve_vars_if_possible(value))) + Ok((snapshot_vars, self.resolve_vars_if_possible_for_fudging(value))) })?; // At this point, we need to replace any of the now-popped diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index cf62d4223211..28f51095966d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -21,7 +21,7 @@ use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap}; use rustc_hir::definitions::Definitions; use rustc_hir::limit::Limit; -use rustc_hir::{Attribute, find_attr}; +use rustc_hir::{Attribute, MaybeOwner, find_attr}; use rustc_incremental::setup_dep_graph; use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore, unerased_lint_store}; use rustc_metadata::EncodedMetadata; @@ -878,6 +878,10 @@ pub fn write_interface<'tcx>(tcx: TyCtxt<'tcx>) { let providers = &mut Providers::default(); providers.queries.analysis = analysis; providers.queries.hir_crate = rustc_ast_lowering::lower_to_hir; + providers.queries.lower_delayed_owner = rustc_ast_lowering::lower_delayed_owner; + // `delayed_owner` is fed during `lower_delayed_owner`, by default it returns phantom, + // as if this query was not fed it means that `MaybeOwner` does not exist for provided LocalDefId. + providers.queries.delayed_owner = |_, _| MaybeOwner::Phantom; providers.queries.resolver_for_lowering_raw = resolver_for_lowering_raw; providers.queries.stripped_cfg_items = |tcx, _| &tcx.resolutions(()).stripped_cfg_items[..]; providers.queries.resolutions = |tcx, ()| tcx.resolver_for_lowering_raw(()).1; @@ -900,7 +904,7 @@ pub fn write_interface<'tcx>(tcx: TyCtxt<'tcx>) { rustc_hir_typeck::provide(&mut providers.queries); ty::provide(&mut providers.queries); traits::provide(&mut providers.queries); - solve::provide(&mut providers.queries); + solve::provide(providers); rustc_passes::provide(&mut providers.queries); rustc_traits::provide(&mut providers.queries); rustc_ty_utils::provide(&mut providers.queries); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 6c2924dfe9bd..f9d583bdf18c 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -637,7 +637,6 @@ macro_rules! tracked { tracked!(profile_use, Some(PathBuf::from("abc"))); tracked!(relocation_model, Some(RelocModel::Pic)); tracked!(relro_level, Some(RelroLevel::Full)); - tracked!(soft_float, true); tracked!(split_debuginfo, Some(SplitDebuginfo::Packed)); tracked!(symbol_mangling_version, Some(SymbolManglingVersion::V0)); tracked!(target_cpu, Some(String::from("abc"))); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index c5344ee66cd0..0cd0275f96bb 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -183,7 +183,6 @@ pub(crate) fn run_in_thread_pool_with_globals< use std::process; use rustc_data_structures::defer; - use rustc_data_structures::sync::FromDyn; use rustc_middle::ty::tls; use rustc_query_impl::break_query_cycles; @@ -191,7 +190,7 @@ pub(crate) fn run_in_thread_pool_with_globals< let registry = sync::Registry::new(std::num::NonZero::new(threads).unwrap()); - if !sync::is_dyn_thread_safe() { + let Some(proof) = sync::check_dyn_thread_safe() else { return run_in_thread_with_globals( thread_stack_size, edition, @@ -204,9 +203,9 @@ pub(crate) fn run_in_thread_pool_with_globals< f(current_gcx, jobserver_proxy) }, ); - } + }; - let current_gcx = FromDyn::from(CurrentGcx::new()); + let current_gcx = proof.derive(CurrentGcx::new()); let current_gcx2 = current_gcx.clone(); let proxy = Proxy::new(); @@ -278,7 +277,7 @@ pub(crate) fn run_in_thread_pool_with_globals< // `Send` in the parallel compiler. rustc_span::create_session_globals_then(edition, extra_symbols, Some(sm_inputs), || { rustc_span::with_session_globals(|session_globals| { - let session_globals = FromDyn::from(session_globals); + let session_globals = proof.derive(session_globals); builder .build_scoped( // Initialize each new worker thread when created. diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 458553fa747c..d5cd26dd75e3 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -1,6 +1,5 @@ use std::borrow::Cow; -use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, Level, elided_lifetime_in_path_suggestion, @@ -10,7 +9,6 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::lint::BuiltinLintDiag; -use rustc_span::BytePos; use tracing::debug; use crate::lints; @@ -28,32 +26,6 @@ pub struct DecorateBuiltinLint<'sess, 'tcx> { impl<'a> Diagnostic<'a, ()> for DecorateBuiltinLint<'_, '_> { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { match self.diagnostic { - BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => { - let spans: Vec<_> = content - .char_indices() - .filter_map(|(i, c)| { - TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { - let lo = comment_span.lo() + BytePos(2 + i as u32); - (c, comment_span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) - }) - }) - .collect(); - let characters = spans - .iter() - .map(|&(c, span)| lints::UnicodeCharNoteSub { span, c_debug: format!("{c:?}") }) - .collect(); - let suggestions = (!spans.is_empty()).then_some(lints::UnicodeTextFlowSuggestion { - spans: spans.iter().map(|(_c, span)| *span).collect(), - }); - - lints::UnicodeTextFlow { - comment_span, - characters, - suggestions, - num_codepoints: spans.len(), - } - .into_diag(dcx, level) - } BuiltinLintDiag::AbsPathWithModule(mod_span) => { let (replacement, applicability) = match self.sess.source_map().span_to_snippet(mod_span) { @@ -153,30 +125,6 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { } .into_diag(dcx, level) } - BuiltinLintDiag::ReservedPrefix(label_span, prefix) => lints::ReservedPrefix { - label: label_span, - suggestion: label_span.shrink_to_hi(), - prefix, - } - .into_diag(dcx, level), - BuiltinLintDiag::RawPrefix(label_span) => { - lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() } - .into_diag(dcx, level) - } - BuiltinLintDiag::ReservedString { is_string, suggestion } => { - if is_string { - lints::ReservedString { suggestion }.into_diag(dcx, level) - } else { - lints::ReservedMultihash { suggestion }.into_diag(dcx, level) - } - } - BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => lints::BreakWithLabelAndLoop { - sub: lints::BreakWithLabelAndLoopSub { - left: sugg_span.shrink_to_lo(), - right: sugg_span.shrink_to_hi(), - }, - } - .into_diag(dcx, level), BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => { let suggestion = match sugg { Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd { @@ -498,6 +446,18 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { &AttributeLintKind::MissingOptionsForOnConst => { lints::MissingOptionsForOnConstAttr.into_diag(dcx, level) } + &AttributeLintKind::MalformedOnMoveAttr { span } => { + lints::MalformedOnMoveAttrLint { span }.into_diag(dcx, level) + } + &AttributeLintKind::OnMoveMalformedFormatLiterals { name } => { + lints::OnMoveMalformedFormatLiterals { name }.into_diag(dcx, level) + } + &AttributeLintKind::OnMoveMalformedAttrExpectedLiteralOrDelimiter => { + lints::OnMoveMalformedAttrExpectedLiteralOrDelimiter.into_diag(dcx, level) + } + &AttributeLintKind::MissingOptionsForOnMove => { + lints::MissingOptionsForOnMoveAttr.into_diag(dcx, level) + } } } } diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index fe95a682c637..a7c5943c250b 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -49,6 +49,11 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let Some((pat, arg)) = extract_for_loop(expr) else { return }; + // Do not put suggestions for external macros. + if pat.span.from_expansion() { + return; + } + let arg_span = arg.span.source_callsite(); let ty = cx.typeck_results().expr_ty(arg); @@ -77,6 +82,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { }; let sub = if let Some(recv) = extract_iterator_next_call(cx, arg) + && recv.span.can_be_used_for_suggestions() + && recv.span.between(arg_span.shrink_to_hi()).can_be_used_for_suggestions() && let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span) { ForLoopsOverFalliblesLoopSub::RemoveNext { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index d8b62e81b0cb..d5b5a50d281a 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3019,46 +3019,6 @@ pub(crate) struct IllFormedAttributeInput { pub docs: &'static str, } -#[derive(Diagnostic)] -#[diag("unicode codepoint changing visible direction of text present in comment")] -#[note( - "these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen" -)] -pub(crate) struct UnicodeTextFlow { - #[label( - "{$num_codepoints -> - [1] this comment contains an invisible unicode text flow control codepoint - *[other] this comment contains invisible unicode text flow control codepoints - }" - )] - pub comment_span: Span, - #[subdiagnostic] - pub characters: Vec, - #[subdiagnostic] - pub suggestions: Option, - - pub num_codepoints: usize, -} - -#[derive(Subdiagnostic)] -#[label("{$c_debug}")] -pub(crate) struct UnicodeCharNoteSub { - #[primary_span] - pub span: Span, - pub c_debug: String, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "if their presence wasn't intentional, you can remove them", - applicability = "machine-applicable", - style = "hidden" -)] -pub(crate) struct UnicodeTextFlowSuggestion { - #[suggestion_part(code = "")] - pub spans: Vec, -} - #[derive(Diagnostic)] #[diag( "absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition" @@ -3192,52 +3152,6 @@ pub(crate) struct PatternsInFnsWithoutBodySub { pub ident: Ident, } -#[derive(Diagnostic)] -#[diag("prefix `{$prefix}` is unknown")] -pub(crate) struct ReservedPrefix { - #[label("unknown prefix")] - pub label: Span, - #[suggestion( - "insert whitespace here to avoid this being parsed as a prefix in Rust 2021", - code = " ", - applicability = "machine-applicable" - )] - pub suggestion: Span, - - pub prefix: String, -} - -#[derive(Diagnostic)] -#[diag("prefix `'r` is reserved")] -pub(crate) struct RawPrefix { - #[label("reserved prefix")] - pub label: Span, - #[suggestion( - "insert whitespace here to avoid this being parsed as a prefix in Rust 2021", - code = " ", - applicability = "machine-applicable" - )] - pub suggestion: Span, -} - -#[derive(Diagnostic)] -#[diag( - "this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression" -)] -pub(crate) struct BreakWithLabelAndLoop { - #[subdiagnostic] - pub sub: BreakWithLabelAndLoopSub, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion("wrap this expression in parentheses", applicability = "machine-applicable")] -pub(crate) struct BreakWithLabelAndLoopSub { - #[suggestion_part(code = "(")] - pub left: Span, - #[suggestion_part(code = ")")] - pub right: Span, -} - #[derive(Diagnostic)] #[diag("where clause not allowed here")] #[note("see issue #89122 for more information")] @@ -3423,28 +3337,6 @@ pub(crate) enum MutRefSugg { #[diag("`use` of a local item without leading `self::`, `super::`, or `crate::`")] pub(crate) struct UnqualifiedLocalImportsDiag; -#[derive(Diagnostic)] -#[diag("will be parsed as a guarded string in Rust 2024")] -pub(crate) struct ReservedString { - #[suggestion( - "insert whitespace here to avoid this being parsed as a guarded string in Rust 2024", - code = " ", - applicability = "machine-applicable" - )] - pub suggestion: Span, -} - -#[derive(Diagnostic)] -#[diag("reserved token in Rust 2024")] -pub(crate) struct ReservedMultihash { - #[suggestion( - "insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024", - code = " ", - applicability = "machine-applicable" - )] - pub suggestion: Span, -} - #[derive(Diagnostic)] #[diag("direct cast of function item into an integer")] pub(crate) struct FunctionCastsAsIntegerDiag<'tcx> { @@ -3953,6 +3845,11 @@ pub(crate) struct IgnoredDiagnosticOption { #[help("at least one of the `message`, `note` and `label` options are expected")] pub(crate) struct MissingOptionsForOnConstAttr; +#[derive(Diagnostic)] +#[diag("missing options for `on_move` attribute")] +#[help("at least one of the `message`, `note` and `label` options are expected")] +pub(crate) struct MissingOptionsForOnMoveAttr; + #[derive(Diagnostic)] #[diag("malformed `on_unimplemented` attribute")] #[help("only `message`, `note` and `label` are allowed as options")] @@ -3973,3 +3870,27 @@ pub(crate) struct MalformedOnConstAttrLint { #[diag("`Eq::assert_receiver_is_total_eq` should never be implemented by hand")] #[note("this method was used to add checks to the `Eq` derive macro")] pub(crate) struct EqInternalMethodImplemented; + +#[derive(Diagnostic)] +#[diag("unknown or malformed `on_move` attribute")] +#[help( + "only `message`, `note` and `label` are allowed as options. Their values must be string literals" +)] +pub(crate) struct MalformedOnMoveAttrLint { + #[label("invalid option found here")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag("unknown parameter `{$name}`")] +#[help("expect `Self` as format argument")] +pub(crate) struct OnMoveMalformedFormatLiterals { + pub name: Symbol, +} + +#[derive(Diagnostic)] +#[diag("expected a literal or missing delimiter")] +#[help( + "only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma" +)] +pub(crate) struct OnMoveMalformedAttrExpectedLiteralOrDelimiter; diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 1492df50a418..e2e4ffc64bea 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -678,16 +678,7 @@ pub enum BuiltinLintDiag { ident: Ident, is_foreign: bool, }, - ReservedPrefix(Span, String), - /// `'r#` in edition < 2021. - RawPrefix(Span), /// `##` or `#"` in edition < 2024. - ReservedString { - is_string: bool, - suggestion: Span, - }, - BreakWithLabelAndLoop(Span), - UnicodeTextFlow(Span, String), DeprecatedWhereclauseLocation(Span, Option<(Span, String)>), SingleUseLifetime { /// Span of the parameter which declares this lifetime. @@ -840,6 +831,9 @@ pub enum AttributeLintKind { MalformedOnConstAttr { span: Span, }, + MalformedOnMoveAttr { + span: Span, + }, MalformedDiagnosticFormat { warning: FormatWarning, }, @@ -855,6 +849,11 @@ pub enum AttributeLintKind { }, MissingOptionsForOnUnimplemented, MissingOptionsForOnConst, + MissingOptionsForOnMove, + OnMoveMalformedFormatLiterals { + name: Symbol, + }, + OnMoveMalformedAttrExpectedLiteralOrDelimiter, } #[derive(Debug, Clone, HashStable_Generic)] diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index a803d86f2771..b75236b35230 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -140,13 +140,13 @@ struct CacheOnDiskIf { /// See `rustc_middle::query::modifiers` for documentation of each query modifier. struct QueryModifiers { // tidy-alphabetical-start - anon: Option, arena_cache: Option, cache_on_disk_if: Option, depth_limit: Option, desc: Desc, eval_always: Option, feedable: Option, + no_force: Option, no_hash: Option, separate_provide_extern: Option, // tidy-alphabetical-end @@ -156,8 +156,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { let mut arena_cache = None; let mut cache_on_disk_if = None; let mut desc = None; + let mut no_force = None; let mut no_hash = None; - let mut anon = None; let mut eval_always = None; let mut depth_limit = None; let mut separate_provide_extern = None; @@ -189,10 +189,10 @@ macro_rules! try_insert { try_insert!(cache_on_disk_if = CacheOnDiskIf { modifier, block }); } else if modifier == "arena_cache" { try_insert!(arena_cache = modifier); + } else if modifier == "no_force" { + try_insert!(no_force = modifier); } else if modifier == "no_hash" { try_insert!(no_hash = modifier); - } else if modifier == "anon" { - try_insert!(anon = modifier); } else if modifier == "eval_always" { try_insert!(eval_always = modifier); } else if modifier == "depth_limit" { @@ -212,8 +212,8 @@ macro_rules! try_insert { arena_cache, cache_on_disk_if, desc, + no_force, no_hash, - anon, eval_always, depth_limit, separate_provide_extern, @@ -243,24 +243,24 @@ fn returns_error_guaranteed(ret_ty: &ReturnType) -> bool { fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { let QueryModifiers { // tidy-alphabetical-start - anon, arena_cache, cache_on_disk_if, depth_limit, desc: _, eval_always, feedable, + no_force, no_hash, separate_provide_extern, // tidy-alphabetical-end } = &query.modifiers; - let anon = anon.is_some(); let arena_cache = arena_cache.is_some(); let cache_on_disk = cache_on_disk_if.is_some(); let depth_limit = depth_limit.is_some(); let eval_always = eval_always.is_some(); let feedable = feedable.is_some(); + let no_force = no_force.is_some(); let no_hash = no_hash.is_some(); let returns_error_guaranteed = returns_error_guaranteed(&query.return_ty); let separate_provide_extern = separate_provide_extern.is_some(); @@ -273,12 +273,12 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { query_name_span => // Search for (QMODLIST) to find all occurrences of this query modifier list. // tidy-alphabetical-start - anon: #anon, arena_cache: #arena_cache, cache_on_disk: #cache_on_disk, depth_limit: #depth_limit, eval_always: #eval_always, feedable: #feedable, + no_force: #no_force, no_hash: #no_hash, returns_error_guaranteed: #returns_error_guaranteed, separate_provide_extern: #separate_provide_extern, @@ -387,13 +387,15 @@ macro_rules! doc_link { } doc_link!( + // tidy-alphabetical-start arena_cache, - no_hash, - anon, - eval_always, depth_limit, - separate_provide_extern, + eval_always, feedable, + no_force, + no_hash, + separate_provide_extern, + // tidy-alphabetical-end ); let name = &query.name; @@ -476,11 +478,6 @@ fn #name(#key_ty) #return_ty }); if let Some(feedable) = &modifiers.feedable { - assert!( - modifiers.anon.is_none(), - feedable.span(), - "Query {name} cannot be both `feedable` and `anon`." - ); assert!( modifiers.eval_always.is_none(), feedable.span(), diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 49ad6bbf8577..81f30411b25a 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -94,10 +94,14 @@ pub const fn as_usize(&self) -> usize { pub struct DepNode { pub kind: DepKind, - /// This is _typically_ a hash of the query key, but sometimes not. + /// If `kind` is a query method, then its "key fingerprint" is always a + /// stable hash of the query key. /// - /// For example, `anon` nodes have a fingerprint that is derived from their - /// dependencies instead of a key. + /// For non-query nodes, the content of this field varies: + /// - Some dep kinds always use a dummy `ZERO` fingerprint. + /// - Some dep kinds use the stable hash of some relevant key-like value. + /// - Some dep kinds use the `with_anon_task` mechanism, and set their key + /// fingerprint to a hash derived from the task's dependencies. /// /// In some cases the key value can be reconstructed from this fingerprint; /// see [`KeyFingerprintStyle`]. diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index d410d9a48cd1..d0d7d581b439 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -375,9 +375,8 @@ pub fn with_task<'tcx, A: Debug, R>( /// incorrectly marked green. /// /// FIXME: This could perhaps return a `WithDepNode` to ensure that the - /// user of this function actually performs the read; we'll have to see - /// how to make that work with `anon` in `execute_job_incr`, though. - pub fn with_anon_task_inner<'tcx, OP, R>( + /// user of this function actually performs the read. + fn with_anon_task_inner<'tcx, OP, R>( &self, tcx: TyCtxt<'tcx>, dep_kind: DepKind, diff --git a/compiler/rustc_middle/src/hir/map.rs b/compiler/rustc_middle/src/hir/map.rs index fc2a7fbd4fa9..499c6dae060b 100644 --- a/compiler/rustc_middle/src/hir/map.rs +++ b/compiler/rustc_middle/src/hir/map.rs @@ -7,7 +7,7 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::svh::Svh; -use rustc_data_structures::sync::{DynSend, DynSync, par_for_each_in, try_par_for_each_in}; +use rustc_data_structures::sync::{DynSend, DynSync, par_for_each_in, spawn, try_par_for_each_in}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefKey, DefPath, DefPathHash}; @@ -1245,7 +1245,25 @@ pub(super) fn hir_module_items(tcx: TyCtxt<'_>, module_id: LocalModDefId) -> Mod } } +fn force_delayed_owners_lowering(tcx: TyCtxt<'_>) { + let krate = tcx.hir_crate(()); + for &id in &krate.delayed_ids { + tcx.ensure_done().lower_delayed_owner(id); + } + + let (_, krate) = krate.delayed_resolver.steal(); + let prof = tcx.sess.prof.clone(); + + // Drop AST to free memory. It can be expensive so try to drop it on a separate thread. + spawn(move || { + let _timer = prof.verbose_generic_activity("drop_ast"); + drop(krate); + }); +} + pub(crate) fn hir_crate_items(tcx: TyCtxt<'_>, _: ()) -> ModuleItems { + force_delayed_owners_lowering(tcx); + let mut collector = ItemCollector::new(tcx, true); // A "crate collector" and "module collector" start at a diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 82f8eb4bbc4a..ad56e462d293 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -6,19 +6,83 @@ pub mod nested_filter; pub mod place; +use std::sync::Arc; + +use rustc_ast::{self as ast}; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_data_structures::steal::Steal; use rustc_data_structures::sync::{DynSend, DynSync, try_par_for_each_in}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::lints::DelayedLint; use rustc_hir::*; +use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_span::{ErrorGuaranteed, ExpnId, Span}; use crate::query::Providers; -use crate::ty::TyCtxt; +use crate::ty::{ResolverAstLowering, TyCtxt}; + +/// The top-level data structure that stores the entire contents of +/// the crate currently being compiled. +/// +/// For more details, see the [rustc dev guide]. +/// +/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir.html +#[derive(Debug)] +pub struct Crate<'hir> { + // This field is private by intention, access it through `owner` method. + owners: IndexVec>, + // Ids of delayed AST owners which are lowered through `lower_delayed_owner` query. + pub delayed_ids: FxIndexSet, + // The resolver and AST crate which are set in the end of the `hir_crate` query + // and then stolen and dropped in `force_delayed_owners_lowering`. + pub delayed_resolver: Steal<(ResolverAstLowering<'hir>, Arc)>, + // Only present when incr. comp. is enabled. + pub opt_hir_hash: Option, +} + +impl<'hir> Crate<'hir> { + pub fn new( + owners: IndexVec>, + delayed_ids: FxIndexSet, + delayed_resolver: Steal<(ResolverAstLowering<'hir>, Arc)>, + opt_hir_hash: Option, + ) -> Crate<'hir> { + Crate { owners, delayed_ids, delayed_resolver, opt_hir_hash } + } + + /// Serves as an entry point for getting `MaybeOwner`. As owner can either be in + /// `owners` of `hir_crate` or it can be delayed AST owner (i.e., delegations) + /// we need to firstly check in `hir_crate` and then delayed AST owners. + /// This method can be invoked when not all delayed AST owners are lowered. + pub fn owner(&self, tcx: TyCtxt<'hir>, def_id: LocalDefId) -> MaybeOwner<'hir> { + // Delayed LocalDefId can be in `self.owners` if there exists non-delayed LocalDefId + // which is greater than delayed LocalDefId, we use IndexVec for owners, + // so we will call ensure_contains_elem which will grow it. + if let Some(owner) = self.owners.get(def_id) + && (self.delayed_ids.is_empty() || !matches!(owner, MaybeOwner::Phantom)) + { + return *owner; + } + + if self.delayed_ids.contains(&def_id) { + tcx.ensure_done().lower_delayed_owner(def_id); + } + + tcx.delayed_owner(def_id) + } +} + +impl HashStable for Crate<'_> { + fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) { + let Crate { opt_hir_hash, .. } = self; + opt_hir_hash.unwrap().hash_stable(hcx, hasher) + } +} /// Gather the LocalDefId for each item-like within a module, including items contained within /// bodies. The Ids are in visitor order. This is used to partition a pass between modules. @@ -372,7 +436,9 @@ fn hir_owner_parent_impl(self, owner_id: OwnerId) -> HirId { let parent_owner_id = self.local_def_id_to_hir_id(parent_def_id).owner; HirId { owner: parent_owner_id, - local_id: self.hir_crate(()).owners[parent_owner_id.def_id] + local_id: self + .hir_crate(()) + .owner(self, parent_owner_id.def_id) .unwrap() .parenting .get(&owner_id.def_id) @@ -406,19 +472,19 @@ pub fn provide(providers: &mut Providers) { providers.hir_crate_items = map::hir_crate_items; providers.crate_hash = map::crate_hash; providers.hir_module_items = map::hir_module_items; - providers.local_def_id_to_hir_id = |tcx, def_id| match tcx.hir_crate(()).owners[def_id] { + providers.local_def_id_to_hir_id = |tcx, def_id| match tcx.hir_crate(()).owner(tcx, def_id) { MaybeOwner::Owner(_) => HirId::make_owner(def_id), MaybeOwner::NonOwner(hir_id) => hir_id, MaybeOwner::Phantom => bug!("No HirId for {:?}", def_id), }; providers.opt_hir_owner_nodes = - |tcx, id| tcx.hir_crate(()).owners.get(id)?.as_owner().map(|i| &i.nodes); + |tcx, id| tcx.hir_crate(()).owner(tcx, id).as_owner().map(|i| &i.nodes); providers.hir_owner_parent_q = |tcx, owner_id| tcx.hir_owner_parent_impl(owner_id); providers.hir_attr_map = |tcx, id| { - tcx.hir_crate(()).owners[id.def_id].as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs) + tcx.hir_crate(()).owner(tcx, id.def_id).as_owner().map_or(AttributeMap::EMPTY, |o| &o.attrs) }; providers.opt_ast_lowering_delayed_lints = - |tcx, id| tcx.hir_crate(()).owners[id.def_id].as_owner().map(|o| &o.delayed_lints); + |tcx, id| tcx.hir_crate(()).owner(tcx, id.def_id).as_owner().map(|o| &o.delayed_lints); providers.def_span = |tcx, def_id| tcx.hir_span(tcx.local_def_id_to_hir_id(def_id)); providers.def_ident_span = |tcx, def_id| { let hir_id = tcx.local_def_id_to_hir_id(def_id); @@ -459,6 +525,6 @@ pub fn provide(providers: &mut Providers) { providers.expn_that_defined = |tcx, id| tcx.resolutions(()).expn_that_defined.get(&id).copied().unwrap_or(ExpnId::root()); providers.in_scope_traits_map = |tcx, id| { - tcx.hir_crate(()).owners[id.def_id].as_owner().map(|owner_info| &owner_info.trait_map) + tcx.hir_crate(()).owner(tcx, id.def_id).as_owner().map(|owner_info| &owner_info.trait_map) }; } diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 60327be6f6f8..1f339ea0cabf 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -3,14 +3,16 @@ //! similar to queries, but queries come with a lot of machinery for caching and incremental //! compilation, whereas hooks are just plain function pointers without any of the query magic. +use std::marker::PhantomData; + use rustc_hir::def_id::{DefId, DefPathHash}; use rustc_session::StableCrateId; use rustc_span::def_id::{CrateNum, LocalDefId}; -use rustc_span::{ExpnHash, ExpnId}; +use rustc_span::{ExpnHash, ExpnId, Span}; -use crate::mir; use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex}; use crate::ty::{Ty, TyCtxt}; +use crate::{mir, ty}; macro_rules! declare_hooks { ($($(#[$attr:meta])*hook $name:ident($($arg:ident: $K:ty),*) -> $V:ty;)*) => { @@ -35,8 +37,10 @@ pub struct Providers { impl Default for Providers { fn default() -> Self { + #[allow(unused)] Providers { - $($name: |_, $($arg,)*| default_hook(stringify!($name), &($($arg,)*))),* + $($name: + |_, $($arg,)*| default_hook(stringify!($name))),* } } } @@ -113,11 +117,32 @@ fn clone(&self) -> Self { *self } encoder: &mut CacheEncoder<'_, 'tcx>, query_result_index: &mut EncodedDepNodeIndex ) -> (); + + /// Tries to normalize an alias, ignoring any errors. + /// + /// Generalization with the new trait solver calls into this, + /// when generalizing outside of the trait solver in `hir_typeck`. + hook try_eagerly_normalize_alias( + type_erased_infcx: TypeErasedInfcx<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + alias: ty::AliasTy<'tcx> + ) -> Ty<'tcx>; +} + +/// The `try_eagerly_normalize_alias` hook passes an `Infcx` from where it's called (in `rustc_infer`) +/// to where it's provided (in `rustc_trait_selection`). +/// Both of those crates have that type available, but `rustc_middle` does not. +/// Instead we pass this type-erased `Infcx` and transmute on both sides. +/// +/// Has to be `repr(transparent)` so we can transmute a `&'a Infcx<'tcx>` to this struct. +#[repr(transparent)] +pub struct TypeErasedInfcx<'a, 'tcx> { + _infcx: *const (), + phantom: PhantomData<&'a mut &'tcx ()>, } #[cold] -fn default_hook(name: &str, args: &dyn std::fmt::Debug) -> ! { - bug!( - "`tcx.{name}{args:?}` cannot be called as `{name}` was never assigned to a provider function" - ) +fn default_hook(name: &str) -> ! { + bug!("`tcx.{name}` cannot be called as `{name}` was never assigned to a provider function") } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 4dd68db4f913..c5dab806fa19 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -68,7 +68,7 @@ CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId, }; use rustc_hir::lang_items::{LangItem, LanguageItems}; -use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, PreciseCapturingArgKind, TraitCandidate}; +use rustc_hir::{ItemLocalId, ItemLocalMap, PreciseCapturingArgKind, TraitCandidate}; use rustc_index::IndexVec; use rustc_lint_defs::LintId; use rustc_macros::rustc_queries; @@ -82,6 +82,7 @@ use rustc_span::{DUMMY_SP, LocalExpnId, Span, Spanned, Symbol}; use rustc_target::spec::PanicStrategy; +use crate::hir::Crate; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::metadata::ModChild; @@ -101,7 +102,7 @@ CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono, }; use crate::query::describe_as_module; -use crate::query::plumbing::{define_callbacks, query_helper_param_ty}; +use crate::query::plumbing::{define_callbacks, maybe_into_query_key}; use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, @@ -210,6 +211,16 @@ desc { "getting the crate HIR" } } + query lower_delayed_owner(def_id: LocalDefId) { + eval_always + desc { "lowering the delayed AST owner `{}`", tcx.def_path_str(def_id) } + } + + query delayed_owner(def_id: LocalDefId) -> hir::MaybeOwner<'tcx> { + feedable + desc { "getting child of lowered delayed AST owner `{}`", tcx.def_path_str(def_id) } + } + /// All items in the crate. query hir_crate_items(_: ()) -> &'tcx rustc_middle::hir::ModuleItems { arena_cache @@ -574,16 +585,15 @@ desc { "checking if `{}` is representable", tcx.def_path_str(key) } // We don't want recursive representability calls to be forced with // incremental compilation because, if a cycle occurs, we need the - // entire cycle to be in memory for diagnostics. This means we can't - // use `ensure_ok()` with this query. - anon + // entire cycle to be in memory for diagnostics. + no_force } /// An implementation detail for the `check_representability` query. See that query for more /// details, particularly on the modifiers. query check_representability_adt_ty(key: Ty<'tcx>) { desc { "checking if `{}` is representable", key } - anon + no_force } /// Set of param indexes for type params that are in the type's representation @@ -757,14 +767,10 @@ /// Normally you would just use `tcx.erase_and_anonymize_regions(value)`, /// however, which uses this query as a kind of cache. query erase_and_anonymize_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> { - // This query is not expected to have input -- as a result, it - // is not a good candidates for "replay" because it is essentially a - // pure function of its input (and hence the expectation is that - // no caller would be green **apart** from just these - // queries). Making it anonymous avoids hashing the result, which - // may save a bit of time. - anon desc { "erasing regions from `{}`", ty } + // Not hashing the return value appears to give marginally better perf for this query, + // which should always be marked green for having no dependencies anyway. + no_hash } query wasm_import_module_map(_: CrateNum) -> &'tcx DefIdMap { diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index c2525d26d4c6..402c448a1fa3 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -77,23 +77,34 @@ pub(crate) fn query_ensure_result<'tcx, C, T>( C: QueryCache>>, Result: Erasable, { + let convert = |value: Erased>| -> Result<(), ErrorGuaranteed> { + match erase::restore_val(value) { + Ok(_) => Ok(()), + Err(guar) => Err(guar), + } + }; + match try_get_cached(tcx, &query.cache, key) { - Some(value) => erase::restore_val(value).map(drop), - None => (query.execute_query_fn)( - tcx, - DUMMY_SP, - key, - QueryMode::Ensure { ensure_mode: EnsureMode::Ok }, - ) - .map(erase::restore_val) - .map(|value| value.map(drop)) - // Either we actually executed the query, which means we got a full `Result`, - // or we can just assume the query succeeded, because it was green in the - // incremental cache. If it is green, that means that the previous compilation - // that wrote to the incremental cache compiles successfully. That is only - // possible if the cache entry was `Ok(())`, so we emit that here, without - // actually encoding the `Result` in the cache or loading it from there. - .unwrap_or(Ok(())), + Some(value) => convert(value), + None => { + match (query.execute_query_fn)( + tcx, + DUMMY_SP, + key, + QueryMode::Ensure { ensure_mode: EnsureMode::Ok }, + ) { + // We executed the query. Convert the successful result. + Some(res) => convert(res), + + // Reaching here means we didn't execute the query, but we can just assume the + // query succeeded, because it was green in the incremental cache. If it is green, + // that means that the previous compilation that wrote to the incremental cache + // compiles successfully. That is only possible if the cache entry was `Ok(())`, so + // we emit that here, without actually encoding the `Result` in the cache or + // loading it from there. + None => Ok(()), + } + } } } diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index ad9b9de119d5..50c9136ac6e9 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -7,11 +7,6 @@ // tidy-alphabetical-start // -/// # `anon` query modifier -/// -/// Generate a dep node based not on the query key, but on the query's dependencies. -pub(crate) struct anon; - /// # `arena_cache` query modifier /// /// Query return values must impl `Copy` and be small, but some queries must return values that @@ -59,6 +54,16 @@ /// Generate a `feed` method to set the query's value from another query. pub(crate) struct feedable; +/// # `no_force` query modifier +/// +/// Dep nodes of queries with this modifier will never be "forced" when trying +/// to mark their dependents green, even if their key is recoverable from the +/// key fingerprint. +/// +/// Used by some queries with custom cycle-handlers to ensure that if a cycle +/// occurs, all of the relevant query calls will be on the query stack. +pub(crate) struct no_force; + /// # `no_hash` query modifier /// /// Do not hash the query's return value for incremental compilation. If the value needs to be diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index d4e7bb52be97..dcf7a952fb97 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -77,8 +77,6 @@ pub enum EnsureMode { pub struct QueryVTable<'tcx, C: QueryCache> { pub name: &'static str, - /// True if this query has the `anon` modifier. - pub anon: bool, /// True if this query has the `eval_always` modifier. pub eval_always: bool, /// True if this query has the `depth_limit` modifier. @@ -201,6 +199,13 @@ pub struct TyCtxtEnsureDone<'tcx> { } impl<'tcx> TyCtxt<'tcx> { + /// Returns a transparent wrapper for `TyCtxt` which uses + /// `span` as the location of queries performed through it. + #[inline(always)] + pub fn at(self, span: Span) -> TyCtxtAt<'tcx> { + TyCtxtAt { tcx: self, span } + } + /// FIXME: `ensure_ok`'s effects are subtle. Is this comment fully accurate? /// /// Wrapper that calls queries in a special "ensure OK" mode, for callers @@ -258,16 +263,9 @@ pub fn ensure_result(self) -> TyCtxtEnsureResult<'tcx> { pub fn ensure_done(self) -> TyCtxtEnsureDone<'tcx> { TyCtxtEnsureDone { tcx: self } } - - /// Returns a transparent wrapper for `TyCtxt` which uses - /// `span` as the location of queries performed through it. - #[inline(always)] - pub fn at(self, span: Span) -> TyCtxtAt<'tcx> { - TyCtxtAt { tcx: self, span } - } } -macro_rules! query_helper_param_ty { +macro_rules! maybe_into_query_key { (DefId) => { impl $crate::query::IntoQueryKey }; (LocalDefId) => { impl $crate::query::IntoQueryKey }; ($K:ty) => { $K }; @@ -276,19 +274,19 @@ macro_rules! query_helper_param_ty { macro_rules! define_callbacks { ( // You might expect the key to be `$K:ty`, but it needs to be `$($K:tt)*` so that - // `query_helper_param_ty!` can match on specific type names. + // `maybe_into_query_key!` can match on specific type names. queries { $( $(#[$attr:meta])* fn $name:ident($($K:tt)*) -> $V:ty { // Search for (QMODLIST) to find all occurrences of this query modifier list. - anon: $anon:literal, arena_cache: $arena_cache:literal, cache_on_disk: $cache_on_disk:literal, depth_limit: $depth_limit:literal, eval_always: $eval_always:literal, feedable: $feedable:literal, + no_force: $no_force:literal, no_hash: $no_hash:literal, returns_error_guaranteed: $returns_error_guaranteed:literal, separate_provide_extern: $separate_provide_extern:literal, @@ -323,6 +321,9 @@ pub mod $name { #[cfg(not($arena_cache))] pub type ProvidedValue<'tcx> = Value<'tcx>; + pub type Cache<'tcx> = + as $crate::query::QueryKey>::Cache>>; + /// This helper function takes a value returned by the query provider /// (or loaded from disk, or supplied by query feeding), allocates /// it in an arena if requested by the `arena_cache` modifier, and @@ -354,9 +355,6 @@ pub fn provided_to_erased<'tcx>( erase::erase_val(value) } - pub type Cache<'tcx> = - as $crate::query::QueryKey>::Cache>>; - // Ensure that keys grow no larger than 88 bytes by accident. // Increase this limit if necessary, but do try to keep the size low if possible #[cfg(target_pointer_width = "64")] @@ -390,116 +388,8 @@ pub fn provided_to_erased<'tcx>( } )* - /// Holds per-query arenas for queries with the `arena_cache` modifier. - #[derive(Default)] - pub struct QueryArenas<'tcx> { - $( - // Use the `ArenaCached` helper trait to determine the arena's value type. - #[cfg($arena_cache)] - pub $name: TypedArena< - <$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Allocated, - >, - )* - } - - impl<'tcx> $crate::query::TyCtxtEnsureOk<'tcx> { - $( - $(#[$attr])* - #[inline(always)] - pub fn $name(self, key: query_helper_param_ty!($($K)*)) { - crate::query::inner::query_ensure_ok_or_done( - self.tcx, - &self.tcx.query_system.query_vtables.$name, - $crate::query::IntoQueryKey::into_query_key(key), - $crate::query::EnsureMode::Ok, - ) - } - )* - } - - // Only defined when the `ensure_result` modifier is present. - impl<'tcx> $crate::query::TyCtxtEnsureResult<'tcx> { - $( - #[cfg($returns_error_guaranteed)] - $(#[$attr])* - #[inline(always)] - pub fn $name( - self, - key: query_helper_param_ty!($($K)*), - ) -> Result<(), rustc_errors::ErrorGuaranteed> { - crate::query::inner::query_ensure_result( - self.tcx, - &self.tcx.query_system.query_vtables.$name, - $crate::query::IntoQueryKey::into_query_key(key), - ) - } - )* - } - - impl<'tcx> $crate::query::TyCtxtEnsureDone<'tcx> { - $( - $(#[$attr])* - #[inline(always)] - pub fn $name(self, key: query_helper_param_ty!($($K)*)) { - crate::query::inner::query_ensure_ok_or_done( - self.tcx, - &self.tcx.query_system.query_vtables.$name, - $crate::query::IntoQueryKey::into_query_key(key), - $crate::query::EnsureMode::Done, - ); - } - )* - } - - impl<'tcx> TyCtxt<'tcx> { - $( - $(#[$attr])* - #[inline(always)] - #[must_use] - pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V { - self.at(DUMMY_SP).$name(key) - } - )* - } - - impl<'tcx> $crate::query::TyCtxtAt<'tcx> { - $( - $(#[$attr])* - #[inline(always)] - pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V { - use $crate::query::{erase, inner}; - - erase::restore_val::<$V>(inner::query_get_at( - self.tcx, - self.span, - &self.tcx.query_system.query_vtables.$name, - $crate::query::IntoQueryKey::into_query_key(key), - )) - } - )* - } - - $( - #[cfg($feedable)] - impl<'tcx, K: $crate::query::IntoQueryKey<$name::Key<'tcx>> + Copy> - TyCtxtFeed<'tcx, K> - { - $(#[$attr])* - #[inline(always)] - pub fn $name(self, value: $name::ProvidedValue<'tcx>) { - let key = self.key().into_query_key(); - let erased_value = $name::provided_to_erased(self.tcx, value); - $crate::query::inner::query_feed( - self.tcx, - &self.tcx.query_system.query_vtables.$name, - key, - erased_value, - ); - } - } - )* - - /// Identifies a query by kind and key. This is in contrast to `QueryJobId` which is just a number. + /// Identifies a query by kind and key. This is in contrast to `QueryJobId` which is just a + /// number. #[allow(non_camel_case_types)] #[derive(Clone, Copy, Debug)] pub enum TaggedQueryKey<'tcx> { @@ -528,7 +418,8 @@ pub fn query_name(&self) -> &'static str { pub fn description(&self, tcx: TyCtxt<'tcx>) -> String { let (name, description) = ty::print::with_no_queries!(match self { $( - TaggedQueryKey::$name(key) => (stringify!($name), _description_fns::$name(tcx, *key)), + TaggedQueryKey::$name(key) => + (stringify!($name), _description_fns::$name(tcx, *key)), )* }); if tcx.sess.verbose_internals() { @@ -550,7 +441,8 @@ pub fn default_span(&self, tcx: TyCtxt<'tcx>, span: Span) -> Span { } match self { $( - TaggedQueryKey::$name(key) => crate::query::QueryKey::default_span(key, tcx), + TaggedQueryKey::$name(key) => + $crate::query::QueryKey::default_span(key, tcx), )* } } @@ -558,14 +450,20 @@ pub fn default_span(&self, tcx: TyCtxt<'tcx>, span: Span) -> Span { pub fn def_kind(&self, tcx: TyCtxt<'tcx>) -> Option { // This is used to reduce code generation as it // can be reused for queries with the same key type. - fn inner<'tcx>(key: &impl crate::query::QueryKey, tcx: TyCtxt<'tcx>) -> Option { - key.key_as_def_id().and_then(|def_id| def_id.as_local()).map(|def_id| tcx.def_kind(def_id)) + fn inner<'tcx>(key: &impl $crate::query::QueryKey, tcx: TyCtxt<'tcx>) + -> Option + { + key + .key_as_def_id() + .and_then(|def_id| def_id.as_local()) + .map(|def_id| tcx.def_kind(def_id)) } if let TaggedQueryKey::def_kind(..) = self { // Try to avoid infinite recursion. return None } + match self { $( TaggedQueryKey::$name(key) => inner(key, tcx), @@ -577,7 +475,19 @@ fn inner<'tcx>(key: &impl crate::query::QueryKey, tcx: TyCtxt<'tcx>) -> Option { $( - pub $name: crate::query::QueryVTable<'tcx, $name::Cache<'tcx>>, + pub $name: $crate::query::QueryVTable<'tcx, $name::Cache<'tcx>>, + )* + } + + /// Holds per-query arenas for queries with the `arena_cache` modifier. + #[derive(Default)] + pub struct QueryArenas<'tcx> { + $( + // Use the `ArenaCached` helper trait to determine the arena's value type. + #[cfg($arena_cache)] + pub $name: TypedArena< + <$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Allocated, + >, )* } @@ -637,12 +547,108 @@ impl Copy for ExternProviders {} impl Clone for ExternProviders { fn clone(&self) -> Self { *self } } + + impl<'tcx> TyCtxt<'tcx> { + $( + $(#[$attr])* + #[inline(always)] + #[must_use] + pub fn $name(self, key: maybe_into_query_key!($($K)*)) -> $V { + self.at(DUMMY_SP).$name(key) + } + )* + } + + impl<'tcx> $crate::query::TyCtxtAt<'tcx> { + $( + $(#[$attr])* + #[inline(always)] + pub fn $name(self, key: maybe_into_query_key!($($K)*)) -> $V { + use $crate::query::{erase, inner}; + + erase::restore_val::<$V>(inner::query_get_at( + self.tcx, + self.span, + &self.tcx.query_system.query_vtables.$name, + $crate::query::IntoQueryKey::into_query_key(key), + )) + } + )* + } + + impl<'tcx> $crate::query::TyCtxtEnsureOk<'tcx> { + $( + $(#[$attr])* + #[inline(always)] + pub fn $name(self, key: maybe_into_query_key!($($K)*)) { + $crate::query::inner::query_ensure_ok_or_done( + self.tcx, + &self.tcx.query_system.query_vtables.$name, + $crate::query::IntoQueryKey::into_query_key(key), + $crate::query::EnsureMode::Ok, + ) + } + )* + } + + // Only defined when the `returns_error_guaranteed` modifier is present. + impl<'tcx> $crate::query::TyCtxtEnsureResult<'tcx> { + $( + #[cfg($returns_error_guaranteed)] + $(#[$attr])* + #[inline(always)] + pub fn $name( + self, + key: maybe_into_query_key!($($K)*), + ) -> Result<(), rustc_errors::ErrorGuaranteed> { + $crate::query::inner::query_ensure_result( + self.tcx, + &self.tcx.query_system.query_vtables.$name, + $crate::query::IntoQueryKey::into_query_key(key), + ) + } + )* + } + + impl<'tcx> $crate::query::TyCtxtEnsureDone<'tcx> { + $( + $(#[$attr])* + #[inline(always)] + pub fn $name(self, key: maybe_into_query_key!($($K)*)) { + $crate::query::inner::query_ensure_ok_or_done( + self.tcx, + &self.tcx.query_system.query_vtables.$name, + $crate::query::IntoQueryKey::into_query_key(key), + $crate::query::EnsureMode::Done, + ); + } + )* + } + + $( + // Only defined when the `feedable` modifier is present. + #[cfg($feedable)] + impl<'tcx, K: $crate::query::IntoQueryKey<$name::Key<'tcx>> + Copy> + TyCtxtFeed<'tcx, K> + { + $(#[$attr])* + #[inline(always)] + pub fn $name(self, value: $name::ProvidedValue<'tcx>) { + $crate::query::inner::query_feed( + self.tcx, + &self.tcx.query_system.query_vtables.$name, + self.key().into_query_key(), + $name::provided_to_erased(self.tcx, value), + ); + } + } + )* }; } // Re-export `macro_rules!` macros as normal items, so that they can be imported normally. pub(crate) use define_callbacks; -pub(crate) use query_helper_param_ty; +pub(crate) use maybe_into_query_key; #[cold] pub(crate) fn default_query(name: &str, key: &dyn std::fmt::Debug) -> ! { diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 881165706a03..51f33691bb7d 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -866,6 +866,13 @@ pub enum PatKind<'tcx> { pats: Box<[Pat<'tcx>]>, }, + /// A guard pattern, e.g. `x if guard(x)` + Guard { + subpattern: Box>, + #[type_visitable(ignore)] + condition: ExprId, + }, + /// A never pattern `!`. Never, diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index c17e15513cdf..aa1b6b1663bf 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -247,6 +247,12 @@ pub fn walk_pat<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>( visitor: &mut V, pat: &'thir Pat<'tcx>, ) { + if let PatKind::Guard { subpattern, condition } = &pat.kind { + visitor.visit_pat(subpattern); + visitor.visit_expr(&visitor.thir()[*condition]); + return; + }; + for_each_immediate_subpat(pat, |p| visitor.visit_pat(p)); } @@ -287,5 +293,9 @@ pub(crate) fn for_each_immediate_subpat<'a, 'tcx>( callback(pat); } } + + PatKind::Guard { subpattern, .. } => { + callback(subpattern); + } } } diff --git a/compiler/rustc_middle/src/ty/consts/lit.rs b/compiler/rustc_middle/src/ty/consts/lit.rs index be6dfb20e043..7be225a9e921 100644 --- a/compiler/rustc_middle/src/ty/consts/lit.rs +++ b/compiler/rustc_middle/src/ty/consts/lit.rs @@ -10,7 +10,10 @@ pub struct LitToConstInput<'tcx> { /// The absolute value of the resultant constant. pub lit: LitKind, /// The type of the constant. - pub ty: Ty<'tcx>, + /// + /// `None` is used by const generics when the type of the constant is unknown, e.g. + /// if there are inference variables + pub ty: Option>, /// If the constant is negative. pub neg: bool, } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 56428780d22d..58a2edca8ece 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -36,7 +36,7 @@ use rustc_hir::intravisit::VisitorExt; use rustc_hir::lang_items::LangItem; use rustc_hir::limit::Limit; -use rustc_hir::{self as hir, CRATE_HIR_ID, HirId, Node, TraitCandidate, find_attr}; +use rustc_hir::{self as hir, CRATE_HIR_ID, HirId, MaybeOwner, Node, TraitCandidate, find_attr}; use rustc_index::IndexVec; use rustc_macros::Diagnostic; use rustc_session::Session; @@ -657,6 +657,11 @@ pub fn feed_anon_const_type(self, key: LocalDefId, value: ty::EarlyBinder<'tcx, debug_assert_eq!(self.def_kind(key), DefKind::AnonConst); TyCtxtFeed { tcx: self, key }.type_of(value) } + + /// Feeds the HIR delayed owner during AST -> HIR delayed lowering. + pub fn feed_delayed_owner(self, key: LocalDefId, owner: MaybeOwner<'tcx>) { + TyCtxtFeed { tcx: self, key }.delayed_owner(owner); + } } impl<'tcx, KEY: Copy> TyCtxtFeed<'tcx, KEY> { diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs index b454689e243d..5bb11c917502 100644 --- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs +++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs @@ -61,7 +61,7 @@ pub(crate) fn provide(providers: &mut Providers) { /// requires calling [`InhabitedPredicate::instantiate`] fn inhabited_predicate_adt(tcx: TyCtxt<'_>, def_id: DefId) -> InhabitedPredicate<'_> { if let Some(def_id) = def_id.as_local() { - let _ = tcx.check_representability(def_id); + tcx.ensure_ok().check_representability(def_id); } let adt = tcx.adt_def(def_id); diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index d35b755b3193..f072b26e2691 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -27,7 +27,6 @@ Align, FieldIdx, Integer, IntegerType, ReprFlags, ReprOptions, ScalableElt, VariantIdx, }; use rustc_ast as ast; -use rustc_ast::AttrVec; use rustc_ast::expand::typetree::{FncTree, Kind, Type, TypeTree}; use rustc_ast::node_id::NodeMap; pub use rustc_ast_ir::{Movability, Mutability, try_visit}; @@ -221,43 +220,15 @@ pub struct ResolverAstLowering<'tcx> { /// Lints that were emitted by the resolver and early lints. pub lint_buffer: Steal, - /// Information about functions signatures for delegation items expansion - pub delegation_fn_sigs: LocalDefIdMap, // Information about delegations which is used when handling recursive delegations pub delegation_infos: LocalDefIdMap, } -bitflags::bitflags! { - #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] - pub struct DelegationFnSigAttrs: u8 { - const TARGET_FEATURE = 1 << 0; - const MUST_USE = 1 << 1; - } -} - -pub const DELEGATION_INHERIT_ATTRS_START: DelegationFnSigAttrs = DelegationFnSigAttrs::MUST_USE; - #[derive(Debug)] pub struct DelegationInfo { // NodeId (either delegation.id or item_id in case of a trait impl) for signature resolution, // for details see https://github.com/rust-lang/rust/issues/118212#issuecomment-2160686914 pub resolution_node: ast::NodeId, - pub attrs: DelegationAttrs, -} - -#[derive(Debug)] -pub struct DelegationAttrs { - pub flags: DelegationFnSigAttrs, - pub to_inherit: AttrVec, -} - -#[derive(Debug)] -pub struct DelegationFnSig { - pub header: ast::FnHeader, - pub param_count: usize, - pub has_self: bool, - pub c_variadic: bool, - pub attrs: DelegationAttrs, } #[derive(Clone, Copy, Debug, HashStable)] diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index ad6c1f7dce5b..c67d99a8eb7d 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -50,7 +50,8 @@ pub(crate) fn as_constant_inner<'tcx>( match *kind { ExprKind::Literal { lit, neg } => { - let const_ = lit_to_mir_constant(tcx, LitToConstInput { lit: lit.node, ty, neg }); + let const_ = + lit_to_mir_constant(tcx, LitToConstInput { lit: lit.node, ty: Some(ty), neg }); ConstOperand { span, user_ty: None, const_ } } @@ -109,6 +110,8 @@ pub(crate) fn as_constant_inner<'tcx>( fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>) -> Const<'tcx> { let LitToConstInput { lit, ty, neg } = lit_input; + let ty = ty.expect("type of literal must be known at this point"); + if let Err(guar) = ty.error_reported() { return Const::Ty(Ty::new_error(tcx, guar), ty::Const::new_error(tcx, guar)); } diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 7698af4cd38b..cbe31fd7d132 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -360,6 +360,11 @@ pub(super) fn for_pattern( Some(TestableCase::Deref { temp, mutability }) } + PatKind::Guard { .. } => { + // FIXME(guard_patterns) + None + } + PatKind::Never => Some(TestableCase::Never), }; diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index d7edf82ae4af..d4f5fe84e0ff 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -944,6 +944,9 @@ fn visit_primary_bindings_special( visit_subpat(self, subpattern, user_tys, f); } } + PatKind::Guard { ref subpattern, .. } => { + visit_subpat(self, subpattern, user_tys, f); + } } } } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index ee35539221e6..aed0c6e6085d 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -315,6 +315,7 @@ fn visit_pat(&mut self, pat: &'a Pat<'tcx>) { | PatKind::Range { .. } | PatKind::Slice { .. } | PatKind::Array { .. } + | PatKind::Guard { .. } // Never constitutes a witness of uninhabitedness. | PatKind::Never => { self.requires_unsafe(pat.span, AccessToUnionField); diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index b4eedb15033c..019af2461354 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -31,25 +31,27 @@ pub(crate) fn lit_to_const<'tcx>( .unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result)) }; - let (valtree, valtree_ty) = match (lit, expected_ty.kind()) { + let (valtree, valtree_ty) = match (lit, expected_ty.map(|ty| ty.kind())) { (ast::LitKind::Str(s, _), _) => { let str_bytes = s.as_str().as_bytes(); let valtree_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_); (ty::ValTree::from_raw_bytes(tcx, str_bytes), valtree_ty) } - (ast::LitKind::ByteStr(byte_sym, _), ty::Ref(_, inner_ty, _)) + (ast::LitKind::ByteStr(byte_sym, _), Some(ty::Ref(_, inner_ty, _))) if let ty::Slice(ty) | ty::Array(ty, _) = inner_ty.kind() && let ty::Uint(UintTy::U8) = ty.kind() => { - (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty) + (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty.unwrap()) } - (ast::LitKind::ByteStr(byte_sym, _), ty::Slice(inner_ty) | ty::Array(inner_ty, _)) - if tcx.features().deref_patterns() - && let ty::Uint(UintTy::U8) = inner_ty.kind() => + ( + ast::LitKind::ByteStr(byte_sym, _), + Some(ty::Slice(inner_ty) | ty::Array(inner_ty, _)), + ) if tcx.features().deref_patterns() + && let ty::Uint(UintTy::U8) = inner_ty.kind() => { // Byte string literal patterns may have type `[u8]` or `[u8; N]` if `deref_patterns` is // enabled, in order to allow, e.g., `deref!(b"..."): Vec`. - (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty) + (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty.unwrap()) } (ast::LitKind::ByteStr(byte_sym, _), _) => { let valtree = ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()); @@ -79,11 +81,11 @@ pub(crate) fn lit_to_const<'tcx>( trunc(if neg { u128::wrapping_neg(n.get()) } else { n.get() }, i.to_unsigned()); (ty::ValTree::from_scalar_int(tcx, scalar_int), Ty::new_int(tcx, i)) } - (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), ty::Uint(ui)) if !neg => { + (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), Some(ty::Uint(ui))) if !neg => { let scalar_int = trunc(n.get(), *ui); (ty::ValTree::from_scalar_int(tcx, scalar_int), Ty::new_uint(tcx, *ui)) } - (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), ty::Int(i)) => { + (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), Some(ty::Int(i))) => { // Unsigned "negation" has the same bitwise effect as signed negation, // which gets the result we want without additional casts. let scalar_int = @@ -101,7 +103,7 @@ pub(crate) fn lit_to_const<'tcx>( let bits = parse_float_into_scalar(n, fty, neg)?; (ty::ValTree::from_scalar_int(tcx, bits), Ty::new_float(tcx, fty)) } - (ast::LitKind::Float(n, ast::LitFloatType::Unsuffixed), ty::Float(fty)) => { + (ast::LitKind::Float(n, ast::LitFloatType::Unsuffixed), Some(ty::Float(fty))) => { let bits = parse_float_into_scalar(n, *fty, neg)?; (ty::ValTree::from_scalar_int(tcx, bits), Ty::new_float(tcx, *fty)) } diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index b108dff5555f..ea27252ad6ce 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -83,6 +83,7 @@ fn mirror_stmts( } Some(_) | None => local.span, }; + let initializer = local.init.map(|init| self.mirror_expr(init)); let stmt = Stmt { kind: StmtKind::Let { remainder_scope, @@ -91,7 +92,7 @@ fn mirror_stmts( data: region::ScopeData::Node, }, pattern, - initializer: local.init.map(|init| self.mirror_expr(init)), + initializer, else_block, hir_id: local.hir_id, span, diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index c646b0fc45ea..b79bda204405 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -876,7 +876,7 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx> }; let indices = self.typeck_results.offset_of_data().get(expr.hir_id).unwrap(); - let mut expr = None::>; + let mut expr = None::>; for &(container, variant, field) in indices.iter() { let next = mk_call(&mut self.thir, container, variant, field); diff --git a/compiler/rustc_mir_build/src/thir/cx/mod.rs b/compiler/rustc_mir_build/src/thir/cx/mod.rs index 60cb509ee9dd..7f4b70b95614 100644 --- a/compiler/rustc_mir_build/src/thir/cx/mod.rs +++ b/compiler/rustc_mir_build/src/thir/cx/mod.rs @@ -13,14 +13,14 @@ use rustc_middle::ty::{self, TyCtxt}; /// Query implementation for [`TyCtxt::thir_body`]. -pub(crate) fn thir_body( - tcx: TyCtxt<'_>, +pub(crate) fn thir_body<'tcx>( + tcx: TyCtxt<'tcx>, owner_def: LocalDefId, -) -> Result<(&Steal>, ExprId), ErrorGuaranteed> { +) -> Result<(&'tcx Steal>, ExprId), ErrorGuaranteed> { debug_assert!(!tcx.is_type_const(owner_def.to_def_id()), "thir_body queried for type_const"); let body = tcx.hir_body_owned_by(owner_def); - let mut cx = ThirBuildCx::new(tcx, owner_def); + let mut cx: ThirBuildCx<'tcx> = ThirBuildCx::new(tcx, owner_def); if let Some(reported) = cx.typeck_results.tainted_by_errors { return Err(reported); } @@ -50,7 +50,7 @@ pub(crate) fn thir_body( } /// Context for lowering HIR to THIR for a single function body (or other kind of body). -struct ThirBuildCx<'tcx> { +pub(crate) struct ThirBuildCx<'tcx> { tcx: TyCtxt<'tcx>, /// The THIR data that this context is building. thir: Thir<'tcx>, @@ -118,6 +118,7 @@ fn pattern_from_hir_with_annotation( let_stmt_type: Option<&hir::Ty<'tcx>>, ) -> Box> { crate::thir::pattern::pat_from_hir( + self, self.tcx, self.typing_env, self.typeck_results, @@ -201,7 +202,7 @@ fn explicit_params( fn_sig.inputs()[index] }; - let pat = self.pattern_from_hir(param.pat); + let pat: Box> = self.pattern_from_hir(param.pat); Param { pat: Some(pat), ty, ty_span, self_kind, hir_id: Some(param.hir_id) } }) } diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index ff9d456f7e70..ff4778dacc8d 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -25,7 +25,7 @@ PointerPattern, TypeNotPartialEq, TypeNotStructural, UnionPattern, UnsizedPattern, }; -impl<'tcx> PatCtxt<'tcx> { +impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { /// Converts a constant to a pattern (if possible). /// This means aggregate values (like structs and enums) are converted /// to a pattern that matches the value (as if you'd compared via structural equality). @@ -61,7 +61,7 @@ struct ConstToPat<'tcx> { } impl<'tcx> ConstToPat<'tcx> { - fn new(pat_ctxt: &PatCtxt<'tcx>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self { + fn new(pat_ctxt: &PatCtxt<'tcx, '_>, id: hir::HirId, span: Span, c: ty::Const<'tcx>) -> Self { trace!(?pat_ctxt.typeck_results.hir_owner); ConstToPat { tcx: pat_ctxt.tcx, typing_env: pat_ctxt.typing_env, span, id, c } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 67cde0e2c886..7d4e25cd814b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -30,9 +30,11 @@ pub(crate) use self::check_match::check_match; use self::migration::PatMigration; use crate::errors::*; +use crate::thir::cx::ThirBuildCx; /// Context for lowering HIR patterns to THIR patterns. -struct PatCtxt<'tcx> { +struct PatCtxt<'tcx, 'ptcx> { + upper: &'ptcx mut ThirBuildCx<'tcx>, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, typeck_results: &'tcx ty::TypeckResults<'tcx>, @@ -41,8 +43,9 @@ struct PatCtxt<'tcx> { rust_2024_migration: Option>, } -#[instrument(level = "debug", skip(tcx, typing_env, typeck_results), ret)] -pub(super) fn pat_from_hir<'tcx>( +#[instrument(level = "debug", skip(upper, tcx, typing_env, typeck_results), ret)] +pub(super) fn pat_from_hir<'tcx, 'ptcx>( + upper: &'ptcx mut ThirBuildCx<'tcx>, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, typeck_results: &'tcx ty::TypeckResults<'tcx>, @@ -51,6 +54,7 @@ pub(super) fn pat_from_hir<'tcx>( let_stmt_type: Option<&hir::Ty<'tcx>>, ) -> Box> { let mut pcx = PatCtxt { + upper, tcx, typing_env, typeck_results, @@ -87,7 +91,7 @@ pub(super) fn pat_from_hir<'tcx>( thir_pat } -impl<'tcx> PatCtxt<'tcx> { +impl<'tcx, 'ptcx> PatCtxt<'tcx, 'ptcx> { fn lower_pattern(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box> { let adjustments: &[PatAdjustment<'tcx>] = self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v); @@ -443,8 +447,10 @@ fn lower_pattern_unadjusted(&mut self, pat: &'tcx hir::Pat<'tcx>) -> Box PatKind::Or { pats: self.lower_patterns(pats) }, - // FIXME(guard_patterns): implement guard pattern lowering - hir::PatKind::Guard(pat, _) => self.lower_pattern(pat).kind, + hir::PatKind::Guard(pat, condition) => PatKind::Guard { + subpattern: self.lower_pattern(pat), + condition: self.upper.mirror_expr(condition), + }, hir::PatKind::Err(guar) => PatKind::Error(guar), }; @@ -696,7 +702,7 @@ fn lower_pat_expr( // patterns to `str`, and byte-string literal patterns to `[u8; N]` or `[u8]`. let pat_ty = self.typeck_results.node_type(pat.hir_id); - let lit_input = LitToConstInput { lit: lit.node, ty: pat_ty, neg: *negated }; + let lit_input = LitToConstInput { lit: lit.node, ty: Some(pat_ty), neg: *negated }; let constant = const_lit_matches_ty(self.tcx, &lit.node, pat_ty, *negated) .then(|| self.tcx.at(expr.span).lit_to_const(lit_input)) .flatten() diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 27dec99696f5..ea34e5f4d97d 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -844,6 +844,14 @@ fn print_pat_kind(&mut self, pat_kind: &PatKind<'tcx>, depth_lvl: usize) { print_indented!(self, "]", depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } + PatKind::Guard { subpattern, condition } => { + print_indented!(self, "Guard pattern: {", depth_lvl + 1); + print_indented!(self, "subpattern: ", depth_lvl + 2); + self.print_pat(subpattern, depth_lvl + 3); + print_indented!(self, "guard: ", depth_lvl + 2); + self.print_expr(*condition, depth_lvl + 3); + print_indented!(self, "}", depth_lvl + 1); + } PatKind::Error(_) => { print_indented!(self, "Error", depth_lvl + 1); } diff --git a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs index ce2be24adc58..e469451da993 100644 --- a/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonical/canonicalizer.rs @@ -177,7 +177,11 @@ fn canonicalize_param_env( cache: Default::default(), }; let param_env = param_env.fold_with(&mut env_canonicalizer); - debug_assert!(env_canonicalizer.sub_root_lookup_table.is_empty()); + debug_assert!( + env_canonicalizer.sub_root_lookup_table.is_empty(), + "{:?}", + env_canonicalizer.sub_root_lookup_table + ); ( param_env, env_canonicalizer.variables, 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 cd74e87b670f..456fe5246d80 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 @@ -987,7 +987,12 @@ fn try_eagerly_replace_alias( let replacement = self.ecx.instantiate_binder_with_infer(*replacement); self.nested.extend( self.ecx - .eq_and_get_goals(self.param_env, alias_term, replacement.projection_term) + .relate_and_get_goals( + self.param_env, + alias_term, + ty::Invariant, + replacement.projection_term, + ) .expect("expected to be able to unify goal projection with dyn's projection"), ); diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 6841fe1c5124..fedb6390d958 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -408,7 +408,7 @@ pub(super) fn ignore_candidate_head_usages(&mut self, usages: CandidateHeadUsage /// Recursively evaluates `goal`, returning whether any inference vars have /// been constrained and the certainty of the result. - fn evaluate_goal( + pub(super) fn evaluate_goal( &mut self, source: GoalSource, goal: Goal, @@ -1018,7 +1018,8 @@ pub(super) fn relate>( variance: ty::Variance, rhs: T, ) -> Result<(), NoSolution> { - let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?; + let goals = self.relate_and_get_goals(param_env, lhs, variance, rhs)?; + for &goal in goals.iter() { let source = match goal.predicate.kind().skip_binder() { ty::PredicateKind::Subtype { .. } | ty::PredicateKind::AliasRelate(..) => { @@ -1039,13 +1040,37 @@ pub(super) fn relate>( /// If possible, try using `eq` instead which automatically handles nested /// goals correctly. #[instrument(level = "trace", skip(self, param_env), ret)] - pub(super) fn eq_and_get_goals>( - &self, + pub(super) fn relate_and_get_goals>( + &mut self, param_env: I::ParamEnv, lhs: T, + variance: ty::Variance, rhs: T, ) -> Result>, NoSolution> { - Ok(self.delegate.relate(param_env, lhs, ty::Variance::Invariant, rhs, self.origin_span)?) + let cx = self.cx(); + let delegate = self.delegate; + let origin_span = self.origin_span; + + let mut normalize = |alias: ty::AliasTy| { + let inference_var = self.next_ty_infer(); + + let goal = Goal::new( + cx, + param_env, + ty::PredicateKind::AliasRelate( + alias.to_ty(cx).into(), + inference_var.into(), + ty::AliasRelationDirection::Equate, + ), + ); + + // Ignore the result. If we can't eagerly normalize, returning the inference variable is enough. + let _ = self.evaluate_goal(GoalSource::TypeRelating, goal, None); + + self.resolve_vars_if_possible(inference_var) + }; + + Ok(delegate.relate(param_env, lhs, variance, rhs, origin_span, &mut normalize)?) } pub(super) fn instantiate_binder_with_infer + Copy>( diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index f4f718292205..0e852d2abda0 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -4499,3 +4499,111 @@ pub(super) fn from_token(token: &Token) -> Option { } } } + +#[derive(Diagnostic)] +#[diag( + "this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression" +)] +pub(crate) struct BreakWithLabelAndLoop { + #[subdiagnostic] + pub sub: BreakWithLabelAndLoopSub, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("wrap this expression in parentheses", applicability = "machine-applicable")] +pub(crate) struct BreakWithLabelAndLoopSub { + #[suggestion_part(code = "(")] + pub left: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} + +#[derive(Diagnostic)] +#[diag("prefix `'r` is reserved")] +pub(crate) struct RawPrefix { + #[label("reserved prefix")] + pub label: Span, + #[suggestion( + "insert whitespace here to avoid this being parsed as a prefix in Rust 2021", + code = " ", + applicability = "machine-applicable" + )] + pub suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("unicode codepoint changing visible direction of text present in comment")] +#[note( + "these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen" +)] +pub(crate) struct UnicodeTextFlow { + #[label( + "{$num_codepoints -> + [1] this comment contains an invisible unicode text flow control codepoint + *[other] this comment contains invisible unicode text flow control codepoints + }" + )] + pub comment_span: Span, + #[subdiagnostic] + pub characters: Vec, + #[subdiagnostic] + pub suggestions: Option, + + pub num_codepoints: usize, +} + +#[derive(Subdiagnostic)] +#[label("{$c_debug}")] +pub(crate) struct UnicodeCharNoteSub { + #[primary_span] + pub span: Span, + pub c_debug: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "if their presence wasn't intentional, you can remove them", + applicability = "machine-applicable", + style = "hidden" +)] +pub(crate) struct UnicodeTextFlowSuggestion { + #[suggestion_part(code = "")] + pub spans: Vec, +} + +#[derive(Diagnostic)] +#[diag("prefix `{$prefix}` is unknown")] +pub(crate) struct ReservedPrefix { + #[label("unknown prefix")] + pub label: Span, + #[suggestion( + "insert whitespace here to avoid this being parsed as a prefix in Rust 2021", + code = " ", + applicability = "machine-applicable" + )] + pub suggestion: Span, + + pub prefix: String, +} + +#[derive(Diagnostic)] +#[diag("will be parsed as a guarded string in Rust 2024")] +pub(crate) struct ReservedStringLint { + #[suggestion( + "insert whitespace here to avoid this being parsed as a guarded string in Rust 2024", + code = " ", + applicability = "machine-applicable" + )] + pub suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("reserved token in Rust 2024")] +pub(crate) struct ReservedMultihashLint { + #[suggestion( + "insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024", + code = " ", + applicability = "machine-applicable" + )] + pub suggestion: Span, +} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index cd90655125b2..2223e6a26575 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -4,12 +4,11 @@ use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::unicode::{TEXT_FLOW_CONTROL_CHARS, contains_text_flow_control_chars}; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, DiagCtxtHandle, StashKey}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, Diagnostic, StashKey}; use rustc_lexer::{ Base, Cursor, DocStyle, FrontmatterAllowed, LiteralKind, RawStrError, is_horizontal_whitespace, }; use rustc_literal_escaper::{EscapeError, Mode, check_for_errors}; -use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, TEXT_DIRECTION_CODEPOINT_IN_LITERAL, @@ -388,7 +387,10 @@ fn next_token_from_cursor(&mut self) -> (Token, bool) { RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, prefix_span, ast::CRATE_NODE_ID, - BuiltinLintDiag::RawPrefix(prefix_span), + errors::RawPrefix { + label: prefix_span, + suggestion: prefix_span.shrink_to_hi() + }, ); // Reset the state so we just lex the `'r`. @@ -498,11 +500,41 @@ fn lint_unicode_text_flow(&self, start: BytePos) { let content = self.str_from(content_start); if contains_text_flow_control_chars(content) { let span = self.mk_sp(start, self.pos); - self.psess.buffer_lint( + let content = content.to_string(); + self.psess.dyn_buffer_lint( TEXT_DIRECTION_CODEPOINT_IN_COMMENT, span, ast::CRATE_NODE_ID, - BuiltinLintDiag::UnicodeTextFlow(span, content.to_string()), + move |dcx, level| { + let spans: Vec<_> = content + .char_indices() + .filter_map(|(i, c)| { + TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| { + let lo = span.lo() + BytePos(2 + i as u32); + (c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32))) + }) + }) + .collect(); + let characters = spans + .iter() + .map(|&(c, span)| errors::UnicodeCharNoteSub { + span, + c_debug: format!("{c:?}"), + }) + .collect(); + let suggestions = + (!spans.is_empty()).then_some(errors::UnicodeTextFlowSuggestion { + spans: spans.iter().map(|(_c, span)| *span).collect(), + }); + + errors::UnicodeTextFlow { + comment_span: span, + characters, + suggestions, + num_codepoints: spans.len(), + } + .into_diag(dcx, level) + }, ); } } @@ -1038,7 +1070,11 @@ fn report_unknown_prefix(&self, start: BytePos) { RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX, prefix_span, ast::CRATE_NODE_ID, - BuiltinLintDiag::ReservedPrefix(prefix_span, prefix.to_string()), + errors::ReservedPrefix { + label: prefix_span, + suggestion: prefix_span.shrink_to_hi(), + prefix: prefix.to_string(), + }, ); } } @@ -1112,11 +1148,18 @@ fn maybe_report_guarded_str(&mut self, start: BytePos, str_before: &'src str) -> }) } else { // Before Rust 2024, only emit a lint for migration. - self.psess.buffer_lint( + self.psess.dyn_buffer_lint( RUST_2024_GUARDED_STRING_INCOMPATIBLE_SYNTAX, span, ast::CRATE_NODE_ID, - BuiltinLintDiag::ReservedString { is_string, suggestion: space_span }, + move |dcx, level| { + if is_string { + errors::ReservedStringLint { suggestion: space_span }.into_diag(dcx, level) + } else { + errors::ReservedMultihashLint { suggestion: space_span } + .into_diag(dcx, level) + } + }, ); // For backwards compatibility, roll back to after just the first `#` diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index adfc68f4bb22..7d1a0661aaab 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -22,7 +22,6 @@ use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic}; use rustc_literal_escaper::unescape_char; use rustc_session::errors::{ExprParenthesesNeeded, report_lit_error}; -use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP; use rustc_span::edition::Edition; use rustc_span::{BytePos, ErrorGuaranteed, Ident, Pos, Span, Spanned, Symbol, kw, respan, sym}; @@ -1921,11 +1920,17 @@ fn parse_expr_break(&mut self) -> PResult<'a, Box> { _ => false, } { + let span = expr.span; self.psess.buffer_lint( BREAK_WITH_LABEL_AND_LOOP, lo.to(expr.span), ast::CRATE_NODE_ID, - BuiltinLintDiag::BreakWithLabelAndLoop(expr.span), + errors::BreakWithLabelAndLoop { + sub: errors::BreakWithLabelAndLoopSub { + left: span.shrink_to_lo(), + right: span.shrink_to_hi(), + }, + }, ); } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index bec6ab7e8355..a8ca8011b571 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -16,22 +16,18 @@ use rustc_data_structures::thin_vec::ThinVec; use rustc_data_structures::unord::UnordMap; use rustc_errors::{DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey, msg}; -use rustc_feature::{ - ACCEPTED_LANG_FEATURES, AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, - BuiltinAttribute, -}; +use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute}; use rustc_hir::attrs::diagnostic::Directive; use rustc_hir::attrs::{ AttributeKind, DocAttribute, DocInline, EiiDecl, EiiImpl, EiiImplResolution, InlineAttr, - MirDialect, MirPhase, ReprAttr, SanitizerSet, + ReprAttr, SanitizerSet, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalModDefId; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{ self as hir, Attribute, CRATE_HIR_ID, Constness, FnSig, ForeignItem, GenericParamKind, HirId, - Item, ItemKind, MethodKind, Node, ParamName, PartialConstStability, Safety, Stability, - StabilityLevel, Target, TraitItem, find_attr, + Item, ItemKind, MethodKind, Node, ParamName, Safety, Target, TraitItem, find_attr, }; use rustc_macros::Diagnostic; use rustc_middle::hir::nested_filter; @@ -74,6 +70,10 @@ struct DiagnosticOnConstOnlyForNonConstTraitImpls { item_span: Span, } +#[derive(Diagnostic)] +#[diag("`#[diagnostic::on_move]` can only be applied to enums, structs or unions")] +struct DiagnosticOnMoveOnlyForAdt; + fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, @@ -152,16 +152,6 @@ fn check_attributes( Attribute::Parsed(AttributeKind::ProcMacroDerive { .. }) => { self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) } - Attribute::Parsed( - AttributeKind::Stability { - span: attr_span, - stability: Stability { level, feature }, - } - | AttributeKind::RustcConstStability { - span: attr_span, - stability: PartialConstStability { level, feature, .. }, - }, - ) => self.check_stability(*attr_span, span, level, *feature), Attribute::Parsed(AttributeKind::Inline(InlineAttr::Force { .. }, ..)) => {} // handled separately below Attribute::Parsed(AttributeKind::Inline(kind, attr_span)) => { self.check_inline(hir_id, *attr_span, kind, target) @@ -208,9 +198,6 @@ fn check_attributes( Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => { self.check_may_dangle(hir_id, *attr_span) } - &Attribute::Parsed(AttributeKind::CustomMir(dialect, phase, attr_span)) => { - self.check_custom_mir(dialect, phase, attr_span) - } &Attribute::Parsed(AttributeKind::Sanitize { on_set, off_set, rtsan: _, span: attr_span}) => { self.check_sanitize(attr_span, on_set | off_set, span, target); }, @@ -233,6 +220,9 @@ fn check_attributes( Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)}, Attribute::Parsed(AttributeKind::OnUnimplemented{span, directive}) => {self.check_diagnostic_on_unimplemented(*span, hir_id, target,directive.as_deref())}, Attribute::Parsed(AttributeKind::OnConst{span, ..}) => {self.check_diagnostic_on_const(*span, hir_id, target, item)} + Attribute::Parsed(AttributeKind::OnMove { span, directive }) => { + self.check_diagnostic_on_move(*span, hir_id, target, directive.as_deref()) + }, Attribute::Parsed( // tidy-alphabetical-start AttributeKind::RustcAllowIncoherentImpl(..) @@ -247,6 +237,7 @@ fn check_attributes( | AttributeKind::Coverage (..) | AttributeKind::CrateName { .. } | AttributeKind::CrateType(..) + | AttributeKind::CustomMir(..) | AttributeKind::DebuggerVisualizer(..) | AttributeKind::DefaultLibAllocator // `#[doc]` is actually a lot more than just doc comments, so is checked below @@ -307,6 +298,7 @@ fn check_attributes( | AttributeKind::RustcCoherenceIsCore(..) | AttributeKind::RustcCoinductive(..) | AttributeKind::RustcConfusables { .. } + | AttributeKind::RustcConstStability { .. } | AttributeKind::RustcConstStableIndirect | AttributeKind::RustcConversionSuggestion | AttributeKind::RustcDeallocator @@ -379,6 +371,7 @@ fn check_attributes( | AttributeKind::RustcTrivialFieldReads | AttributeKind::RustcUnsafeSpecializationMarker(..) | AttributeKind::ShouldPanic { .. } + | AttributeKind::Stability { .. } | AttributeKind::TestRunner(..) | AttributeKind::ThreadLocal | AttributeKind::TypeLengthLimit { .. } @@ -684,6 +677,56 @@ fn check_diagnostic_on_const( // The traits' or the impls'? } + /// Checks if `#[diagnostic::on_move]` is applied to an ADT definition + fn check_diagnostic_on_move( + &self, + attr_span: Span, + hir_id: HirId, + target: Target, + directive: Option<&Directive>, + ) { + if !matches!(target, Target::Enum | Target::Struct | Target::Union) { + self.tcx.emit_node_span_lint( + MISPLACED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + DiagnosticOnMoveOnlyForAdt, + ); + } + + if let Some(directive) = directive { + if let Node::Item(Item { + kind: + ItemKind::Struct(_, generics, _) + | ItemKind::Enum(_, generics, _) + | ItemKind::Union(_, generics, _), + .. + }) = self.tcx.hir_node(hir_id) + { + directive.visit_params(&mut |argument_name, span| { + let has_generic = generics.params.iter().any(|p| { + if !matches!(p.kind, GenericParamKind::Lifetime { .. }) + && let ParamName::Plain(name) = p.name + && name.name == argument_name + { + true + } else { + false + } + }); + if !has_generic { + self.tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + hir_id, + span, + errors::OnMoveMalformedFormatLiterals { name: argument_name }, + ) + } + }); + } + } + } + /// Checks if an `#[inline]` is applied to a function or a closure. fn check_inline(&self, hir_id: HirId, attr_span: Span, kind: &InlineAttr, target: Target) { match target { @@ -1515,24 +1558,6 @@ fn check_rustc_allow_const_fn_unstable( } } - fn check_stability( - &self, - attr_span: Span, - item_span: Span, - level: &StabilityLevel, - feature: Symbol, - ) { - // Stable *language* features shouldn't be used as unstable library features. - // (Not doing this for stable library features is checked by tidy.) - if level.is_unstable() - && ACCEPTED_LANG_FEATURES.iter().find(|f| f.name == feature).is_some() - { - self.tcx - .dcx() - .emit_err(errors::UnstableAttrForAlreadyStableFeature { attr_span, item_span }); - } - } - fn check_deprecated(&self, hir_id: HirId, attr_span: Span, target: Target) { match target { Target::AssocConst | Target::Method(..) | Target::AssocTy @@ -1856,48 +1881,6 @@ fn check_const_continue(&self, hir_id: HirId, attr_span: Span, target: Target) { self.dcx().emit_err(errors::ConstContinueAttr { attr_span, node_span }); }; } - - fn check_custom_mir( - &self, - dialect: Option<(MirDialect, Span)>, - phase: Option<(MirPhase, Span)>, - attr_span: Span, - ) { - let Some((dialect, dialect_span)) = dialect else { - if let Some((_, phase_span)) = phase { - self.dcx() - .emit_err(errors::CustomMirPhaseRequiresDialect { attr_span, phase_span }); - } - return; - }; - - match dialect { - MirDialect::Analysis => { - if let Some((MirPhase::Optimized, phase_span)) = phase { - self.dcx().emit_err(errors::CustomMirIncompatibleDialectAndPhase { - dialect, - phase: MirPhase::Optimized, - attr_span, - dialect_span, - phase_span, - }); - } - } - - MirDialect::Built => { - if let Some((phase, phase_span)) = phase { - self.dcx().emit_err(errors::CustomMirIncompatibleDialectAndPhase { - dialect, - phase, - attr_span, - dialect_span, - phase_span, - }); - } - } - MirDialect::Runtime => {} - } - } } impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 228f21c81b94..5e84122fcb10 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -7,7 +7,6 @@ MultiSpan, msg, }; use rustc_hir::Target; -use rustc_hir::attrs::{MirDialect, MirPhase}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::ty::{MainDefinition, Ty}; use rustc_span::{DUMMY_SP, Ident, Span, Symbol}; @@ -872,17 +871,6 @@ pub(crate) struct CannotStabilizeDeprecated { pub item_sp: Span, } -#[derive(Diagnostic)] -#[diag("can't mark as unstable using an already stable feature")] -pub(crate) struct UnstableAttrForAlreadyStableFeature { - #[primary_span] - #[label("this feature is already stable")] - #[help("consider removing the attribute")] - pub attr_span: Span, - #[label("the stability attribute annotates this item")] - pub item_span: Span, -} - #[derive(Diagnostic)] #[diag("{$descr} has missing stability attribute")] pub(crate) struct MissingStabilityAttr<'a> { @@ -1274,28 +1262,6 @@ pub(crate) struct ReprAlignShouldBeAlignStatic { pub item: &'static str, } -#[derive(Diagnostic)] -#[diag("`dialect` key required")] -pub(crate) struct CustomMirPhaseRequiresDialect { - #[primary_span] - pub attr_span: Span, - #[label("`phase` argument requires a `dialect` argument")] - pub phase_span: Span, -} - -#[derive(Diagnostic)] -#[diag("the {$dialect} dialect is not compatible with the {$phase} phase")] -pub(crate) struct CustomMirIncompatibleDialectAndPhase { - pub dialect: MirDialect, - pub phase: MirPhase, - #[primary_span] - pub attr_span: Span, - #[label("this dialect...")] - pub dialect_span: Span, - #[label("... is not compatible with this phase")] - pub phase_span: Span, -} - #[derive(Diagnostic)] #[diag("`eii_macro_for` is only valid on functions")] pub(crate) struct EiiImplNotFunction { @@ -1432,3 +1398,10 @@ pub(crate) struct UnknownFormatParameterForOnUnimplementedAttr { #[help(r#"expect either a generic argument name or {"`{Self}`"} as format argument"#)] pub help: bool, } + +#[derive(Diagnostic)] +#[diag("unknown parameter `{$name}`")] +#[help(r#"expect either a generic argument name or {"`{Self}`"} as format argument"#)] +pub(crate) struct OnMoveMalformedFormatLiterals { + pub name: Symbol, +} diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index dc38f2d8bc70..0226c54db58c 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -462,7 +462,8 @@ pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> { let arity; let fields: Vec<_>; match &pat.kind { - PatKind::Binding { subpattern: Some(subpat), .. } => return self.lower_pat(subpat), + PatKind::Binding { subpattern: Some(subpat), .. } + | PatKind::Guard { subpattern: subpat, .. } => return self.lower_pat(subpat), PatKind::Missing | PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { ctor = Wildcard; fields = vec![]; diff --git a/compiler/rustc_public/src/compiler_interface.rs b/compiler/rustc_public/src/compiler_interface.rs index b0ea1e0f5b84..30fbc5c5233d 100644 --- a/compiler/rustc_public/src/compiler_interface.rs +++ b/compiler/rustc_public/src/compiler_interface.rs @@ -20,7 +20,8 @@ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, CoroutineDef, Discr, FieldDef, FnDef, ForeignDef, ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics, ImplDef, ImplTrait, IntrinsicDef, LineInfo, MirConst, PolyFnSig, RigidTy, Span, - TraitDecl, TraitDef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, VariantIdx, + TraitDecl, TraitDef, TraitRef, Ty, TyConst, TyConstId, TyKind, UintTy, VariantDef, VariantIdx, + VtblEntry, }; use crate::unstable::{RustcInternal, Stable, new_item_kind}; use crate::{ @@ -838,6 +839,25 @@ pub(crate) fn associated_items(&self, def_id: DefId) -> AssocItems { let did = tables[def_id]; cx.associated_items(did).iter().map(|assoc| assoc.stable(&mut *tables, cx)).collect() } + + /// Get all vtable entries of a trait. + pub(crate) fn vtable_entries(&self, trait_ref: &TraitRef) -> Vec { + let mut tables = self.tables.borrow_mut(); + let cx = &*self.cx.borrow(); + cx.vtable_entries(trait_ref.internal(&mut *tables, cx.tcx)) + .iter() + .map(|v| v.stable(&mut *tables, cx)) + .collect() + } + + /// Returns the vtable entry at the given index. + /// + /// Returns `None` if the index is out of bounds. + pub(crate) fn vtable_entry(&self, trait_ref: &TraitRef, idx: usize) -> Option { + let mut tables = self.tables.borrow_mut(); + let cx = &*self.cx.borrow(); + cx.vtable_entry(trait_ref.internal(&mut *tables, cx.tcx), idx).stable(&mut *tables, cx) + } } // A thread local variable that stores a pointer to [`CompilerInterface`]. diff --git a/compiler/rustc_public/src/ty.rs b/compiler/rustc_public/src/ty.rs index 8205d29c4534..7ec8b688402a 100644 --- a/compiler/rustc_public/src/ty.rs +++ b/compiler/rustc_public/src/ty.rs @@ -9,7 +9,7 @@ use crate::abi::{FnAbi, Layout}; use crate::crate_def::{CrateDef, CrateDefType}; use crate::mir::alloc::{AllocId, read_target_int, read_target_uint}; -use crate::mir::mono::StaticDef; +use crate::mir::mono::{Instance, StaticDef}; use crate::target::MachineInfo; use crate::{AssocItems, Filename, IndexedVal, Opaque, ThreadLocalIndex}; @@ -1440,6 +1440,18 @@ pub fn self_ty(&self) -> Ty { }; self_ty } + + /// Retrieve all vtable entries. + pub fn vtable_entries(&self) -> Vec { + with(|cx| cx.vtable_entries(self)) + } + + /// Returns the vtable entry at the given index. + /// + /// Returns `None` if the index is out of bounds. + pub fn vtable_entry(&self, idx: usize) -> Option { + with(|cx| cx.vtable_entry(self, idx)) + } } #[derive(Clone, Debug, Eq, PartialEq, Serialize)] @@ -1656,3 +1668,19 @@ pub fn is_impl_trait_in_trait(&self) -> bool { matches!(self.kind, AssocKind::Type { data: AssocTypeData::Rpitit(_) }) } } + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum VtblEntry { + /// destructor of this type (used in vtable header) + MetadataDropInPlace, + /// layout size of this type (used in vtable header) + MetadataSize, + /// layout align of this type (used in vtable header) + MetadataAlign, + /// non-dispatchable associated function that is excluded from trait object + Vacant, + /// dispatchable associated function + Method(Instance), + /// pointer to a separate supertrait vtable, can be used by trait upcasting coercion + TraitVPtr(TraitRef), +} diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index 4bf98c4f0731..31cc6bd46959 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -1139,3 +1139,25 @@ fn stable<'cx>( crate::ty::Discr { val: self.val, ty: self.ty.stable(tables, cx) } } } + +impl<'tcx> Stable<'tcx> for rustc_middle::ty::VtblEntry<'tcx> { + type T = crate::ty::VtblEntry; + + fn stable<'cx>( + &self, + tables: &mut Tables<'cx, BridgeTys>, + cx: &CompilerCtxt<'cx, BridgeTys>, + ) -> Self::T { + use crate::ty::VtblEntry; + match self { + ty::VtblEntry::MetadataDropInPlace => VtblEntry::MetadataDropInPlace, + ty::VtblEntry::MetadataSize => VtblEntry::MetadataSize, + ty::VtblEntry::MetadataAlign => VtblEntry::MetadataAlign, + ty::VtblEntry::Vacant => VtblEntry::Vacant, + ty::VtblEntry::Method(instance) => VtblEntry::Method(instance.stable(tables, cx)), + ty::VtblEntry::TraitVPtr(trait_ref) => { + VtblEntry::TraitVPtr(trait_ref.stable(tables, cx)) + } + } + } +} diff --git a/compiler/rustc_public_bridge/src/context/impls.rs b/compiler/rustc_public_bridge/src/context/impls.rs index 4418d68c5c3a..359769d4cfe4 100644 --- a/compiler/rustc_public_bridge/src/context/impls.rs +++ b/compiler/rustc_public_bridge/src/context/impls.rs @@ -18,7 +18,7 @@ AdtDef, AdtKind, AssocItem, Binder, ClosureKind, CoroutineArgsExt, EarlyBinder, ExistentialTraitRef, FnSig, GenericArgsRef, Instance, InstanceKind, IntrinsicDef, List, PolyFnSig, ScalarInt, TraitDef, TraitRef, Ty, TyCtxt, TyKind, TypeVisitableExt, UintTy, - ValTree, VariantDef, + ValTree, VariantDef, VtblEntry, }; use rustc_middle::{mir, ty}; use rustc_session::cstore::ForeignModule; @@ -757,4 +757,16 @@ pub fn associated_items(&self, def_id: DefId) -> Vec { }; assoc_items } + + /// Get all vtable entries of a trait. + pub fn vtable_entries(&self, trait_ref: TraitRef<'tcx>) -> Vec> { + self.tcx.vtable_entries(trait_ref).to_vec() + } + + /// Returns the vtable entry at the given index. + /// + /// Returns `None` if the index is out of bounds. + pub fn vtable_entry(&self, trait_ref: TraitRef<'tcx>, idx: usize) -> Option> { + self.vtable_entries(trait_ref).get(idx).copied() + } } diff --git a/compiler/rustc_query_impl/src/dep_kind_vtables.rs b/compiler/rustc_query_impl/src/dep_kind_vtables.rs index 597c8b2a9e74..44b92dc727ab 100644 --- a/compiler/rustc_query_impl/src/dep_kind_vtables.rs +++ b/compiler/rustc_query_impl/src/dep_kind_vtables.rs @@ -96,30 +96,23 @@ pub(crate) fn Metadata<'tcx>() -> DepKindVTable<'tcx> { /// Shared implementation of the [`DepKindVTable`] constructor for queries. /// Called from macro-generated code for each query. pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q>( - is_anon: bool, is_cache_on_disk: bool, is_eval_always: bool, + is_no_force: bool, ) -> DepKindVTable<'tcx> where Q: GetQueryVTable<'tcx>, { - let key_fingerprint_style = if is_anon { - KeyFingerprintStyle::Opaque - } else { - ::Key::key_fingerprint_style() - }; - // A query dep-node can only be forced or promoted if it can recover a key // from its key fingerprint. + let key_fingerprint_style = ::Key::key_fingerprint_style(); let can_recover = key_fingerprint_style.is_maybe_recoverable(); - if is_anon { - assert!(!can_recover); - } DepKindVTable { is_eval_always, key_fingerprint_style, - force_from_dep_node_fn: can_recover.then_some(force_from_dep_node_inner::), + force_from_dep_node_fn: (can_recover && !is_no_force) + .then_some(force_from_dep_node_inner::), promote_from_disk_fn: (can_recover && is_cache_on_disk) .then_some(promote_from_disk_inner::), } @@ -133,12 +126,12 @@ macro_rules! define_dep_kind_vtables { fn $name:ident($K:ty) -> $V:ty { // Search for (QMODLIST) to find all occurrences of this query modifier list. - anon: $anon:literal, arena_cache: $arena_cache:literal, cache_on_disk: $cache_on_disk:literal, depth_limit: $depth_limit:literal, eval_always: $eval_always:literal, feedable: $feedable:literal, + no_force: $no_force:literal, no_hash: $no_hash:literal, returns_error_guaranteed: $returns_error_guaranteed:literal, separate_provide_extern: $separate_provide_extern:literal, @@ -165,9 +158,9 @@ fn $name:ident($K:ty) -> $V:ty $crate::dep_kind_vtables::make_dep_kind_vtable_for_query::< $crate::query_impl::$name::VTableGetter, >( - $anon, $cache_on_disk, $eval_always, + $no_force, ) ),* ]; diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index aea0bb4d3534..bbd55e9ead11 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -424,7 +424,7 @@ fn execute_job_incr<'tcx, C: QueryCache>( let dep_graph_data = tcx.dep_graph.data().expect("should always be present in incremental mode"); - if !query.anon && !query.eval_always { + if !query.eval_always { // `to_dep_node` is expensive for some `DepKind`s. let dep_node = dep_node_opt.get_or_insert_with(|| DepNode::construct(tcx, query.dep_kind, &key)); @@ -451,13 +451,6 @@ fn execute_job_incr<'tcx, C: QueryCache>( let prof_timer = tcx.prof.query_provider(); let (result, dep_node_index) = start_query(job_id, query.depth_limit, || { - if query.anon { - // Call the query provider inside an anon task. - return dep_graph_data.with_anon_task_inner(tcx, query.dep_kind, || { - (query.invoke_provider_fn)(tcx, key) - }); - } - // `to_dep_node` is expensive for some `DepKind`s. let dep_node = dep_node_opt.unwrap_or_else(|| DepNode::construct(tcx, query.dep_kind, &key)); @@ -601,9 +594,6 @@ fn check_if_ensure_can_skip_execution<'tcx, C: QueryCache>( return EnsureCanSkip { skip_execution: false, dep_node: None }; } - // Ensuring an anonymous query makes no sense - assert!(!query.anon); - let dep_node = DepNode::construct(tcx, query.dep_kind, &key); let serialized_dep_node_index = match tcx.dep_graph.try_mark_green(tcx, &dep_node) { @@ -703,8 +693,6 @@ pub(crate) fn force_query<'tcx, C: QueryCache>( return; } - debug_assert!(!query.anon); - ensure_sufficient_stack(|| { try_execute_query::(query, tcx, DUMMY_SP, key, Some(dep_node)) }); diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs index 29877c8aac88..213fc79a6843 100644 --- a/compiler/rustc_query_impl/src/job.rs +++ b/compiler/rustc_query_impl/src/job.rs @@ -155,11 +155,11 @@ fn abstracted_waiters_of(job_map: &QueryJobMap<'_>, query: QueryJobId) -> Vec( +/// the cycle. `stack` will contain just the cycle on return if detected. +fn find_cycle<'tcx>( job_map: &QueryJobMap<'tcx>, query: QueryJobId, span: Span, @@ -190,7 +190,7 @@ fn cycle_check<'tcx>( continue; }; if let ControlFlow::Break(maybe_resumable) = - cycle_check(job_map, parent, abstracted_waiter.span, stack, visited) + find_cycle(job_map, parent, abstracted_waiter.span, stack, visited) { // Return the resumable waiter in `waiter.resumable` if present return ControlFlow::Break(abstracted_waiter.resumable.or(maybe_resumable)); @@ -232,7 +232,7 @@ fn connected_to_root<'tcx>( false } -/// Looks for query cycles starting from the last query in `jobs`. +/// Looks for a query cycle using the last query in `jobs`. /// If a cycle is found, all queries in the cycle is removed from `jobs` and /// the function return true. /// If a cycle was not found, the starting query is removed from `jobs` and @@ -246,7 +246,7 @@ fn remove_cycle<'tcx>( let mut stack = Vec::new(); // Look for a cycle starting with the last query in `jobs` if let ControlFlow::Break(resumable) = - cycle_check(job_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) + find_cycle(job_map, jobs.pop().unwrap(), DUMMY_SP, &mut stack, &mut visited) { // The stack is a vector of pairs of spans and queries; reverse it so that // the earlier entries require later entries diff --git a/compiler/rustc_query_impl/src/query_impl.rs b/compiler/rustc_query_impl/src/query_impl.rs index caf4c7dde7e6..56b7534da9ed 100644 --- a/compiler/rustc_query_impl/src/query_impl.rs +++ b/compiler/rustc_query_impl/src/query_impl.rs @@ -16,12 +16,12 @@ macro_rules! define_queries { fn $name:ident($K:ty) -> $V:ty { // Search for (QMODLIST) to find all occurrences of this query modifier list. - anon: $anon:literal, arena_cache: $arena_cache:literal, cache_on_disk: $cache_on_disk:literal, depth_limit: $depth_limit:literal, eval_always: $eval_always:literal, feedable: $feedable:literal, + no_force: $no_force:literal, no_hash: $no_hash:literal, returns_error_guaranteed: $returns_error_guaranteed:literal, separate_provide_extern: $separate_provide_extern:literal, @@ -132,7 +132,6 @@ pub(crate) fn make_query_vtable<'tcx>(incremental: bool) QueryVTable { name: stringify!($name), - anon: $anon, eval_always: $eval_always, depth_limit: $depth_limit, feedable: $feedable, diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 80538aebf5f6..d9d42cfefcd8 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -898,7 +898,7 @@ fn build_reduced_graph_for_item(&mut self, item: &'a Item) { } // These items live in both the type and value namespaces. - ItemKind::Struct(ident, _, ref vdata) => { + ItemKind::Struct(ident, ref generics, ref vdata) => { self.build_reduced_graph_for_struct_variant( vdata.fields(), ident, @@ -947,6 +947,7 @@ fn build_reduced_graph_for_item(&mut self, item: &'a Item) { .struct_constructors .insert(local_def_id, (ctor_res, ctor_vis.to_def_id(), ret_fields)); } + self.r.struct_generics.insert(local_def_id, generics.clone()); } ItemKind::Union(ident, _, ref vdata) => { diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 5aa09657a807..24c923de6794 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -560,6 +560,10 @@ pub(crate) struct ExpectedModuleFound { #[diag("cannot determine resolution for the visibility", code = E0578)] pub(crate) struct Indeterminate(#[primary_span] pub(crate) Span); +#[derive(Diagnostic)] +#[diag("trait implementation can only be restricted to ancestor modules")] +pub(crate) struct RestrictionAncestorOnly(#[primary_span] pub(crate) Span); + #[derive(Diagnostic)] #[diag("cannot use a tool module through an import")] pub(crate) struct ToolModuleImported { @@ -883,6 +887,21 @@ pub(crate) struct UnexpectedResChangeTyToConstParamSugg { pub applicability: Applicability, } +#[derive(Subdiagnostic)] +#[suggestion( + "you might have meant to introduce a const parameter `{$item_name}` on the {$item_location}", + code = "{snippet}", + applicability = "machine-applicable", + style = "verbose" +)] +pub(crate) struct UnexpectedMissingConstParameter { + #[primary_span] + pub span: Span, + pub snippet: String, + pub item_name: String, + pub item_location: String, +} + #[derive(Subdiagnostic)] #[multipart_suggestion( "you might have meant to write a const parameter here", diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 453fe9d7a8e0..a484a5136db5 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -29,10 +29,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{MissingLifetimeKind, PrimTy, TraitCandidate}; use rustc_middle::middle::resolve_bound_vars::Set1; -use rustc_middle::ty::{ - AssocTag, DELEGATION_INHERIT_ATTRS_START, DelegationAttrs, DelegationFnSig, - DelegationFnSigAttrs, DelegationInfo, Visibility, -}; +use rustc_middle::ty::{AssocTag, DelegationInfo, Visibility}; use rustc_middle::{bug, span_bug}; use rustc_session::config::{CrateType, ResolveDocLinks}; use rustc_session::lint; @@ -43,9 +40,9 @@ use tracing::{debug, instrument, trace}; use crate::{ - BindingError, BindingKey, Decl, Finalize, IdentKey, LateDecl, Module, ModuleOrUniformRoot, - ParentScope, PathResult, ResolutionError, Resolver, Segment, Stage, TyCtxt, UseError, Used, - errors, path_names_to_string, rustdoc, + BindingError, BindingKey, Decl, DelegationFnSig, Finalize, IdentKey, LateDecl, Module, + ModuleOrUniformRoot, ParentScope, PathResult, ResolutionError, Resolver, Segment, Stage, + TyCtxt, UseError, Used, errors, path_names_to_string, rustdoc, }; mod diagnostics; @@ -452,6 +449,8 @@ pub(crate) enum PathSource<'a, 'ast, 'ra> { DefineOpaques, /// Resolving a macro Macro, + /// Paths for module or crate root. Used for restrictions. + Module, } impl PathSource<'_, '_, '_> { @@ -460,7 +459,8 @@ fn namespace(self) -> Namespace { PathSource::Type | PathSource::Trait(_) | PathSource::Struct(_) - | PathSource::DefineOpaques => TypeNS, + | PathSource::DefineOpaques + | PathSource::Module => TypeNS, PathSource::Expr(..) | PathSource::Pat | PathSource::TupleStruct(..) @@ -485,7 +485,8 @@ fn defer_to_typeck(self) -> bool { | PathSource::DefineOpaques | PathSource::Delegation | PathSource::PreciseCapturingArg(..) - | PathSource::Macro => false, + | PathSource::Macro + | PathSource::Module => false, } } @@ -528,6 +529,7 @@ fn descr_expected(self) -> &'static str { PathSource::ReturnTypeNotation | PathSource::Delegation => "function", PathSource::PreciseCapturingArg(..) => "type or const parameter", PathSource::Macro => "macro", + PathSource::Module => "module", } } @@ -626,6 +628,7 @@ pub(crate) fn is_expected(self, res: Res) -> bool { ), PathSource::PreciseCapturingArg(MacroNS) => false, PathSource::Macro => matches!(res, Res::Def(DefKind::Macro(_), _)), + PathSource::Module => matches!(res, Res::Def(DefKind::Mod, _)), } } @@ -646,6 +649,12 @@ fn error_code(self, has_unexpected_resolution: bool) -> ErrCode { (PathSource::PreciseCapturingArg(..), true) => E0799, (PathSource::PreciseCapturingArg(..), false) => E0800, (PathSource::Macro, _) => E0425, + // FIXME: There is no dedicated error code for this case yet. + // E0577 already covers the same situation for visibilities, + // so we reuse it here for now. It may make sense to generalize + // it for restrictions in the future. + (PathSource::Module, true) => E0577, + (PathSource::Module, false) => E0433, } } } @@ -2174,7 +2183,8 @@ fn resolve_elided_lifetimes_in_path( | PathSource::Type | PathSource::PreciseCapturingArg(..) | PathSource::ReturnTypeNotation - | PathSource::Macro => false, + | PathSource::Macro + | PathSource::Module => false, PathSource::Expr(..) | PathSource::Pat | PathSource::Struct(_) @@ -2800,7 +2810,10 @@ fn resolve_item(&mut self, item: &'ast Item) { self.diag_metadata.current_impl_items = None; } - ItemKind::Trait(box Trait { generics, bounds, items, .. }) => { + ItemKind::Trait(box Trait { generics, bounds, items, impl_restriction, .. }) => { + // resolve paths for `impl` restrictions + self.resolve_impl_restriction_path(impl_restriction); + // Create a new rib for the trait-wide type parameters. self.with_generic_param_rib( &generics.params, @@ -2989,7 +3002,7 @@ fn resolve_item(&mut self, item: &'ast Item) { item.id, LifetimeBinderKind::Function, span, - |this| this.resolve_delegation(delegation, item.id, false, &item.attrs), + |this| this.resolve_delegation(delegation, item.id, false), ); } @@ -3335,7 +3348,7 @@ fn resolve_trait_items(&mut self, trait_items: &'ast [Box]) { item.id, LifetimeBinderKind::Function, delegation.path.segments.last().unwrap().ident.span, - |this| this.resolve_delegation(delegation, item.id, false, &item.attrs), + |this| this.resolve_delegation(delegation, item.id, false), ); } AssocItemKind::Type(box TyAlias { generics, .. }) => self @@ -3649,7 +3662,7 @@ fn resolve_impl_item( // Here we don't use `trait_id`, as we can process unresolved trait, however // in this case we are still in a trait impl, https://github.com/rust-lang/rust/issues/150152 - this.resolve_delegation(delegation, item.id, is_in_trait_impl, &item.attrs); + this.resolve_delegation(delegation, item.id, is_in_trait_impl); }, ); } @@ -3804,7 +3817,6 @@ fn resolve_delegation( delegation: &'ast Delegation, item_id: NodeId, is_in_trait_impl: bool, - attrs: &[Attribute], ) { self.smart_resolve_path( delegation.id, @@ -3822,7 +3834,6 @@ fn resolve_delegation( self.r.delegation_infos.insert( self.r.local_def_id(item_id), DelegationInfo { - attrs: create_delegation_attrs(attrs), resolution_node: if is_in_trait_impl { item_id } else { delegation.id }, }, ); @@ -4389,6 +4400,25 @@ fn try_resolve_as_non_binding( } } + fn resolve_impl_restriction_path(&mut self, restriction: &'ast ast::ImplRestriction) { + match &restriction.kind { + ast::RestrictionKind::Unrestricted => (), + ast::RestrictionKind::Restricted { path, id, shorthand: _ } => { + self.smart_resolve_path(*id, &None, path, PathSource::Module); + if let Some(res) = self.r.partial_res_map[&id].full_res() + && let Some(def_id) = res.opt_def_id() + { + if !self.r.is_accessible_from( + Visibility::Restricted(def_id), + self.parent_scope.module, + ) { + self.r.dcx().create_err(errors::RestrictionAncestorOnly(path.span)).emit(); + } + } + } + } + } + // High-level and context dependent path resolution routine. // Resolves the path and records the resolution into definition map. // If resolution fails tries several techniques to find likely @@ -4425,7 +4455,7 @@ fn smart_resolve_path_fragment( let Finalize { node_id, path_span, .. } = finalize; let report_errors = |this: &mut Self, res: Option| { if this.should_report_errs() { - let (err, candidates) = this.smart_resolve_report_errors( + let (mut err, candidates) = this.smart_resolve_report_errors( path, None, path_span, @@ -4436,7 +4466,8 @@ fn smart_resolve_path_fragment( let def_id = this.parent_scope.module.nearest_parent_mod(); let instead = res.is_some(); - let suggestion = if let Some((start, end)) = this.diag_metadata.in_range + let (suggestion, const_err) = if let Some((start, end)) = + this.diag_metadata.in_range && path[0].ident.span.lo() == end.span.lo() && !matches!(start.kind, ExprKind::Lit(_)) { @@ -4448,12 +4479,15 @@ fn smart_resolve_path_fragment( span = span.with_lo(span.lo() + BytePos(1)); sugg = ""; } - Some(( - span, - "you might have meant to write `.` instead of `..`", - sugg.to_string(), - Applicability::MaybeIncorrect, - )) + ( + Some(( + span, + "you might have meant to write `.` instead of `..`", + sugg.to_string(), + Applicability::MaybeIncorrect, + )), + None, + ) } else if res.is_none() && let PathSource::Type | PathSource::Expr(_) @@ -4461,9 +4495,14 @@ fn smart_resolve_path_fragment( { this.suggest_adding_generic_parameter(path, source) } else { - None + (None, None) }; + if let Some(const_err) = const_err { + err.cancel(); + err = const_err; + } + let ue = UseError { err, candidates, @@ -5433,52 +5472,13 @@ struct ItemInfoCollector<'a, 'ra, 'tcx> { } impl ItemInfoCollector<'_, '_, '_> { - fn collect_fn_info( - &mut self, - header: FnHeader, - decl: &FnDecl, - id: NodeId, - attrs: &[Attribute], - ) { - self.r.delegation_fn_sigs.insert( - self.r.local_def_id(id), - DelegationFnSig { - header, - param_count: decl.inputs.len(), - has_self: decl.has_self(), - c_variadic: decl.c_variadic(), - attrs: create_delegation_attrs(attrs), - }, - ); + fn collect_fn_info(&mut self, decl: &FnDecl, id: NodeId) { + self.r + .delegation_fn_sigs + .insert(self.r.local_def_id(id), DelegationFnSig { has_self: decl.has_self() }); } } -fn create_delegation_attrs(attrs: &[Attribute]) -> DelegationAttrs { - static NAMES_TO_FLAGS: &[(Symbol, DelegationFnSigAttrs)] = &[ - (sym::target_feature, DelegationFnSigAttrs::TARGET_FEATURE), - (sym::must_use, DelegationFnSigAttrs::MUST_USE), - ]; - - let mut to_inherit_attrs = AttrVec::new(); - let mut flags = DelegationFnSigAttrs::empty(); - - 'attrs_loop: for attr in attrs { - for &(name, flag) in NAMES_TO_FLAGS { - if attr.has_name(name) { - flags.set(flag, true); - - if flag.bits() >= DELEGATION_INHERIT_ATTRS_START.bits() { - to_inherit_attrs.push(attr.clone()); - } - - continue 'attrs_loop; - } - } - } - - DelegationAttrs { flags, to_inherit: to_inherit_attrs } -} - fn required_generic_args_suggestion(generics: &ast::Generics) -> Option { let required = generics .params @@ -5518,7 +5518,7 @@ fn visit_item(&mut self, item: &'ast Item) { | ItemKind::Trait(box Trait { generics, .. }) | ItemKind::TraitAlias(box TraitAlias { generics, .. }) => { if let ItemKind::Fn(box Fn { sig, .. }) = &item.kind { - self.collect_fn_info(sig.header, &sig.decl, item.id, &item.attrs); + self.collect_fn_info(&sig.decl, item.id); } let def_id = self.r.local_def_id(item.id); @@ -5530,12 +5530,10 @@ fn visit_item(&mut self, item: &'ast Item) { self.r.item_generics_num_lifetimes.insert(def_id, count); } - ItemKind::ForeignMod(ForeignMod { extern_span, safety: _, abi, items }) => { + ItemKind::ForeignMod(ForeignMod { items, .. }) => { for foreign_item in items { if let ForeignItemKind::Fn(box Fn { sig, .. }) = &foreign_item.kind { - let new_header = - FnHeader { ext: Extern::from_abi(*abi, *extern_span), ..sig.header }; - self.collect_fn_info(new_header, &sig.decl, foreign_item.id, &item.attrs); + self.collect_fn_info(&sig.decl, foreign_item.id); } } } @@ -5561,7 +5559,7 @@ fn visit_item(&mut self, item: &'ast Item) { fn visit_assoc_item(&mut self, item: &'ast AssocItem, ctxt: AssocCtxt) { if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { - self.collect_fn_info(sig.header, &sig.decl, item.id, &item.attrs); + self.collect_fn_info(&sig.decl, item.id); } if let AssocItemKind::Type(box ast::TyAlias { generics, .. }) = &item.kind { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 80e7a957c4d2..a8ca4faf5ab0 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -6,11 +6,12 @@ use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt, Visitor, walk_ty}; use rustc_ast::{ - self as ast, AssocItemKind, DUMMY_NODE_ID, Expr, ExprKind, GenericParam, GenericParamKind, - Item, ItemKind, MethodCall, NodeId, Path, PathSegment, Ty, TyKind, + self as ast, AngleBracketedArg, AssocItemKind, DUMMY_NODE_ID, Expr, ExprKind, GenericArg, + GenericArgs, GenericParam, GenericParamKind, Item, ItemKind, MethodCall, NodeId, Path, + PathSegment, Ty, TyKind, }; use rustc_ast_pretty::pprust::{path_to_string, where_bound_predicate_to_string}; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_errors::codes::*; use rustc_errors::{ Applicability, Diag, ErrorGuaranteed, MultiSpan, SuggestionStyle, pluralize, @@ -37,8 +38,8 @@ }; use crate::ty::fast_reject::SimplifiedType; use crate::{ - Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PathSource, Resolver, - ScopeSet, Segment, errors, path_names_to_string, + Finalize, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PathSource, + Resolver, ScopeSet, Segment, errors, path_names_to_string, }; type Res = def::Res; @@ -3277,11 +3278,203 @@ fn suggest_using_enum_variant( } } - pub(crate) fn suggest_adding_generic_parameter( - &self, + /// Detects missing const parameters in `impl` blocks and suggests adding them. + /// + /// When a const parameter is used in the self type of an `impl` but not declared + /// in the `impl`'s own generic parameter list, this function emits a targeted + /// diagnostic with a suggestion to add it at the correct position. + /// + /// Example: + /// + /// ```rust,ignore (suggested field is not completely correct, it should be a single suggestion) + /// struct C; + /// + /// impl Foo for C {} + /// // ^ the struct `C` in `C` is used as the self type + /// // ^ ^ ^ but A, X and P are not declared on the impl + /// + /// Suggested fix: + /// + /// impl Foo for C {} + /// + /// Current behavior (suggestions are emitted one-by-one): + /// + /// impl Foo for C {} + /// impl Foo for C {} + /// impl Foo for C {} + /// + /// Ideally the suggestion should aggregate them into a single line: + /// + /// impl Foo for C {} + /// ``` + /// + pub(crate) fn detect_and_suggest_const_parameter_error( + &mut self, path: &[Segment], - source: PathSource<'_, '_, '_>, - ) -> Option<(Span, &'static str, String, Applicability)> { + source: PathSource<'_, 'ast, 'ra>, + ) -> Option> { + let Some(item) = self.diag_metadata.current_item else { return None }; + let ItemKind::Impl(impl_) = &item.kind else { return None }; + let self_ty = &impl_.self_ty; + + // Represents parameter to the struct whether `A`, `X` or `P` + let [current_parameter] = path else { + return None; + }; + + let target_ident = current_parameter.ident; + + // Find the parent segment i.e `C` in `C` + let visitor = ParentPathVisitor::new(self_ty, target_ident); + + let Some(parent_segment) = visitor.parent else { + return None; + }; + + let Some(args) = parent_segment.args.as_ref() else { + return None; + }; + + let GenericArgs::AngleBracketed(angle) = args.as_ref() else { + return None; + }; + + // Build map: NodeId of each usage in C -> its position + // e.g NodeId(A) -> 0, NodeId(X) -> 1, NodeId(C) -> 2 + let usage_to_pos: FxHashMap = angle + .args + .iter() + .enumerate() + .filter_map(|(pos, arg)| { + if let AngleBracketedArg::Arg(GenericArg::Type(ty)) = arg + && let TyKind::Path(_, path) = &ty.kind + && let [segment] = path.segments.as_slice() + { + Some((segment.id, pos)) + } else { + None + } + }) + .collect(); + + // Get the position of the missing param in C + // e.g for missing `B` in `C` this gives idx=1 + let Some(idx) = current_parameter.id.and_then(|id| usage_to_pos.get(&id).copied()) else { + return None; + }; + + // Now resolve the parent struct `C` to get its definition + let ns = source.namespace(); + let segment = Segment::from(parent_segment); + let segments = [segment]; + let finalize = Finalize::new(parent_segment.id, parent_segment.ident.span); + + if let Ok(Some(resolve)) = self.resolve_qpath_anywhere( + &None, + &segments, + ns, + source.defer_to_typeck(), + finalize, + source, + ) && let Some(resolve) = resolve.full_res() + && let Res::Def(_, def_id) = resolve + && def_id.is_local() + && let Some(local_def_id) = def_id.as_local() + && let Some(struct_generics) = self.r.struct_generics.get(&local_def_id) + && let target_param = &struct_generics.params[idx] + && let GenericParamKind::Const { ty, .. } = &target_param.kind + && let TyKind::Path(_, path) = &ty.kind + { + let full_type = path + .segments + .iter() + .map(|seg| seg.ident.to_string()) + .collect::>() + .join("::"); + + // Find the first impl param whose position in C + // is strictly greater than our missing param's index + // e.g missing B(idx=1), impl has A(pos=0) and C(pos=2) + // C has pos=2 > 1 so insert before C + let next_impl_param = impl_.generics.params.iter().find(|impl_param| { + angle + .args + .iter() + .find_map(|arg| { + if let AngleBracketedArg::Arg(GenericArg::Type(ty)) = arg + && let TyKind::Path(_, path) = &ty.kind + && let [segment] = path.segments.as_slice() + && segment.ident == impl_param.ident + { + usage_to_pos.get(&segment.id).copied() + } else { + None + } + }) + .map_or(false, |pos| pos > idx) + }); + + let (insert_span, snippet) = match next_impl_param { + Some(next_param) => { + // Insert in the middle before next_param + // e.g impl -> impl + ( + next_param.span().shrink_to_lo(), + format!("const {}: {}, ", target_ident, full_type), + ) + } + None => match impl_.generics.params.last() { + Some(last) => { + // Append after last existing param + // e.g impl -> impl + ( + last.span().shrink_to_hi(), + format!(", const {}: {}", target_ident, full_type), + ) + } + None => { + // No generics at all on impl + // e.g impl Foo for C -> impl Foo for C + ( + impl_.generics.span.shrink_to_hi(), + format!("", target_ident, full_type), + ) + } + }, + }; + + let mut err = self.r.dcx().struct_span_err( + target_ident.span, + format!("cannot find const `{}` in this scope", target_ident), + ); + + err.code(E0425); + + err.span_label(target_ident.span, "not found in this scope"); + + err.span_label( + target_param.span(), + format!("corresponding const parameter on the type defined here",), + ); + + err.subdiagnostic(errors::UnexpectedMissingConstParameter { + span: insert_span, + snippet, + item_name: format!("{}", target_ident), + item_location: String::from("impl"), + }); + + return Some(err); + } + + None + } + + pub(crate) fn suggest_adding_generic_parameter( + &mut self, + path: &[Segment], + source: PathSource<'_, 'ast, 'ra>, + ) -> (Option<(Span, &'static str, String, Applicability)>, Option>) { let (ident, span) = match path { [segment] if !segment.has_generic_args @@ -3290,13 +3483,13 @@ pub(crate) fn suggest_adding_generic_parameter( { (segment.ident.to_string(), segment.ident.span) } - _ => return None, + _ => return (None, None), }; let mut iter = ident.chars().map(|c| c.is_uppercase()); let single_uppercase_char = matches!(iter.next(), Some(true)) && matches!(iter.next(), None); if !self.diag_metadata.currently_processing_generic_args && !single_uppercase_char { - return None; + return (None, None); } match (self.diag_metadata.current_item, single_uppercase_char, self.diag_metadata.currently_processing_generic_args) { (Some(Item { kind: ItemKind::Fn(fn_), .. }), _, _) if fn_.ident.name == sym::main => { @@ -3326,18 +3519,21 @@ pub(crate) fn suggest_adding_generic_parameter( // | ^- help: you might be missing a type parameter: `, A` // | | // | not found in this scope - return None; + return (None, None); } let (msg, sugg) = match source { PathSource::Type | PathSource::PreciseCapturingArg(TypeNS) => { + if let Some(err) = self.detect_and_suggest_const_parameter_error(path, source) { + return (None, Some(err)); + } ("you might be missing a type parameter", ident) } PathSource::Expr(_) | PathSource::PreciseCapturingArg(ValueNS) => ( "you might be missing a const parameter", format!("const {ident}: /* Type */"), ), - _ => return None, + _ => return (None, None), }; let (span, sugg) = if let [.., param] = &generics.params[..] { let span = if let [.., bound] = ¶m.bounds[..] { @@ -3355,18 +3551,18 @@ pub(crate) fn suggest_adding_generic_parameter( }; // Do not suggest if this is coming from macro expansion. if span.can_be_used_for_suggestions() { - return Some(( + return (Some(( span.shrink_to_hi(), msg, sugg, Applicability::MaybeIncorrect, - )); + )), None); } } } _ => {} } - None + (None, None) } /// Given the target `label`, search the `rib_index`th label rib for similarly named labels, @@ -4349,3 +4545,44 @@ pub(super) fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident .with_span_label(shadower, format!("label `{name}` already in scope")) .emit(); } + +struct ParentPathVisitor<'a> { + target: Ident, + parent: Option<&'a PathSegment>, + stack: Vec<&'a Ty>, +} + +impl<'a> ParentPathVisitor<'a> { + fn new(self_ty: &'a Ty, target: Ident) -> Self { + let mut v = ParentPathVisitor { target, parent: None, stack: Vec::new() }; + + v.visit_ty(self_ty); + v + } +} + +impl<'a> Visitor<'a> for ParentPathVisitor<'a> { + fn visit_ty(&mut self, ty: &'a Ty) { + if self.parent.is_some() { + return; + } + + // push current type + self.stack.push(ty); + + if let TyKind::Path(_, path) = &ty.kind + // is this just `N`? + && let [segment] = path.segments.as_slice() + && segment.ident == self.target + // parent is previous element in stack + && let [.., parent_ty, _ty] = self.stack.as_slice() + && let TyKind::Path(_, parent_path) = &parent_ty.kind + { + self.parent = parent_path.segments.first(); + } + + walk_ty(self, ty); + + self.stack.pop(); + } +} diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 7c942191d467..27d41825331b 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -40,7 +40,7 @@ use rustc_ast::node_id::NodeMap; use rustc_ast::{ self as ast, AngleBracketedArg, CRATE_NODE_ID, Crate, Expr, ExprKind, GenericArg, GenericArgs, - NodeId, Path, attr, + Generics, NodeId, Path, attr, }; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, default}; use rustc_data_structures::intern::Interned; @@ -65,8 +65,8 @@ use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::query::Providers; use rustc_middle::ty::{ - self, DelegationFnSig, DelegationInfo, Feed, MainDefinition, RegisteredTools, - ResolverAstLowering, ResolverGlobalCtxt, TyCtxt, TyCtxtFeed, Visibility, + self, DelegationInfo, Feed, MainDefinition, RegisteredTools, ResolverAstLowering, + ResolverGlobalCtxt, TyCtxt, TyCtxtFeed, Visibility, }; use rustc_session::config::CrateType; use rustc_session::lint::builtin::PRIVATE_MACRO_USE; @@ -1157,6 +1157,11 @@ pub struct ResolverOutputs<'tcx> { pub ast_lowering: ResolverAstLowering<'tcx>, } +#[derive(Debug)] +struct DelegationFnSig { + pub has_self: bool, +} + /// The main resolver class. /// /// This is the visitor that walks the whole crate. @@ -1317,6 +1322,10 @@ pub struct Resolver<'ra, 'tcx> { /// Also includes of list of each fields visibility struct_constructors: LocalDefIdMap<(Res, Visibility, Vec>)> = Default::default(), + /// for all the struct + /// it's not used during normal resolution, only for better error reporting. + struct_generics: LocalDefIdMap = Default::default(), + lint_buffer: LintBuffer, next_node_id: NodeId = CRATE_NODE_ID, @@ -1856,7 +1865,6 @@ pub fn into_outputs(self) -> ResolverOutputs<'tcx> { trait_map: self.trait_map, lifetime_elision_allowed: self.lifetime_elision_allowed, lint_buffer: Steal::new(self.lint_buffer), - delegation_fn_sigs: self.delegation_fn_sigs, delegation_infos: self.delegation_infos, }; ResolverOutputs { global_ctxt, ast_lowering } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index d6c708f7a12e..6ae9d3aaeb23 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -707,7 +707,7 @@ fn smart_resolve_macro_path( } const DIAG_ATTRS: &[Symbol] = - &[sym::on_unimplemented, sym::do_not_recommend, sym::on_const]; + &[sym::on_unimplemented, sym::do_not_recommend, sym::on_const, sym::on_move]; if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) && let [namespace, attribute, ..] = &*path.segments diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index cb3f7363957e..d1899de067b4 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -517,17 +517,6 @@ pub(crate) struct FailedToCreateProfiler { pub(crate) err: String, } -#[derive(Diagnostic)] -#[diag("`-Csoft-float` is ignored on this target; it only has an effect on *eabihf targets")] -#[note("this may become a hard error in a future version of Rust")] -pub(crate) struct SoftFloatIgnored; - -#[derive(Diagnostic)] -#[diag("`-Csoft-float` is unsound and deprecated; use a corresponding *eabi target instead")] -#[note("it will be removed or ignored in a future version of Rust")] -#[note("see issue #129893 for more information")] -pub(crate) struct SoftFloatDeprecated; - #[derive(Diagnostic)] #[diag("unexpected `--cfg {$cfg}` flag")] #[note("config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}`")] diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 98731a235d41..1741dde90f5c 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -1,5 +1,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![feature(const_option_ops)] +#![feature(const_trait_impl)] #![feature(default_field_values)] #![feature(iter_intersperse)] #![feature(macro_derive)] diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 5db8ed823dfe..fb1b3c867948 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -611,7 +611,7 @@ macro_rules! options { $parse:ident, [$dep_tracking_marker:ident $( $tmod:ident )?], $desc:expr - $(, is_deprecated_and_do_nothing: $dnn:literal )?) + $(, removed: $removed:ident )?) ),* ,) => ( #[derive(Clone)] @@ -667,7 +667,7 @@ pub fn gather_target_modifiers( pub const $stat: OptionDescrs<$struct_name> = &[ $( OptionDesc{ name: stringify!($opt), setter: $optmod::$opt, - type_desc: desc::$parse, desc: $desc, is_deprecated_and_do_nothing: false $( || $dnn )?, + type_desc: desc::$parse, desc: $desc, removed: None $( .or(Some(RemovedOption::$removed)) )?, tmod: tmod_enum_opt!($struct_name, $tmod_enum_name, $opt, $($tmod),*) } ),* ]; mod $optmod { @@ -705,6 +705,12 @@ macro_rules! redirect_field { type OptionSetter = fn(&mut O, v: Option<&str>) -> bool; type OptionDescrs = &'static [OptionDesc]; +/// Indicates whether a removed option should warn or error. +enum RemovedOption { + Warn, + Err, +} + pub struct OptionDesc { name: &'static str, setter: OptionSetter, @@ -712,7 +718,7 @@ pub struct OptionDesc { type_desc: &'static str, // description for option from options table desc: &'static str, - is_deprecated_and_do_nothing: bool, + removed: Option, tmod: Option, } @@ -743,18 +749,18 @@ fn build_options( let option_to_lookup = key.replace('-', "_"); match descrs.iter().find(|opt_desc| opt_desc.name == option_to_lookup) { - Some(OptionDesc { - name: _, - setter, - type_desc, - desc, - is_deprecated_and_do_nothing, - tmod, - }) => { - if *is_deprecated_and_do_nothing { + Some(OptionDesc { name: _, setter, type_desc, desc, removed, tmod }) => { + if let Some(removed) = removed { // deprecation works for prefixed options only assert!(!prefix.is_empty()); - early_dcx.early_warn(format!("`-{prefix} {key}`: {desc}")); + match removed { + RemovedOption::Warn => { + early_dcx.early_warn(format!("`-{prefix} {key}`: {desc}")) + } + RemovedOption::Err => { + early_dcx.early_fatal(format!("`-{prefix} {key}`: {desc}")) + } + } } if !setter(&mut op, value) { match value { @@ -783,6 +789,7 @@ fn build_options( #[allow(non_upper_case_globals)] mod desc { + pub(crate) const parse_ignore: &str = ""; // should not be user-visible pub(crate) const parse_no_value: &str = "no value"; pub(crate) const parse_bool: &str = "one of: `y`, `yes`, `on`, `true`, `n`, `no`, `off` or `false`"; @@ -889,6 +896,12 @@ pub mod parse { pub(crate) use super::*; pub(crate) const MAX_THREADS_CAP: usize = 256; + /// Ignore the value. Used for removed options where we don't actually want to store + /// anything in the session. + pub(crate) fn parse_ignore(_slot: &mut (), _v: Option<&str>) -> bool { + true + } + /// This is for boolean options that don't take a value, and are true simply /// by existing on the command-line. /// @@ -2059,7 +2072,7 @@ pub(crate) fn parse_align(slot: &mut Option, v: Option<&str>) -> bool { #[rustc_lint_opt_deny_field_access("documented to do nothing")] ar: String = (String::new(), parse_string, [UNTRACKED], "this option is deprecated and does nothing", - is_deprecated_and_do_nothing: true), + removed: Warn), #[rustc_lint_opt_deny_field_access("use `Session::code_model` instead of this field")] code_model: Option = (None, parse_code_model, [TRACKED], "choose the code model to use (`rustc --print code-models` for details)"), @@ -2098,7 +2111,7 @@ pub(crate) fn parse_align(slot: &mut Option, v: Option<&str>) -> bool { inline_threshold: Option = (None, parse_opt_number, [UNTRACKED], "this option is deprecated and does nothing \ (consider using `-Cllvm-args=--inline-threshold=...`)", - is_deprecated_and_do_nothing: true), + removed: Warn), #[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")] instrument_coverage: InstrumentCoverage = (InstrumentCoverage::No, parse_instrument_coverage, [TRACKED], "instrument the generated code to support LLVM source-based code coverage reports \ @@ -2139,7 +2152,7 @@ pub(crate) fn parse_align(slot: &mut Option, v: Option<&str>) -> bool { #[rustc_lint_opt_deny_field_access("documented to do nothing")] no_stack_check: bool = (false, parse_no_value, [UNTRACKED], "this option is deprecated and does nothing", - is_deprecated_and_do_nothing: true), + removed: Warn), no_vectorize_loops: bool = (false, parse_no_value, [TRACKED], "disable loop vectorization optimization passes"), no_vectorize_slp: bool = (false, parse_no_value, [TRACKED], @@ -2173,8 +2186,11 @@ pub(crate) fn parse_align(slot: &mut Option, v: Option<&str>) -> bool { "set rpath values in libs/exes (default: no)"), save_temps: bool = (false, parse_bool, [UNTRACKED], "save all temporary output files during compilation (default: no)"), - soft_float: bool = (false, parse_bool, [TRACKED], - "deprecated option: use soft float ABI (*eabihf targets only) (default: no)"), + #[rustc_lint_opt_deny_field_access("documented to do nothing")] + soft_float: () = ((), parse_ignore, [UNTRACKED], + "this option has been removed \ + (use a corresponding *eabi target instead)", + removed: Err), #[rustc_lint_opt_deny_field_access("use `Session::split_debuginfo` instead of this field")] split_debuginfo: Option = (None, parse_split_debuginfo, [TRACKED], "how to handle split-debuginfo, a platform-specific option"), @@ -2240,7 +2256,7 @@ pub(crate) fn parse_align(slot: &mut Option, v: Option<&str>) -> bool { (default: no)"), box_noalias: bool = (true, parse_bool, [TRACKED], "emit noalias metadata for box (default: yes)"), - branch_protection: Option = (None, parse_branch_protection, [TRACKED], + branch_protection: Option = (None, parse_branch_protection, [TRACKED TARGET_MODIFIER], "set options for branch target identification and pointer authentication on AArch64"), build_sdylib_interface: bool = (false, parse_bool, [UNTRACKED], "whether the stable interface is being built"), diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 2b1520274fb6..a8210f2315f1 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -7,12 +7,12 @@ use rustc_ast::attr::AttrIdGenerator; use rustc_ast::node_id::NodeId; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; -use rustc_data_structures::sync::{AppendOnlyVec, Lock}; +use rustc_data_structures::sync::{AppendOnlyVec, DynSend, Lock}; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; use rustc_errors::emitter::{EmitterWithNote, stderr_destination}; use rustc_errors::{ BufferedEarlyLint, ColorConfig, DecorateDiagCompat, Diag, DiagCtxt, DiagCtxtHandle, - DiagMessage, EmissionGuarantee, MultiSpan, StashKey, + DiagMessage, EmissionGuarantee, Level, MultiSpan, StashKey, }; use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue}; use rustc_span::edition::Edition; @@ -331,6 +331,23 @@ pub fn buffer_lint( self.opt_span_buffer_lint(lint, Some(span.into()), node_id, diagnostic.into()) } + pub fn dyn_buffer_lint< + F: for<'a> FnOnce(DiagCtxtHandle<'a>, Level) -> Diag<'a, ()> + DynSend + 'static, + >( + &self, + lint: &'static Lint, + span: impl Into, + node_id: NodeId, + callback: F, + ) { + self.opt_span_buffer_lint( + lint, + Some(span.into()), + node_id, + DecorateDiagCompat::Dynamic(Box::new(callback)), + ) + } + pub(crate) fn opt_span_buffer_lint( &self, lint: &'static Lint, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 8290bdf91a03..fece46dd9711 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1360,16 +1360,6 @@ fn validate_commandline_args_with_session_available(sess: &Session) { } } } - - if sess.opts.cg.soft_float { - if sess.target.arch == Arch::Arm { - sess.dcx().emit_warn(errors::SoftFloatDeprecated); - } else { - // All `use_softfp` does is the equivalent of `-mfloat-abi` in GCC/clang, which only exists on ARM targets. - // We document this flag to only affect `*eabihf` targets, so let's show a warning for all other targets. - sess.dcx().emit_warn(errors::SoftFloatIgnored); - } - } } /// Holds data on the current incremental compilation session, if there is one. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 286fea7d9050..738c9b975fd0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -797,6 +797,7 @@ diagnostic, diagnostic_namespace, diagnostic_on_const, + diagnostic_on_move, dialect, direct, discriminant_kind, @@ -1407,6 +1408,7 @@ omit_gdb_pretty_printer_section, on, on_const, + on_move, on_unimplemented, opaque, opaque_generic_const_args, diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 5cb8922574b6..7fcaea3a629f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -158,6 +158,7 @@ fn try_get_prefix(&self) -> Option<&str> { struct ClosureEraser<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, + depth: usize, } impl<'a, 'tcx> ClosureEraser<'a, 'tcx> { @@ -172,7 +173,8 @@ fn cx(&self) -> TyCtxt<'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind() { + self.depth += 1; + let ty = match ty.kind() { ty::Closure(_, args) => { // For a closure type, we turn it into a function pointer so that it gets rendered // as `fn(args) -> Ret`. @@ -233,9 +235,15 @@ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { // its type parameters. ty.super_fold_with(self) } - // We don't have an unknown type parameter anywhere, replace with `_`. + // We are in the top-level type, not one of its type parameters. Name it with its + // parameters replaced. + _ if self.depth == 1 => ty.super_fold_with(self), + // We don't have an unknown type parameter anywhere, and we are in a type parameter. + // Replace with `_`. _ => self.new_infer(), - } + }; + self.depth -= 1; + ty } fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { @@ -287,7 +295,7 @@ fn ty_to_string<'tcx>( let ty = infcx.resolve_vars_if_possible(ty); // We use `fn` ptr syntax for closures, but this only works when the closure does not capture // anything. We also remove all type parameters that are fully known to the type system. - let ty = ty.fold_with(&mut ClosureEraser { infcx }); + let ty = ty.fold_with(&mut ClosureEraser { infcx, depth: 0 }); match (ty.kind(), called_method_def_id) { // We don't want the regular output for `fn`s because it includes its path in @@ -467,6 +475,25 @@ pub fn emit_inference_failure_err( term: Term<'tcx>, error_code: TypeAnnotationNeeded, should_label_span: bool, + ) -> Diag<'a> { + self.emit_inference_failure_err_with_type_hint( + body_def_id, + failure_span, + term, + error_code, + should_label_span, + None, + ) + } + + pub fn emit_inference_failure_err_with_type_hint( + &self, + body_def_id: LocalDefId, + failure_span: Span, + term: Term<'tcx>, + error_code: TypeAnnotationNeeded, + should_label_span: bool, + ty: Option>, ) -> Diag<'a> { let term = self.resolve_vars_if_possible(term); let arg_data = self @@ -479,7 +506,7 @@ pub fn emit_inference_failure_err( return self.bad_inference_failure_err(failure_span, arg_data, error_code); }; - let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term); + let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term, ty); if let Some(body) = self.tcx.hir_maybe_body_owned_by( self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(), ) { @@ -779,10 +806,20 @@ fn ty_localized_msg(&self, infcx: &InferCtxt<'tcx>) -> (&'static str, String, Op | InferSourceKind::ClosureReturn { ty, .. } => { if ty.is_closure() { ("closure", closure_as_fn_str(infcx, ty), long_ty_path) - } else if !ty.is_ty_or_numeric_infer() { - ("normal", infcx.tcx.short_string(ty, &mut long_ty_path), long_ty_path) - } else { + } else if ty.is_ty_or_numeric_infer() + || ty.is_primitive() + || matches!( + ty.kind(), + ty::Adt(_, args) + if args.types().count() == 0 && args.consts().count() == 0 + ) + { + // `ty` is either `_`, a primitive type like `u32` or a type with no type or + // const parameters. We will not mention the type in the main inference error + // message. ("other", String::new(), long_ty_path) + } else { + ("normal", infcx.tcx.short_string(ty, &mut long_ty_path), long_ty_path) } } // FIXME: We should be able to add some additional info here. @@ -815,6 +852,7 @@ struct FindInferSourceVisitor<'a, 'tcx> { typeck_results: &'a TypeckResults<'tcx>, target: Term<'tcx>, + ty: Option>, attempt: usize, infer_source_cost: usize, @@ -826,12 +864,14 @@ fn new( tecx: &'a TypeErrCtxt<'a, 'tcx>, typeck_results: &'a TypeckResults<'tcx>, target: Term<'tcx>, + ty: Option>, ) -> Self { FindInferSourceVisitor { tecx, typeck_results, target, + ty, attempt: 0, infer_source_cost: usize::MAX, @@ -1213,7 +1253,7 @@ fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) { intravisit::walk_local(self, local); - if let Some(ty) = self.opt_node_type(local.hir_id) { + if let Some(mut ty) = self.opt_node_type(local.hir_id) { if self.generic_arg_contains_target(ty.into()) { fn get_did( typeck_results: &TypeckResults<'_>, @@ -1241,7 +1281,11 @@ fn get_did( _ => None, } } - + if let Some(t) = self.ty + && ty.has_infer() + { + ty = t; + } if let LocalSource::Normal = local.source && local.ty.is_none() { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 7de8891196d8..7f5ed9ecb6d1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -246,12 +246,37 @@ pub(super) fn maybe_report_ambiguity( .find(|s| s.has_non_region_infer()); let mut err = if let Some(term) = term { - self.emit_inference_failure_err( + let candidates: Vec<_> = self + .tcx + .all_impls(trait_pred.def_id()) + .filter_map(|def_id| { + let imp = self.tcx.impl_trait_header(def_id); + if imp.polarity != ty::ImplPolarity::Positive + || !self.tcx.is_user_visible_dep(def_id.krate) + { + return None; + } + let imp = imp.trait_ref.skip_binder(); + if imp + .with_replaced_self_ty(self.tcx, trait_pred.skip_binder().self_ty()) + == trait_pred.skip_binder().trait_ref + { + Some(imp.self_ty()) + } else { + None + } + }) + .collect(); + self.emit_inference_failure_err_with_type_hint( obligation.cause.body_id, span, term, TypeAnnotationNeeded::E0283, true, + match &candidates[..] { + [candidate] => Some(*candidate), + _ => None, + }, ) } else { struct_span_code_err!( diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d5383fd4d083..6bf08170bcf7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2251,6 +2251,16 @@ pub(super) fn report_similar_impl_candidates( if candidates.is_empty() { return false; } + let mut specific_candidates = candidates.clone(); + specific_candidates.retain(|(tr, _)| { + tr.with_replaced_self_ty(self.tcx, trait_pred.skip_binder().self_ty()) + == trait_pred.skip_binder().trait_ref + }); + if !specific_candidates.is_empty() { + // We have found a subset of impls that fully satisfy the expected trait, only + // mention those types. + candidates = specific_candidates; + } if let &[(cand, def_id)] = &candidates[..] { if self.tcx.is_diagnostic_item(sym::FromResidual, cand.def_id) && !self.tcx.features().enabled(sym::try_trait_v2) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index d08e0fa3521b..4ae6584fc2d1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -124,8 +124,11 @@ pub(crate) fn on_unimplemented_components( } } - if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) { - crate_local = true; + if let Some(adt) = self_ty.ty_adt_def() { + if adt.did().is_local() { + crate_local = true; + } + self_types.push(format!("{{{}}}", adt.descr())) } // Allow targeting all integers using `{integral}`, even if the exact type was resolved 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 ee2f8d9783cf..4a6d5eb48f8f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4037,12 +4037,13 @@ pub fn can_suggest_derive( _ => return false, }; let is_derivable_trait = match diagnostic_name { - sym::Default => !adt.is_enum(), + sym::Copy | sym::Clone => true, + _ if adt.is_union() => false, sym::PartialEq | sym::PartialOrd => { let rhs_ty = trait_pred.skip_binder().trait_ref.args.type_at(1); trait_pred.skip_binder().self_ty() == rhs_ty } - sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true, + sym::Eq | sym::Ord | sym::Hash | sym::Debug | sym::Default => true, _ => false, }; is_derivable_trait && diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs index 5d200c4d340b..c7699f31a0f9 100644 --- a/compiler/rustc_trait_selection/src/solve.rs +++ b/compiler/rustc_trait_selection/src/solve.rs @@ -1,3 +1,8 @@ +use std::mem; + +use rustc_infer::infer::InferCtxt; +use rustc_infer::traits::{Obligation, ObligationCause}; +use rustc_middle::hooks::TypeErasedInfcx; pub use rustc_next_trait_solver::solve::*; mod delegate; @@ -13,10 +18,13 @@ deeply_normalize, deeply_normalize_with_skipped_universes, deeply_normalize_with_skipped_universes_and_ambiguous_coroutine_goals, }; -use rustc_middle::query::Providers; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::util::Providers; +use rustc_span::Span; pub use select::InferCtxtSelectExt; +use crate::traits::ObligationCtxt; + fn evaluate_root_goal_for_proof_tree_raw<'tcx>( tcx: TyCtxt<'tcx>, canonical_input: CanonicalInput>, @@ -27,6 +35,46 @@ fn evaluate_root_goal_for_proof_tree_raw<'tcx>( ) } -pub fn provide(providers: &mut Providers) { - *providers = Providers { evaluate_root_goal_for_proof_tree_raw, ..*providers }; +fn try_eagerly_normalize_alias<'a, 'tcx>( + tcx: TyCtxt<'tcx>, + type_erased_infcx: TypeErasedInfcx<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + alias: ty::AliasTy<'tcx>, +) -> Ty<'tcx> { + let infcx = unsafe { + mem::transmute::, &'a InferCtxt<'tcx>>(type_erased_infcx) + }; + + let ocx = ObligationCtxt::new(infcx); + + let infer_term = infcx.next_ty_var(span); + + // Dummy because we ignore the error anyway. + // We do provide a span, because this span is used when registering opaque types. + // For example, if we don't provide a span here, some diagnostics talking about TAIT will refer to a dummy span. + let cause = ObligationCause::dummy_with_span(span); + let obligation = Obligation::new( + tcx, + cause, + param_env, + ty::PredicateKind::AliasRelate( + alias.to_ty(tcx).into(), + infer_term.into(), + ty::AliasRelationDirection::Equate, + ), + ); + + ocx.register_obligation(obligation); + + // We only use this to constrain inference variables. + // We don't care if it errors. + let _ = ocx.try_evaluate_obligations(); + + infcx.resolve_vars_if_possible(infer_term) +} + +pub fn provide(providers: &mut Providers) { + providers.hooks.try_eagerly_normalize_alias = try_eagerly_normalize_alias; + providers.queries.evaluate_root_goal_for_proof_tree_raw = evaluate_root_goal_for_proof_tree_raw; } diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 1d444079f896..49e0bdde3787 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -58,7 +58,8 @@ fn recurse_build<'tcx>( } &ExprKind::Literal { lit, neg } => { let sp = node.span; - match tcx.at(sp).lit_to_const(LitToConstInput { lit: lit.node, ty: node.ty, neg }) { + match tcx.at(sp).lit_to_const(LitToConstInput { lit: lit.node, ty: Some(node.ty), neg }) + { Some(value) => ty::Const::new_value(tcx, value.valtree, value.ty), None => ty::Const::new_misc_error(tcx), } diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs index cef964ab139a..9fb16af75aaf 100644 --- a/compiler/rustc_ty_utils/src/representability.rs +++ b/compiler/rustc_ty_utils/src/representability.rs @@ -19,7 +19,7 @@ fn check_representability(tcx: TyCtxt<'_>, def_id: LocalDefId) { DefKind::Struct | DefKind::Union | DefKind::Enum => { for variant in tcx.adt_def(def_id).variants() { for field in variant.fields.iter() { - let _ = tcx.check_representability(field.did.expect_local()); + tcx.ensure_ok().check_representability(field.did.expect_local()); } } } @@ -35,7 +35,7 @@ fn check_representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) { // This one must be a query rather than a vanilla `check_representability_adt_ty` call. See // the comment on `check_representability_adt_ty` below for why. ty::Adt(..) => { - let _ = tcx.check_representability_adt_ty(ty); + tcx.ensure_ok().check_representability_adt_ty(ty); } // FIXME(#11924) allow zero-length arrays? ty::Array(ty, _) => { @@ -69,7 +69,7 @@ fn check_representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) { fn check_representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) { let ty::Adt(adt, args) = ty.kind() else { bug!("expected adt") }; if let Some(def_id) = adt.did().as_local() { - let _ = tcx.check_representability(def_id); + tcx.ensure_ok().check_representability(def_id); } // At this point, we know that the item of the ADT type is representable; // but the type parameters may cause a cycle with an upstream type diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index 78f3a1922887..7a84a87a789a 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -117,7 +117,7 @@ fn adt_sizedness_constraint<'tcx>( (def_id, sizedness): (DefId, SizedTraitKind), ) -> Option>> { if let Some(def_id) = def_id.as_local() { - let _ = tcx.check_representability(def_id); + tcx.ensure_ok().check_representability(def_id); } let def = tcx.adt_def(def_id); diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml index 10c53c881a36..b00f9eebb826 100644 --- a/compiler/rustc_type_ir/Cargo.toml +++ b/compiler/rustc_type_ir/Cargo.toml @@ -7,7 +7,7 @@ edition = "2024" # tidy-alphabetical-start arrayvec = { version = "0.7", default-features = false } bitflags = "2.4.1" -derive-where = "1.2.7" +derive-where = "1.6.1" ena = "0.14.4" indexmap = "2.0.0" rustc-hash = "2.0.0" diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs index fc8b39f7c01f..0b0f0fd2f424 100644 --- a/compiler/rustc_type_ir/src/binder.rs +++ b/compiler/rustc_type_ir/src/binder.rs @@ -26,11 +26,7 @@ /// for more details. /// /// `Decodable` and `Encodable` are implemented for `Binder` using the `impl_binder_encode_decode!` macro. -// FIXME(derive-where#136): Need to use separate `derive_where` for -// `Copy` and `Ord` to prevent the emitted `Clone` and `PartialOrd` -// impls from incorrectly relying on `T: Copy` and `T: Ord`. -#[derive_where(Copy; I: Interner, T: Copy)] -#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, T)] +#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner, T)] #[derive(GenericTypeVisitable)] #[cfg_attr(feature = "nightly", derive(HashStable_NoContext))] pub struct Binder { @@ -365,12 +361,7 @@ fn visit_region(&mut self, r: I::Region) -> Self::Result { /// `instantiate`. /// /// See for more details. -// FIXME(derive-where#136): Need to use separate `derive_where` for -// `Copy` and `Ord` to prevent the emitted `Clone` and `PartialOrd` -// impls from incorrectly relying on `T: Copy` and `T: Ord`. -#[derive_where(Ord; I: Interner, T: Ord)] -#[derive_where(Copy; I: Interner, T: Copy)] -#[derive_where(Clone, PartialOrd, PartialEq, Hash, Debug; I: Interner, T)] +#[derive_where(Clone, Copy, PartialOrd, Ord, PartialEq, Hash, Debug; I: Interner, T)] #[derive(GenericTypeVisitable)] #[cfg_attr( feature = "nightly", @@ -964,12 +955,7 @@ pub enum BoundVarIndexKind { /// The "placeholder index" fully defines a placeholder region, type, or const. Placeholders are /// identified by both a universe, as well as a name residing within that universe. Distinct bound /// regions/types/consts within the same universe simply have an unknown relationship to one -// FIXME(derive-where#136): Need to use separate `derive_where` for -// `Copy` and `Ord` to prevent the emitted `Clone` and `PartialOrd` -// impls from incorrectly relying on `T: Copy` and `T: Ord`. -#[derive_where(Ord; I: Interner, T: Ord)] -#[derive_where(Copy; I: Interner, T: Copy)] -#[derive_where(Clone, PartialOrd, PartialEq, Eq, Hash; I: Interner, T)] +#[derive_where(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash; I: Interner, T)] #[derive(TypeVisitable_Generic, TypeFoldable_Generic)] #[cfg_attr( feature = "nightly", diff --git a/compiler/rustc_type_ir/src/relate/combine.rs b/compiler/rustc_type_ir/src/relate/combine.rs index 64b87fac77f9..72d54c23733e 100644 --- a/compiler/rustc_type_ir/src/relate/combine.rs +++ b/compiler/rustc_type_ir/src/relate/combine.rs @@ -40,6 +40,8 @@ fn register_predicates( /// Register `AliasRelate` obligation(s) that both types must be related to each other. fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty); + + fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTy) -> I::Ty; } pub fn super_combine_tys( diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs index 82ee4f75fcb0..541b2531fe74 100644 --- a/compiler/rustc_type_ir/src/relate/solver_relating.rs +++ b/compiler/rustc_type_ir/src/relate/solver_relating.rs @@ -15,6 +15,7 @@ fn relate>( variance: ty::Variance, rhs: T, span: ::Span, + normalize: &mut dyn FnMut(ty::AliasTy) -> ::Ty, ) -> Result< Vec::Predicate>>, TypeError, @@ -32,40 +33,46 @@ fn eq_structurally_relating_aliases>( >; } -impl RelateExt for Infcx { - fn relate>( +impl> RelateExt for Infcx { + fn relate>( &self, - param_env: ::ParamEnv, + param_env: I::ParamEnv, lhs: T, variance: ty::Variance, rhs: T, - span: ::Span, - ) -> Result< - Vec::Predicate>>, - TypeError, - > { - let mut relate = - SolverRelating::new(self, StructurallyRelateAliases::No, variance, param_env, span); + span: I::Span, + normalize: &mut dyn FnMut(ty::AliasTy) -> I::Ty, + ) -> Result>, TypeError> { + let mut relate = SolverRelating::new( + self, + StructurallyRelateAliases::No, + variance, + param_env, + span, + normalize, + ); relate.relate(lhs, rhs)?; Ok(relate.goals) } - fn eq_structurally_relating_aliases>( + fn eq_structurally_relating_aliases>( &self, - param_env: ::ParamEnv, + param_env: I::ParamEnv, lhs: T, rhs: T, - span: ::Span, - ) -> Result< - Vec::Predicate>>, - TypeError, - > { + span: I::Span, + ) -> Result>, TypeError> { + // Structurally relating, we treat aliases as rigid, + // so we shouldn't ever try to normalize them. + let mut normalize_unreachable = |_alias| unreachable!(); + let mut relate = SolverRelating::new( self, StructurallyRelateAliases::Yes, ty::Invariant, param_env, span, + &mut normalize_unreachable, ); relate.relate(lhs, rhs)?; Ok(relate.goals) @@ -75,12 +82,14 @@ fn eq_structurally_relating_aliases>( /// Enforce that `a` is equal to or a subtype of `b`. pub struct SolverRelating<'infcx, Infcx, I: Interner> { infcx: &'infcx Infcx, + // Immutable fields. structurally_relate_aliases: StructurallyRelateAliases, param_env: I::ParamEnv, span: I::Span, // Mutable fields. ambient_variance: ty::Variance, + normalize: &'infcx mut dyn FnMut(ty::AliasTy) -> I::Ty, goals: Vec>, /// The cache only tracks the `ambient_variance` as it's the /// only field which is mutable and which meaningfully changes @@ -118,12 +127,14 @@ pub fn new( ambient_variance: ty::Variance, param_env: I::ParamEnv, span: I::Span, + normalize: &'infcx mut dyn FnMut(ty::AliasTy) -> I::Ty, ) -> Self { SolverRelating { infcx, structurally_relate_aliases, span, ambient_variance, + normalize, param_env, goals: vec![], cache: Default::default(), @@ -406,4 +417,8 @@ fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty) { } })]); } + + fn try_eagerly_normalize_alias(&mut self, alias: ty::AliasTy) -> I::Ty { + (self.normalize)(alias) + } } diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index aa1901314e37..bc668f78bf74 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -87,6 +87,7 @@ /// ``` #[cfg(target_has_atomic = "ptr")] #[stable(feature = "wake_trait", since = "1.51.0")] +#[rustc_diagnostic_item = "Wake"] pub trait Wake { /// Wake this task. #[stable(feature = "wake_trait", since = "1.51.0")] diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs index 3eb8ca44a9d1..99ebcb4ada29 100644 --- a/library/alloc/src/vec/splice.rs +++ b/library/alloc/src/vec/splice.rs @@ -1,4 +1,4 @@ -use core::{ptr, slice}; +use core::ptr; use super::{Drain, Vec}; use crate::alloc::{Allocator, Global}; @@ -107,15 +107,13 @@ unsafe fn fill>(&mut self, replace_with: &mut I) -> bool { let vec = unsafe { self.vec.as_mut() }; let range_start = vec.len; let range_end = self.tail_start; - let range_slice = unsafe { - slice::from_raw_parts_mut(vec.as_mut_ptr().add(range_start), range_end - range_start) - }; + // The elements in this range are not initialized so we avoid creating a slice. - for place in range_slice { + for idx in range_start..range_end { let Some(new_item) = replace_with.next() else { return false; }; - unsafe { ptr::write(place, new_item) }; + unsafe { vec.as_mut_ptr().add(idx).write(new_item) }; vec.len += 1; } true diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index b3dc435dda17..49d7487c2803 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -357,6 +357,7 @@ fn assert_fields_are_eq(&self) {} } /// Derive macro generating an impl of the trait [`Eq`]. +/// The behavior of this macro is described in detail [here](Eq#derivable). #[rustc_builtin_macro] #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow_internal_unstable(core_intrinsics, derive_eq_internals, structural_match)] diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 05d8e0d84f05..ecd1e34078e2 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -1037,9 +1037,10 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> Result { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented( on( - crate_local, + all(crate_local, not(Self = "{union}")), note = "add `#[derive(Debug)]` to `{Self}` or manually `impl {This} for {Self}`" ), + on(all(crate_local, Self = "{union}"), note = "manually `impl {This} for {Self}`"), on( from_desugaring = "FormatLiteral", label = "`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{This}`" diff --git a/library/core/src/fmt/num.rs b/library/core/src/fmt/num.rs index d3d6495e75a2..dfa27e32bc72 100644 --- a/library/core/src/fmt/num.rs +++ b/library/core/src/fmt/num.rs @@ -823,7 +823,7 @@ fn enc_16lsd(buf: &mut [MaybeUninit], n: u64) { let mut remain = n; // Format per four digits from the lookup table. - for quad_index in (0..4).rev() { + for quad_index in (1..4).rev() { // pull two pairs let quad = remain % 1_00_00; remain /= 1_00_00; @@ -834,6 +834,14 @@ fn enc_16lsd(buf: &mut [MaybeUninit], n: u64) { buf[quad_index * 4 + OFFSET + 2].write(DECIMAL_PAIRS[pair2 * 2 + 0]); buf[quad_index * 4 + OFFSET + 3].write(DECIMAL_PAIRS[pair2 * 2 + 1]); } + + // final two pairs + let pair1 = (remain / 100) as usize; + let pair2 = (remain % 100) as usize; + buf[OFFSET + 0].write(DECIMAL_PAIRS[pair1 * 2 + 0]); + buf[OFFSET + 1].write(DECIMAL_PAIRS[pair1 * 2 + 1]); + buf[OFFSET + 2].write(DECIMAL_PAIRS[pair2 * 2 + 0]); + buf[OFFSET + 3].write(DECIMAL_PAIRS[pair2 * 2 + 1]); } /// Euclidean division plus remainder with constant 1E16 basically consumes 16 diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 164c27464c60..35f93d8fb33b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -107,8 +107,6 @@ #![feature(core_intrinsics)] #![feature(coverage_attribute)] #![feature(disjoint_bitor)] -#![feature(internal_impls_macro)] -#![feature(link_cfg)] #![feature(offset_of_enum)] #![feature(panic_internals)] #![feature(pattern_type_macro)] @@ -144,6 +142,7 @@ #![feature(intra_doc_pointers)] #![feature(intrinsics)] #![feature(lang_items)] +#![feature(link_cfg)] #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] #![feature(macro_metavar_expr_concat)] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index becac514284e..c79e8fc4060c 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -52,9 +52,6 @@ /// u32, /// } /// ``` -#[unstable(feature = "internal_impls_macro", issue = "none")] -// Allow implementations of `UnsizedConstParamTy` even though std cannot use that feature. -#[allow_internal_unstable(const_param_ty_trait)] macro marker_impls { ( $(#[$($meta:tt)*])* $Trait:ident for $({$($bounds:tt)*})? $T:ty $(, $($rest:tt)*)? ) => { $(#[$($meta)*])* impl< $($($bounds)*)? > $Trait for $T {} diff --git a/library/core/src/num/imp/dec2flt/decimal.rs b/library/core/src/num/imp/dec2flt/decimal.rs index 27a53d4b9e75..c9b0cc531b38 100644 --- a/library/core/src/num/imp/dec2flt/decimal.rs +++ b/library/core/src/num/imp/dec2flt/decimal.rs @@ -1,6 +1,6 @@ //! Representation of a float as the significant digits and exponent. -use dec2flt::float::RawFloat; +use dec2flt::Lemire; use dec2flt::fpu::set_precision; use crate::num::imp::dec2flt; @@ -36,7 +36,7 @@ pub struct Decimal { impl Decimal { /// Detect if the float can be accurately reconstructed from native floats. #[inline] - fn can_use_fast_path(&self) -> bool { + fn can_use_fast_path(&self) -> bool { F::MIN_EXPONENT_FAST_PATH <= self.exponent && self.exponent <= F::MAX_EXPONENT_DISGUISED_FAST_PATH && self.mantissa <= F::MAX_MANTISSA_FAST_PATH @@ -53,7 +53,7 @@ fn can_use_fast_path(&self) -> bool { /// /// There is an exception: disguised fast-path cases, where we can shift /// powers-of-10 from the exponent to the significant digits. - pub fn try_fast_path(&self) -> Option { + pub fn try_fast_path(&self) -> Option { // Here we need to work around . // The fast path crucially depends on arithmetic being rounded to the correct number of bits // without any intermediate rounding. On x86 (without SSE or SSE2) this requires the precision diff --git a/library/core/src/num/imp/dec2flt/lemire.rs b/library/core/src/num/imp/dec2flt/lemire.rs index c3f2723509d0..f89d16c84347 100644 --- a/library/core/src/num/imp/dec2flt/lemire.rs +++ b/library/core/src/num/imp/dec2flt/lemire.rs @@ -1,10 +1,9 @@ //! Implementation of the Eisel-Lemire algorithm. use dec2flt::common::BiasedFp; -use dec2flt::float::RawFloat; use dec2flt::table::{LARGEST_POWER_OF_FIVE, POWER_OF_FIVE_128, SMALLEST_POWER_OF_FIVE}; -use crate::num::imp::dec2flt; +use crate::num::imp::{Float, dec2flt}; /// Compute w * 10^q using an extended-precision float representation. /// @@ -24,7 +23,7 @@ /// at a Gigabyte per Second" in section 5, "Fast Algorithm", and /// section 6, "Exact Numbers And Ties", available online: /// . -pub fn compute_float(q: i64, mut w: u64) -> BiasedFp { +pub fn compute_float(q: i64, mut w: u64) -> BiasedFp { let fp_zero = BiasedFp::zero_pow2(0); let fp_inf = BiasedFp::zero_pow2(F::INFINITE_POWER); let fp_error = BiasedFp::zero_pow2(-1); diff --git a/library/core/src/num/imp/dec2flt/mod.rs b/library/core/src/num/imp/dec2flt/mod.rs index 3f5724add62b..76b8d416ee0f 100644 --- a/library/core/src/num/imp/dec2flt/mod.rs +++ b/library/core/src/num/imp/dec2flt/mod.rs @@ -88,24 +88,104 @@ )] use common::BiasedFp; -use float::RawFloat; use lemire::compute_float; use parse::{parse_inf_nan, parse_number}; use slow::parse_long_mantissa; +use crate::f64; use crate::num::ParseFloatError; use crate::num::float_parse::FloatErrorKind; +use crate::num::imp::FloatExt; mod common; pub mod decimal; pub mod decimal_seq; mod fpu; -mod slow; -mod table; -// float is used in flt2dec, and all are used in unit tests. -pub mod float; pub mod lemire; pub mod parse; +mod slow; +mod table; + +/// Extension to `Float` that are necessary for parsing using the Lemire method. +/// +/// See the parent module's doc comment for why this is necessary. +/// +/// Not intended for use outside of the `dec2flt` module. +#[doc(hidden)] +pub trait Lemire: FloatExt { + /// Maximum exponent for a fast path case, or `⌊(SIG_BITS+1)/log2(5)⌋` + // assuming FLT_EVAL_METHOD = 0 + const MAX_EXPONENT_FAST_PATH: i64 = { + let log2_5 = f64::consts::LOG2_10 - 1.0; + (Self::SIG_TOTAL_BITS as f64 / log2_5) as i64 + }; + + /// Minimum exponent for a fast path case, or `-⌊(SIG_BITS+1)/log2(5)⌋` + const MIN_EXPONENT_FAST_PATH: i64 = -Self::MAX_EXPONENT_FAST_PATH; + + /// Maximum exponent that can be represented for a disguised-fast path case. + /// This is `MAX_EXPONENT_FAST_PATH + ⌊(SIG_BITS+1)/log2(10)⌋` + const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 = + Self::MAX_EXPONENT_FAST_PATH + (Self::SIG_TOTAL_BITS as f64 / f64::consts::LOG2_10) as i64; + + /// Maximum mantissa for the fast-path (`1 << 53` for f64). + const MAX_MANTISSA_FAST_PATH: u64 = 1 << Self::SIG_TOTAL_BITS; + + /// Gets a small power-of-ten for fast-path multiplication. + fn pow10_fast_path(exponent: usize) -> Self; + + /// Converts integer into float through an as cast. + /// This is only called in the fast-path algorithm, and therefore + /// will not lose precision, since the value will always have + /// only if the value is <= Self::MAX_MANTISSA_FAST_PATH. + fn from_u64(v: u64) -> Self; +} + +#[cfg(target_has_reliable_f16)] +impl Lemire for f16 { + fn pow10_fast_path(exponent: usize) -> Self { + #[allow(clippy::use_self)] + const TABLE: [f16; 8] = [1e0, 1e1, 1e2, 1e3, 1e4, 0.0, 0.0, 0.]; + TABLE[exponent & 7] + } + + #[inline] + fn from_u64(v: u64) -> Self { + debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); + v as _ + } +} + +impl Lemire for f32 { + fn pow10_fast_path(exponent: usize) -> Self { + #[allow(clippy::use_self)] + const TABLE: [f32; 16] = + [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 0., 0., 0., 0., 0.]; + TABLE[exponent & 15] + } + + #[inline] + fn from_u64(v: u64) -> Self { + debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); + v as _ + } +} + +impl Lemire for f64 { + fn pow10_fast_path(exponent: usize) -> Self { + const TABLE: [f64; 32] = [ + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, + 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 0., 0., 0., 0., 0., 0., 0., 0., 0., + ]; + TABLE[exponent & 31] + } + + #[inline] + fn from_u64(v: u64) -> Self { + debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); + v as _ + } +} #[inline] pub(super) fn pfe_empty() -> ParseFloatError { @@ -120,7 +200,7 @@ pub fn pfe_invalid() -> ParseFloatError { } /// Converts a `BiasedFp` to the closest machine float type. -fn biased_fp_to_float(x: BiasedFp) -> F { +fn biased_fp_to_float(x: BiasedFp) -> F { let mut word = x.m; word |= (x.p_biased as u64) << F::SIG_BITS; F::from_u64_bits(word) @@ -128,7 +208,7 @@ fn biased_fp_to_float(x: BiasedFp) -> F { /// Converts a decimal string into a floating point number. #[inline(always)] // Will be inlined into a function with `#[inline(never)]`, see above -pub fn dec2flt(s: &str) -> Result { +pub fn dec2flt(s: &str) -> Result { let mut s = s.as_bytes(); let Some(&c) = s.first() else { return Err(pfe_empty()) }; let negative = c == b'-'; diff --git a/library/core/src/num/imp/dec2flt/parse.rs b/library/core/src/num/imp/dec2flt/parse.rs index e4049bc164c6..ee55eadbc7b9 100644 --- a/library/core/src/num/imp/dec2flt/parse.rs +++ b/library/core/src/num/imp/dec2flt/parse.rs @@ -2,9 +2,8 @@ use dec2flt::common::{ByteSlice, is_8digits}; use dec2flt::decimal::Decimal; -use dec2flt::float::RawFloat; -use crate::num::imp::dec2flt; +use crate::num::imp::{Float, dec2flt}; const MIN_19DIGIT_INT: u64 = 100_0000_0000_0000_0000; @@ -197,7 +196,7 @@ pub fn parse_number(s: &[u8]) -> Option { } /// Try to parse a special, non-finite float. -pub(crate) fn parse_inf_nan(s: &[u8], negative: bool) -> Option { +pub(crate) fn parse_inf_nan(s: &[u8], negative: bool) -> Option { // Since a valid string has at most the length 8, we can load // all relevant characters into a u64 and work from there. // This also generates much better code. diff --git a/library/core/src/num/imp/dec2flt/slow.rs b/library/core/src/num/imp/dec2flt/slow.rs index 089b12f5be22..f1b2525cf38e 100644 --- a/library/core/src/num/imp/dec2flt/slow.rs +++ b/library/core/src/num/imp/dec2flt/slow.rs @@ -2,9 +2,8 @@ use dec2flt::common::BiasedFp; use dec2flt::decimal_seq::{DecimalSeq, parse_decimal_seq}; -use dec2flt::float::RawFloat; -use crate::num::imp::dec2flt; +use crate::num::imp::{Float, dec2flt}; /// Parse the significant digits and biased, binary exponent of a float. /// @@ -25,7 +24,7 @@ /// /// The algorithms described here are based on "Processing Long Numbers Quickly", /// available here: . -pub(crate) fn parse_long_mantissa(s: &[u8]) -> BiasedFp { +pub(crate) fn parse_long_mantissa(s: &[u8]) -> BiasedFp { const MAX_SHIFT: usize = 60; const NUM_POWERS: usize = 19; const POWERS: [u8; 19] = diff --git a/library/core/src/num/imp/flt2dec/decoder.rs b/library/core/src/num/imp/flt2dec/decoder.rs index 3d6ad6608efe..5ccc91f4c9fa 100644 --- a/library/core/src/num/imp/flt2dec/decoder.rs +++ b/library/core/src/num/imp/flt2dec/decoder.rs @@ -1,7 +1,7 @@ //! Decodes a floating-point value into individual parts and error ranges. use crate::num::FpCategory; -use crate::num::imp::dec2flt::float::RawFloat; +use crate::num::imp::FloatExt; /// Decoded unsigned finite value, such that: /// @@ -40,7 +40,7 @@ pub enum FullDecoded { } /// A floating point type which can be `decode`d. -pub trait DecodableFloat: RawFloat + Copy { +pub trait DecodableFloat: FloatExt + Copy { /// The minimum positive normalized value. fn min_pos_norm_value() -> Self; } diff --git a/library/core/src/num/imp/mod.rs b/library/core/src/num/imp/mod.rs index d35409f91bde..6fccfd1c238e 100644 --- a/library/core/src/num/imp/mod.rs +++ b/library/core/src/num/imp/mod.rs @@ -16,3 +16,6 @@ pub(crate) mod int_sqrt; pub(crate) mod libm; pub(crate) mod overflow_panic; +mod traits; + +pub use traits::{Float, FloatExt, Int}; diff --git a/library/core/src/num/imp/dec2flt/float.rs b/library/core/src/num/imp/traits.rs similarity index 72% rename from library/core/src/num/imp/dec2flt/float.rs rename to library/core/src/num/imp/traits.rs index 21aabdc8addb..7b84f7a4a5aa 100644 --- a/library/core/src/num/imp/dec2flt/float.rs +++ b/library/core/src/num/imp/traits.rs @@ -1,10 +1,14 @@ -//! Helper trait for generic float types. +//! Numeric traits used for internal implementations. -use core::f64; +#![doc(hidden)] +#![unstable( + feature = "num_internals", + reason = "internal routines only exposed for testing", + issue = "none" +)] -use crate::fmt::{Debug, LowerExp}; use crate::num::FpCategory; -use crate::ops::{self, Add, Div, Mul, Neg}; +use crate::{f64, fmt, ops}; /// Lossy `as` casting between two types. pub trait CastInto: Copy { @@ -12,11 +16,11 @@ pub trait CastInto: Copy { } /// Collection of traits that allow us to be generic over integer size. -pub trait Integer: +pub trait Int: Sized + Clone + Copy - + Debug + + fmt::Debug + ops::Shr + ops::Shl + ops::BitAnd @@ -37,7 +41,7 @@ fn cast(self) -> i16 { } } - impl Integer for $ty { + impl Int for $ty { const ZERO: Self = 0; const ONE: Self = 1; } @@ -48,27 +52,22 @@ impl Integer for $ty { int!(u16, u32, u64); /// A helper trait to avoid duplicating basically all the conversion code for IEEE floats. -/// -/// See the parent module's doc comment for why this is necessary. -/// -/// Should **never ever** be implemented for other types or be used outside the `dec2flt` module. #[doc(hidden)] -pub trait RawFloat: +pub trait Float: Sized - + Div - + Neg - + Mul - + Add - + LowerExp + + ops::Div + + ops::Neg + + ops::Mul + + ops::Add + + fmt::Debug + PartialEq + PartialOrd + Default + Clone + Copy - + Debug { /// The unsigned integer with the same size as the float - type Int: Integer + Into; + type Int: Int + Into; /* general constants */ @@ -128,8 +127,6 @@ pub trait RawFloat: const MIN_EXPONENT_ROUND_TO_EVEN: i32; const MAX_EXPONENT_ROUND_TO_EVEN: i32; - /* limits related to Fast pathing */ - /// Largest decimal exponent for a non-infinite value. /// /// This is the max exponent in binary converted to the max exponent in decimal. Allows fast @@ -151,41 +148,19 @@ pub trait RawFloat: /// compile time since intermediates exceed the range of an `f64`. const SMALLEST_POWER_OF_TEN: i32; - /// Maximum exponent for a fast path case, or `⌊(SIG_BITS+1)/log2(5)⌋` - // assuming FLT_EVAL_METHOD = 0 - const MAX_EXPONENT_FAST_PATH: i64 = { - let log2_5 = f64::consts::LOG2_10 - 1.0; - (Self::SIG_TOTAL_BITS as f64 / log2_5) as i64 - }; - - /// Minimum exponent for a fast path case, or `-⌊(SIG_BITS+1)/log2(5)⌋` - const MIN_EXPONENT_FAST_PATH: i64 = -Self::MAX_EXPONENT_FAST_PATH; - - /// Maximum exponent that can be represented for a disguised-fast path case. - /// This is `MAX_EXPONENT_FAST_PATH + ⌊(SIG_BITS+1)/log2(10)⌋` - const MAX_EXPONENT_DISGUISED_FAST_PATH: i64 = - Self::MAX_EXPONENT_FAST_PATH + (Self::SIG_TOTAL_BITS as f64 / f64::consts::LOG2_10) as i64; - - /// Maximum mantissa for the fast-path (`1 << 53` for f64). - const MAX_MANTISSA_FAST_PATH: u64 = 1 << Self::SIG_TOTAL_BITS; - - /// Converts integer into float through an as cast. - /// This is only called in the fast-path algorithm, and therefore - /// will not lose precision, since the value will always have - /// only if the value is <= Self::MAX_MANTISSA_FAST_PATH. - fn from_u64(v: u64) -> Self; - - /// Performs a raw transmutation from an integer. - fn from_u64_bits(v: u64) -> Self; - - /// Gets a small power-of-ten for fast-path multiplication. - fn pow10_fast_path(exponent: usize) -> Self; - /// Returns the category that this number falls into. fn classify(self) -> FpCategory; /// Transmute to the integer representation fn to_bits(self) -> Self::Int; +} + +/// Items that ideally would be on `Float`, but don't apply to all float types because they +/// rely on the mantissa fitting into a `u64` (which isn't true for `f128`). +#[doc(hidden)] +pub trait FloatExt: Float { + /// Performs a raw transmutation from an integer. + fn from_u64_bits(v: u64) -> Self; /// Returns the mantissa, exponent and sign as integers. /// @@ -219,7 +194,7 @@ const fn pow2_to_pow10(a: i64) -> i64 { } #[cfg(target_has_reliable_f16)] -impl RawFloat for f16 { +impl Float for f16 { type Int = u16; const INFINITY: Self = Self::INFINITY; @@ -236,23 +211,6 @@ impl RawFloat for f16 { const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 5; const SMALLEST_POWER_OF_TEN: i32 = -27; - #[inline] - fn from_u64(v: u64) -> Self { - debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); - v as _ - } - - #[inline] - fn from_u64_bits(v: u64) -> Self { - Self::from_bits((v & 0xFFFF) as u16) - } - - fn pow10_fast_path(exponent: usize) -> Self { - #[allow(clippy::use_self)] - const TABLE: [f16; 8] = [1e0, 1e1, 1e2, 1e3, 1e4, 0.0, 0.0, 0.]; - TABLE[exponent & 7] - } - fn to_bits(self) -> Self::Int { self.to_bits() } @@ -262,7 +220,15 @@ fn classify(self) -> FpCategory { } } -impl RawFloat for f32 { +#[cfg(target_has_reliable_f16)] +impl FloatExt for f16 { + #[inline] + fn from_u64_bits(v: u64) -> Self { + Self::from_bits((v & 0xFFFF) as u16) + } +} + +impl Float for f32 { type Int = u32; const INFINITY: Self = f32::INFINITY; @@ -279,24 +245,6 @@ impl RawFloat for f32 { const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 10; const SMALLEST_POWER_OF_TEN: i32 = -65; - #[inline] - fn from_u64(v: u64) -> Self { - debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); - v as _ - } - - #[inline] - fn from_u64_bits(v: u64) -> Self { - f32::from_bits((v & 0xFFFFFFFF) as u32) - } - - fn pow10_fast_path(exponent: usize) -> Self { - #[allow(clippy::use_self)] - const TABLE: [f32; 16] = - [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 0., 0., 0., 0., 0.]; - TABLE[exponent & 15] - } - fn to_bits(self) -> Self::Int { self.to_bits() } @@ -306,7 +254,14 @@ fn classify(self) -> FpCategory { } } -impl RawFloat for f64 { +impl FloatExt for f32 { + #[inline] + fn from_u64_bits(v: u64) -> Self { + f32::from_bits((v & 0xFFFFFFFF) as u32) + } +} + +impl Float for f64 { type Int = u64; const INFINITY: Self = Self::INFINITY; @@ -323,25 +278,6 @@ impl RawFloat for f64 { const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 23; const SMALLEST_POWER_OF_TEN: i32 = -342; - #[inline] - fn from_u64(v: u64) -> Self { - debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH); - v as _ - } - - #[inline] - fn from_u64_bits(v: u64) -> Self { - f64::from_bits(v) - } - - fn pow10_fast_path(exponent: usize) -> Self { - const TABLE: [f64; 32] = [ - 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, - 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 0., 0., 0., 0., 0., 0., 0., 0., 0., - ]; - TABLE[exponent & 31] - } - fn to_bits(self) -> Self::Int { self.to_bits() } @@ -350,3 +286,10 @@ fn classify(self) -> FpCategory { self.classify() } } + +impl FloatExt for f64 { + #[inline] + fn from_u64_bits(v: u64) -> Self { + f64::from_bits(v) + } +} diff --git a/library/core/src/ops/arith.rs b/library/core/src/ops/arith.rs index 6c6479c99845..aec52424af3c 100644 --- a/library/core/src/ops/arith.rs +++ b/library/core/src/ops/arith.rs @@ -716,6 +716,7 @@ impl const Neg for $t { type Output = $t; #[inline] + #[track_caller] #[rustc_inherit_overflow_checks] fn neg(self) -> $t { -self } } diff --git a/library/core/src/ops/bit.rs b/library/core/src/ops/bit.rs index 0cd61b073738..e347a20d2d86 100644 --- a/library/core/src/ops/bit.rs +++ b/library/core/src/ops/bit.rs @@ -484,6 +484,7 @@ impl const Shl<$f> for $t { type Output = $t; #[inline] + #[track_caller] #[rustc_inherit_overflow_checks] fn shl(self, other: $f) -> $t { self << other @@ -606,6 +607,7 @@ impl const Shr<$f> for $t { type Output = $t; #[inline] + #[track_caller] #[rustc_inherit_overflow_checks] fn shr(self, other: $f) -> $t { self >> other @@ -958,6 +960,7 @@ macro_rules! shl_assign_impl { #[rustc_const_unstable(feature = "const_ops", issue = "143802")] impl const ShlAssign<$f> for $t { #[inline] + #[track_caller] #[rustc_inherit_overflow_checks] fn shl_assign(&mut self, other: $f) { *self <<= other @@ -1044,6 +1047,7 @@ macro_rules! shr_assign_impl { #[rustc_const_unstable(feature = "const_ops", issue = "143802")] impl const ShrAssign<$f> for $t { #[inline] + #[track_caller] #[rustc_inherit_overflow_checks] fn shr_assign(&mut self, other: $f) { *self >>= other diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 72112f8b0113..b0ba07013ed8 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -88,6 +88,7 @@ #![feature(next_index)] #![feature(non_exhaustive_omitted_patterns_lint)] #![feature(nonzero_from_str_radix)] +#![feature(num_internals)] #![feature(numfmt)] #![feature(one_sided_range)] #![feature(panic_internals)] @@ -181,7 +182,6 @@ fn $test() $block mod const_ptr; mod convert; mod ffi; -mod floats; mod fmt; mod future; mod hash; diff --git a/library/coretests/tests/num/dec2flt/float.rs b/library/coretests/tests/num/dec2flt/float.rs index 25e12435b472..0713c5c651fb 100644 --- a/library/coretests/tests/num/dec2flt/float.rs +++ b/library/coretests/tests/num/dec2flt/float.rs @@ -1,4 +1,5 @@ -use core::num::imp::dec2flt::float::RawFloat; +use core::num::imp::dec2flt::Lemire; +use core::num::imp::{Float, FloatExt}; use crate::num::{ldexp_f32, ldexp_f64}; @@ -56,57 +57,60 @@ fn test_f64_integer_decode() { #[test] #[cfg(target_has_reliable_f16)] fn test_f16_consts() { - assert_eq!(::INFINITY, f16::INFINITY); - assert_eq!(::NEG_INFINITY, -f16::INFINITY); - assert_eq!(::NAN.to_bits(), f16::NAN.to_bits()); - assert_eq!(::NEG_NAN.to_bits(), (-f16::NAN).to_bits()); - assert_eq!(::SIG_BITS, 10); - assert_eq!(::MIN_EXPONENT_ROUND_TO_EVEN, -22); - assert_eq!(::MAX_EXPONENT_ROUND_TO_EVEN, 5); - assert_eq!(::MIN_EXPONENT_FAST_PATH, -4); - assert_eq!(::MAX_EXPONENT_FAST_PATH, 4); - assert_eq!(::MAX_EXPONENT_DISGUISED_FAST_PATH, 7); - assert_eq!(::EXP_MIN, -14); - assert_eq!(::EXP_SAT, 0x1f); - assert_eq!(::SMALLEST_POWER_OF_TEN, -27); - assert_eq!(::LARGEST_POWER_OF_TEN, 4); - assert_eq!(::MAX_MANTISSA_FAST_PATH, 2048); + assert_eq!(::INFINITY, f16::INFINITY); + assert_eq!(::NEG_INFINITY, -f16::INFINITY); + assert_eq!(::NAN.to_bits(), f16::NAN.to_bits()); + assert_eq!(::NEG_NAN.to_bits(), (-f16::NAN).to_bits()); + assert_eq!(::SIG_BITS, 10); + assert_eq!(::MIN_EXPONENT_ROUND_TO_EVEN, -22); + assert_eq!(::MAX_EXPONENT_ROUND_TO_EVEN, 5); + assert_eq!(::EXP_MIN, -14); + assert_eq!(::EXP_SAT, 0x1f); + assert_eq!(::SMALLEST_POWER_OF_TEN, -27); + assert_eq!(::LARGEST_POWER_OF_TEN, 4); + + assert_eq!(::MIN_EXPONENT_FAST_PATH, -4); + assert_eq!(::MAX_EXPONENT_FAST_PATH, 4); + assert_eq!(::MAX_EXPONENT_DISGUISED_FAST_PATH, 7); + assert_eq!(::MAX_MANTISSA_FAST_PATH, 2048); } #[test] fn test_f32_consts() { - assert_eq!(::INFINITY, f32::INFINITY); - assert_eq!(::NEG_INFINITY, -f32::INFINITY); - assert_eq!(::NAN.to_bits(), f32::NAN.to_bits()); - assert_eq!(::NEG_NAN.to_bits(), (-f32::NAN).to_bits()); - assert_eq!(::SIG_BITS, 23); - assert_eq!(::MIN_EXPONENT_ROUND_TO_EVEN, -17); - assert_eq!(::MAX_EXPONENT_ROUND_TO_EVEN, 10); - assert_eq!(::MIN_EXPONENT_FAST_PATH, -10); - assert_eq!(::MAX_EXPONENT_FAST_PATH, 10); - assert_eq!(::MAX_EXPONENT_DISGUISED_FAST_PATH, 17); - assert_eq!(::EXP_MIN, -126); - assert_eq!(::EXP_SAT, 0xff); - assert_eq!(::SMALLEST_POWER_OF_TEN, -65); - assert_eq!(::LARGEST_POWER_OF_TEN, 38); - assert_eq!(::MAX_MANTISSA_FAST_PATH, 16777216); + assert_eq!(::INFINITY, f32::INFINITY); + assert_eq!(::NEG_INFINITY, -f32::INFINITY); + assert_eq!(::NAN.to_bits(), f32::NAN.to_bits()); + assert_eq!(::NEG_NAN.to_bits(), (-f32::NAN).to_bits()); + assert_eq!(::SIG_BITS, 23); + assert_eq!(::MIN_EXPONENT_ROUND_TO_EVEN, -17); + assert_eq!(::MAX_EXPONENT_ROUND_TO_EVEN, 10); + assert_eq!(::EXP_MIN, -126); + assert_eq!(::EXP_SAT, 0xff); + assert_eq!(::SMALLEST_POWER_OF_TEN, -65); + assert_eq!(::LARGEST_POWER_OF_TEN, 38); + + assert_eq!(::MIN_EXPONENT_FAST_PATH, -10); + assert_eq!(::MAX_EXPONENT_FAST_PATH, 10); + assert_eq!(::MAX_EXPONENT_DISGUISED_FAST_PATH, 17); + assert_eq!(::MAX_MANTISSA_FAST_PATH, 16777216); } #[test] fn test_f64_consts() { - assert_eq!(::INFINITY, f64::INFINITY); - assert_eq!(::NEG_INFINITY, -f64::INFINITY); - assert_eq!(::NAN.to_bits(), f64::NAN.to_bits()); - assert_eq!(::NEG_NAN.to_bits(), (-f64::NAN).to_bits()); - assert_eq!(::SIG_BITS, 52); - assert_eq!(::MIN_EXPONENT_ROUND_TO_EVEN, -4); - assert_eq!(::MAX_EXPONENT_ROUND_TO_EVEN, 23); - assert_eq!(::MIN_EXPONENT_FAST_PATH, -22); - assert_eq!(::MAX_EXPONENT_FAST_PATH, 22); - assert_eq!(::MAX_EXPONENT_DISGUISED_FAST_PATH, 37); - assert_eq!(::EXP_MIN, -1022); - assert_eq!(::EXP_SAT, 0x7ff); - assert_eq!(::SMALLEST_POWER_OF_TEN, -342); - assert_eq!(::LARGEST_POWER_OF_TEN, 308); - assert_eq!(::MAX_MANTISSA_FAST_PATH, 9007199254740992); + assert_eq!(::INFINITY, f64::INFINITY); + assert_eq!(::NEG_INFINITY, -f64::INFINITY); + assert_eq!(::NAN.to_bits(), f64::NAN.to_bits()); + assert_eq!(::NEG_NAN.to_bits(), (-f64::NAN).to_bits()); + assert_eq!(::SIG_BITS, 52); + assert_eq!(::MIN_EXPONENT_ROUND_TO_EVEN, -4); + assert_eq!(::MAX_EXPONENT_ROUND_TO_EVEN, 23); + assert_eq!(::EXP_MIN, -1022); + assert_eq!(::EXP_SAT, 0x7ff); + assert_eq!(::SMALLEST_POWER_OF_TEN, -342); + assert_eq!(::LARGEST_POWER_OF_TEN, 308); + + assert_eq!(::MIN_EXPONENT_FAST_PATH, -22); + assert_eq!(::MAX_EXPONENT_FAST_PATH, 22); + assert_eq!(::MAX_EXPONENT_DISGUISED_FAST_PATH, 37); + assert_eq!(::MAX_MANTISSA_FAST_PATH, 9007199254740992); } diff --git a/library/coretests/tests/num/dec2flt/lemire.rs b/library/coretests/tests/num/dec2flt/lemire.rs index e4ce533ba44d..e5a7ae346f42 100644 --- a/library/coretests/tests/num/dec2flt/lemire.rs +++ b/library/coretests/tests/num/dec2flt/lemire.rs @@ -1,6 +1,5 @@ -use core::num::imp::dec2flt; +use core::num::imp::{Float, dec2flt}; -use dec2flt::float::RawFloat; use dec2flt::lemire::compute_float; #[cfg(target_has_reliable_f16)] diff --git a/library/coretests/tests/num/float_ieee754_flt2dec_dec2flt.rs b/library/coretests/tests/num/float_ieee754_flt2dec_dec2flt.rs new file mode 100644 index 000000000000..2a82ae326c22 --- /dev/null +++ b/library/coretests/tests/num/float_ieee754_flt2dec_dec2flt.rs @@ -0,0 +1,166 @@ +//! IEEE 754 floating point compliance tests +//! +//! To understand IEEE 754's requirements on a programming language, one must understand that the +//! requirements of IEEE 754 rest on the total programming environment, and not entirely on any +//! one component. That means the hardware, language, and even libraries are considered part of +//! conforming floating point support in a programming environment. +//! +//! A programming language's duty, accordingly, is: +//! 1. offer access to the hardware where the hardware offers support +//! 2. provide operations that fulfill the remaining requirements of the standard +//! 3. provide the ability to write additional software that can fulfill those requirements +//! +//! This may be fulfilled in any combination that the language sees fit. However, to claim that +//! a language supports IEEE 754 is to suggest that it has fulfilled requirements 1 and 2, without +//! deferring minimum requirements to libraries. This is because support for IEEE 754 is defined +//! as complete support for at least one specified floating point type as an "arithmetic" and +//! "interchange" format, plus specified type conversions to "external character sequences" and +//! integer types. +//! +//! For our purposes, +//! "interchange format" => f16, f32, f64, f128 +//! "arithmetic format" => f16, f32, f64, f128, and any "soft floats" +//! "external character sequence" => str from any float +//! "integer format" => {i,u}{8,16,32,64,128} +//! +//! None of these tests are against Rust's own implementation. They are only tests against the +//! standard. That is why they accept wildly diverse inputs or may seem to duplicate other tests. +//! Please consider this carefully when adding, removing, or reorganizing these tests. They are +//! here so that it is clear what tests are required by the standard and what can be changed. + +use core::fmt; +use core::str::FromStr; + +use crate::num::{assert_biteq, float_test}; + +/// ToString uses the default fmt::Display impl without special concerns, and bypasses other parts +/// of the formatting infrastructure, which makes it ideal for testing here. +#[track_caller] +fn string_roundtrip(x: T) -> T +where + T: FromStr + fmt::Display, +{ + x.to_string().parse::().unwrap() +} + +// FIXME(f128): Tests are disabled while we don't have parsing / printing + +// We must preserve signs on all numbers. That includes zero. +// -0 and 0 are == normally, so test bit equality. +float_test! { + name: preserve_signed_zero, + attrs: { + const: #[cfg(false)], + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(false)], + }, + test { + let neg0 = flt(-0.0); + let pos0 = flt(0.0); + assert_biteq!(neg0, string_roundtrip(neg0)); + assert_biteq!(pos0, string_roundtrip(pos0)); + assert_ne!(neg0.to_bits(), pos0.to_bits()); + } +} + +float_test! { + name: preserve_signed_infinity, + attrs: { + const: #[cfg(false)], + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(false)], + }, + test { + let neg_inf = Float::NEG_INFINITY; + let pos_inf = Float::INFINITY; + assert_biteq!(neg_inf, string_roundtrip(neg_inf)); + assert_biteq!(pos_inf, string_roundtrip(pos_inf)); + assert_ne!(neg_inf.to_bits(), pos_inf.to_bits()); + } +} + +float_test! { + name: infinity_to_str, + attrs: { + const: #[cfg(false)], + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(false)], + }, + test { + assert!( + match Float::INFINITY.to_string().to_lowercase().as_str() { + "+infinity" | "infinity" => true, + "+inf" | "inf" => true, + _ => false, + }, + "Infinity must write to a string as some casing of inf or infinity, with an optional +." + ); + assert!( + match Float::NEG_INFINITY.to_string().to_lowercase().as_str() { + "-infinity" | "-inf" => true, + _ => false, + }, + "Negative Infinity must write to a string as some casing of -inf or -infinity" + ); + } +} + +float_test! { + name: nan_to_str, + attrs: { + const: #[cfg(false)], + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(false)], + }, + test { + assert!( + match Float::NAN.to_string().to_lowercase().as_str() { + "nan" | "+nan" | "-nan" => true, + _ => false, + }, + "NaNs must write to a string as some casing of nan." + ) + } +} + +float_test! { + name: infinity_from_str, + attrs: { + const: #[cfg(false)], + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(false)], + }, + test { + // "+"?("inf"|"infinity") in any case => Infinity + assert_biteq!(Float::INFINITY, Float::from_str("infinity").unwrap()); + assert_biteq!(Float::INFINITY, Float::from_str("inf").unwrap()); + assert_biteq!(Float::INFINITY, Float::from_str("+infinity").unwrap()); + assert_biteq!(Float::INFINITY, Float::from_str("+inf").unwrap()); + // yes! this means you are weLcOmE tO mY iNfInItElY tWiStEd MiNd + assert_biteq!(Float::INFINITY, Float::from_str("+iNfInItY").unwrap()); + + // "-inf"|"-infinity" in any case => Negative Infinity + assert_biteq!(Float::NEG_INFINITY, Float::from_str("-infinity").unwrap()); + assert_biteq!(Float::NEG_INFINITY, Float::from_str("-inf").unwrap()); + assert_biteq!(Float::NEG_INFINITY, Float::from_str("-INF").unwrap()); + assert_biteq!(Float::NEG_INFINITY, Float::from_str("-INFinity").unwrap()); + + } +} + +float_test! { + name: qnan_from_str, + attrs: { + const: #[cfg(false)], + f16: #[cfg(any(miri, target_has_reliable_f16))], + f128: #[cfg(false)], + }, + test { + // ("+"|"-"")?"s"?"nan" in any case => qNaN + assert!("nan".parse::().unwrap().is_nan()); + assert!("-nan".parse::().unwrap().is_nan()); + assert!("+nan".parse::().unwrap().is_nan()); + assert!("+NAN".parse::().unwrap().is_nan()); + assert!("-NaN".parse::().unwrap().is_nan()); + } +} diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/num/floats.rs similarity index 99% rename from library/coretests/tests/floats/mod.rs rename to library/coretests/tests/num/floats.rs index c9bbff266d90..d87f85094450 100644 --- a/library/coretests/tests/floats/mod.rs +++ b/library/coretests/tests/num/floats.rs @@ -2,7 +2,7 @@ use std::num::FpCategory as Fp; use std::ops::{Add, Div, Mul, Rem, Sub}; -trait TestableFloat: Sized { +pub(crate) trait TestableFloat: Sized { const BITS: u32; /// Unsigned int with the same size, for converting to/from bits. type Int; @@ -224,7 +224,7 @@ impl TestableFloat for f128 { } /// Determine the tolerance for values of the argument type. -const fn lim_for_ty(_x: T) -> T { +pub(crate) const fn lim_for_ty(_x: T) -> T { T::APPROX } @@ -232,7 +232,9 @@ const fn lim_for_ty(_x: T) -> T { /// Verify that floats are within a tolerance of each other. macro_rules! assert_approx_eq { - ($a:expr, $b:expr $(,)?) => {{ assert_approx_eq!($a, $b, $crate::floats::lim_for_ty($a)) }}; + ($a:expr, $b:expr $(,)?) => {{ + assert_approx_eq!($a, $b, $crate::num::floats::lim_for_ty($a)) + }}; ($a:expr, $b:expr, $lim:expr) => {{ let (a, b) = (&$a, &$b); let diff = (*a - *b).abs(); @@ -270,8 +272,8 @@ macro_rules! assert_biteq { // We rely on the `Float` type being brought in scope by the macros below. l: Float = l, r: Float = r, - lb: ::Int = l.to_bits(), - rb: ::Int = r.to_bits(), + lb: ::Int = l.to_bits(), + rb: ::Int = r.to_bits(), width: usize = ((bits / 4) + 2) as usize, ); }}; @@ -312,6 +314,7 @@ macro_rules! float_test { test $test:block ) => { mod $name { + #[allow(unused_imports)] use super::*; #[test] @@ -360,6 +363,7 @@ const fn flt (x: Float) -> Float { x } $( $( #[$const_meta] )+ )? mod const_ { + #[allow(unused_imports)] use super::*; #[test] @@ -410,6 +414,9 @@ const fn flt (x: Float) -> Float { x } }; } +pub(crate) use assert_biteq; +pub(crate) use float_test; + float_test! { name: num, attrs: { diff --git a/library/coretests/tests/num/ieee754.rs b/library/coretests/tests/num/ieee754.rs deleted file mode 100644 index b0f6a7545aa9..000000000000 --- a/library/coretests/tests/num/ieee754.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! IEEE 754 floating point compliance tests -//! -//! To understand IEEE 754's requirements on a programming language, one must understand that the -//! requirements of IEEE 754 rest on the total programming environment, and not entirely on any -//! one component. That means the hardware, language, and even libraries are considered part of -//! conforming floating point support in a programming environment. -//! -//! A programming language's duty, accordingly, is: -//! 1. offer access to the hardware where the hardware offers support -//! 2. provide operations that fulfill the remaining requirements of the standard -//! 3. provide the ability to write additional software that can fulfill those requirements -//! -//! This may be fulfilled in any combination that the language sees fit. However, to claim that -//! a language supports IEEE 754 is to suggest that it has fulfilled requirements 1 and 2, without -//! deferring minimum requirements to libraries. This is because support for IEEE 754 is defined -//! as complete support for at least one specified floating point type as an "arithmetic" and -//! "interchange" format, plus specified type conversions to "external character sequences" and -//! integer types. -//! -//! For our purposes, -//! "interchange format" => f32, f64 -//! "arithmetic format" => f32, f64, and any "soft floats" -//! "external character sequence" => str from any float -//! "integer format" => {i,u}{8,16,32,64,128} -//! -//! None of these tests are against Rust's own implementation. They are only tests against the -//! standard. That is why they accept wildly diverse inputs or may seem to duplicate other tests. -//! Please consider this carefully when adding, removing, or reorganizing these tests. They are -//! here so that it is clear what tests are required by the standard and what can be changed. - -use ::core::str::FromStr; - -// IEEE 754 for many tests is applied to specific bit patterns. -// These generally are not applicable to NaN, however. -macro_rules! assert_biteq { - ($lhs:expr, $rhs:expr) => { - assert_eq!($lhs.to_bits(), $rhs.to_bits()) - }; -} - -// ToString uses the default fmt::Display impl without special concerns, and bypasses other parts -// of the formatting infrastructure, which makes it ideal for testing here. -macro_rules! roundtrip { - ($f:expr => $t:ty) => { - ($f).to_string().parse::<$t>().unwrap() - }; -} - -macro_rules! assert_floats_roundtrip { - ($f:ident) => { - assert_biteq!(f32::$f, roundtrip!(f32::$f => f32)); - assert_biteq!(f64::$f, roundtrip!(f64::$f => f64)); - }; - ($f:expr) => { - assert_biteq!($f as f32, roundtrip!($f => f32)); - assert_biteq!($f as f64, roundtrip!($f => f64)); - } -} - -macro_rules! assert_floats_bitne { - ($lhs:ident, $rhs:ident) => { - assert_ne!(f32::$lhs.to_bits(), f32::$rhs.to_bits()); - assert_ne!(f64::$lhs.to_bits(), f64::$rhs.to_bits()); - }; - ($lhs:expr, $rhs:expr) => { - assert_ne!(f32::to_bits($lhs), f32::to_bits($rhs)); - assert_ne!(f64::to_bits($lhs), f64::to_bits($rhs)); - }; -} - -// We must preserve signs on all numbers. That includes zero. -// -0 and 0 are == normally, so test bit equality. -#[test] -fn preserve_signed_zero() { - assert_floats_roundtrip!(-0.0); - assert_floats_roundtrip!(0.0); - assert_floats_bitne!(0.0, -0.0); -} - -#[test] -fn preserve_signed_infinity() { - assert_floats_roundtrip!(INFINITY); - assert_floats_roundtrip!(NEG_INFINITY); - assert_floats_bitne!(INFINITY, NEG_INFINITY); -} - -#[test] -fn infinity_to_str() { - assert!(match f32::INFINITY.to_string().to_lowercase().as_str() { - "+infinity" | "infinity" => true, - "+inf" | "inf" => true, - _ => false, - }); - assert!( - match f64::INFINITY.to_string().to_lowercase().as_str() { - "+infinity" | "infinity" => true, - "+inf" | "inf" => true, - _ => false, - }, - "Infinity must write to a string as some casing of inf or infinity, with an optional +." - ); -} - -#[test] -fn neg_infinity_to_str() { - assert!(match f32::NEG_INFINITY.to_string().to_lowercase().as_str() { - "-infinity" | "-inf" => true, - _ => false, - }); - assert!( - match f64::NEG_INFINITY.to_string().to_lowercase().as_str() { - "-infinity" | "-inf" => true, - _ => false, - }, - "Negative Infinity must write to a string as some casing of -inf or -infinity" - ) -} - -#[test] -fn nan_to_str() { - assert!( - match f32::NAN.to_string().to_lowercase().as_str() { - "nan" | "+nan" | "-nan" => true, - _ => false, - }, - "NaNs must write to a string as some casing of nan." - ) -} - -// "+"?("inf"|"infinity") in any case => Infinity -#[test] -fn infinity_from_str() { - assert_biteq!(f32::INFINITY, f32::from_str("infinity").unwrap()); - assert_biteq!(f32::INFINITY, f32::from_str("inf").unwrap()); - assert_biteq!(f32::INFINITY, f32::from_str("+infinity").unwrap()); - assert_biteq!(f32::INFINITY, f32::from_str("+inf").unwrap()); - // yes! this means you are weLcOmE tO mY iNfInItElY tWiStEd MiNd - assert_biteq!(f32::INFINITY, f32::from_str("+iNfInItY").unwrap()); -} - -// "-inf"|"-infinity" in any case => Negative Infinity -#[test] -fn neg_infinity_from_str() { - assert_biteq!(f32::NEG_INFINITY, f32::from_str("-infinity").unwrap()); - assert_biteq!(f32::NEG_INFINITY, f32::from_str("-inf").unwrap()); - assert_biteq!(f32::NEG_INFINITY, f32::from_str("-INF").unwrap()); - assert_biteq!(f32::NEG_INFINITY, f32::from_str("-INFinity").unwrap()); -} - -// ("+"|"-"")?"s"?"nan" in any case => qNaN -#[test] -fn qnan_from_str() { - assert!("nan".parse::().unwrap().is_nan()); - assert!("-nan".parse::().unwrap().is_nan()); - assert!("+nan".parse::().unwrap().is_nan()); - assert!("+NAN".parse::().unwrap().is_nan()); - assert!("-NaN".parse::().unwrap().is_nan()); -} diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs index 73b0e2333fee..e0214c6ae686 100644 --- a/library/coretests/tests/num/mod.rs +++ b/library/coretests/tests/num/mod.rs @@ -25,9 +25,10 @@ mod carryless_mul; mod const_from; mod dec2flt; +mod float_ieee754_flt2dec_dec2flt; mod float_iter_sum_identity; +mod floats; mod flt2dec; -mod ieee754; mod int_log; mod int_sqrt; mod midpoint; @@ -36,6 +37,8 @@ mod ops; mod wrapping; +use floats::{assert_biteq, float_test}; + /// Adds the attribute to all items in the block. macro_rules! cfg_block { ($(#[$attr:meta]{$($it:item)*})*) => {$($( diff --git a/library/std/build.rs b/library/std/build.rs index c0a6e30b3808..5f2e441bf7d8 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -79,4 +79,28 @@ fn main() { println!("cargo:rustc-cfg=backtrace_in_libstd"); println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap()); + + println!("cargo:rustc-check-cfg=cfg(vxworks_lt_25_09)"); + + if target_os == "vxworks" { + match vxworks_version_code() { + Some((major, minor)) if (major, minor) < (25, 9) => { + println!("cargo:rustc-cfg=vxworks_lt_25_09"); + } + _ => {} + } + } +} + +/// Retrieve the VxWorks release version from the environment variable set by the VxWorks build +/// environment, in `(minor, patch)` form. +fn vxworks_version_code() -> Option<(u32, u32)> { + let version = env::var("WIND_RELEASE_ID").ok()?; + + let mut pieces = version.trim().split(['.']); + + let major: u32 = pieces.next().and_then(|x| x.parse().ok()).unwrap_or(0); + let minor: u32 = pieces.next().and_then(|x| x.parse().ok()).unwrap_or(0); + + Some((major, minor)) } diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs index 25b1ece2745b..7bdba3a04416 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/std/src/io/copy/tests.rs @@ -97,16 +97,17 @@ fn copy_specializes_to_vec() { #[test] fn copy_specializes_from_vecdeque() { - let mut source = VecDeque::with_capacity(100 * 1024); - for _ in 0..20 * 1024 { + let num: usize = if cfg!(miri) { 512 } else { 20 * 1024 }; + let mut source = VecDeque::with_capacity(4 * num); + for _ in 0..num { source.push_front(0); } - for _ in 0..20 * 1024 { + for _ in 0..num { source.push_back(0); } let mut sink = WriteObserver { observed_buffer: 0 }; - assert_eq!(40 * 1024u64, io::copy(&mut source, &mut sink).unwrap()); - assert_eq!(20 * 1024, sink.observed_buffer); + assert_eq!(2 * num as u64, io::copy(&mut source, &mut sink).unwrap()); + assert_eq!(num, sink.observed_buffer); } #[test] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 6fcb28edc7d8..c8c8a6c89714 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -275,9 +275,7 @@ #![feature(cfg_sanitizer_cfi)] #![feature(cfg_target_thread_local)] #![feature(cfi_encoding)] -#![feature(const_default)] #![feature(const_trait_impl)] -#![feature(core_float_math)] #![feature(decl_macro)] #![feature(deprecated_suggestion)] #![feature(doc_cfg)] @@ -287,16 +285,11 @@ #![feature(f16)] #![feature(f128)] #![feature(ffi_const)] -#![feature(formatting_options)] -#![feature(funnel_shifts)] #![feature(intra_doc_pointers)] -#![feature(iter_advance_by)] -#![feature(iter_next_chunk)] #![feature(lang_items)] #![feature(link_cfg)] #![feature(linkage)] #![feature(macro_metavar_expr_concat)] -#![feature(maybe_uninit_fill)] #![feature(min_specialization)] #![feature(must_not_suspend)] #![feature(needs_panic_runtime)] @@ -314,7 +307,6 @@ #![feature(try_blocks)] #![feature(try_trait_v2)] #![feature(type_alias_impl_trait)] -#![feature(uint_carryless_mul)] // tidy-alphabetical-end // // Library features (core): @@ -325,6 +317,8 @@ #![feature(char_internals)] #![feature(clone_to_uninit)] #![feature(const_convert)] +#![feature(const_default)] +#![feature(core_float_math)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] #![feature(cstr_display)] @@ -340,13 +334,18 @@ #![feature(float_minimum_maximum)] #![feature(fmt_internals)] #![feature(fn_ptr_trait)] +#![feature(formatting_options)] +#![feature(funnel_shifts)] #![feature(generic_atomic)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(hint_must_use)] #![feature(int_from_ascii)] #![feature(ip)] +#![feature(iter_advance_by)] +#![feature(iter_next_chunk)] #![feature(maybe_uninit_array_assume_init)] +#![feature(maybe_uninit_fill)] #![feature(panic_can_unwind)] #![feature(panic_internals)] #![feature(pin_coerce_unsized_trait)] @@ -364,6 +363,7 @@ #![feature(sync_unsafe_cell)] #![feature(temporary_niche_types)] #![feature(ub_checks)] +#![feature(uint_carryless_mul)] #![feature(used_with_arg)] // tidy-alphabetical-end // diff --git a/library/std/src/os/vxworks/fs.rs b/library/std/src/os/vxworks/fs.rs index b88ed19b067a..a21748ce9fe0 100644 --- a/library/std/src/os/vxworks/fs.rs +++ b/library/std/src/os/vxworks/fs.rs @@ -69,24 +69,54 @@ fn st_rdev(&self) -> u64 { fn st_size(&self) -> u64 { self.as_inner().as_inner().st_size as u64 } + #[cfg(vxworks_lt_25_09)] fn st_atime(&self) -> i64 { self.as_inner().as_inner().st_atime as i64 } + #[cfg(not(vxworks_lt_25_09))] + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atim.tv_sec as i64 + } + #[cfg(vxworks_lt_25_09)] fn st_atime_nsec(&self) -> i64 { 0 } + #[cfg(not(vxworks_lt_25_09))] + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atim.tv_nsec as i64 + } + #[cfg(vxworks_lt_25_09)] fn st_mtime(&self) -> i64 { self.as_inner().as_inner().st_mtime as i64 } + #[cfg(not(vxworks_lt_25_09))] + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtim.tv_sec as i64 + } + #[cfg(vxworks_lt_25_09)] fn st_mtime_nsec(&self) -> i64 { 0 } + #[cfg(not(vxworks_lt_25_09))] + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtim.tv_nsec as i64 + } + #[cfg(vxworks_lt_25_09)] fn st_ctime(&self) -> i64 { self.as_inner().as_inner().st_ctime as i64 } + #[cfg(not(vxworks_lt_25_09))] + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctim.tv_sec as i64 + } + #[cfg(vxworks_lt_25_09)] fn st_ctime_nsec(&self) -> i64 { 0 } + #[cfg(not(vxworks_lt_25_09))] + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctim.tv_nsec as i64 + } fn st_blksize(&self) -> u64 { self.as_inner().as_inner().st_blksize as u64 } diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index 8df81a580f7b..16ae8a88370b 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -623,6 +623,33 @@ pub fn same_channel(&self, other: &Sender) -> bool { _ => false, } } + + /// Returns `true` if the channel is disconnected. + /// + /// Note that a return value of `false` does not guarantee the channel will + /// remain connected. The channel may be disconnected immediately after this method + /// returns, so a subsequent [`Sender::send`] may still fail with [`SendError`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::channel; + /// + /// let (tx, rx) = channel::(); + /// assert!(!tx.is_disconnected()); + /// drop(rx); + /// assert!(tx.is_disconnected()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] + pub fn is_disconnected(&self) -> bool { + match &self.flavor { + SenderFlavor::Array(chan) => chan.is_disconnected(), + SenderFlavor::List(chan) => chan.is_disconnected(), + SenderFlavor::Zero(chan) => chan.is_disconnected(), + } + } } #[unstable(feature = "mpmc_channel", issue = "126840")] @@ -1349,6 +1376,33 @@ pub fn same_channel(&self, other: &Receiver) -> bool { pub fn iter(&self) -> Iter<'_, T> { Iter { rx: self } } + + /// Returns `true` if the channel is disconnected. + /// + /// Note that a return value of `false` does not guarantee the channel will + /// remain connected. The channel may be disconnected immediately after this method + /// returns, so a subsequent [`Receiver::recv`] may still fail with [`RecvError`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpmc_channel)] + /// + /// use std::sync::mpmc::channel; + /// + /// let (tx, rx) = channel::(); + /// assert!(!rx.is_disconnected()); + /// drop(tx); + /// assert!(rx.is_disconnected()); + /// ``` + #[unstable(feature = "mpmc_channel", issue = "126840")] + pub fn is_disconnected(&self) -> bool { + match &self.flavor { + ReceiverFlavor::Array(chan) => chan.is_disconnected(), + ReceiverFlavor::List(chan) => chan.is_disconnected(), + ReceiverFlavor::Zero(chan) => chan.is_disconnected(), + } + } } #[unstable(feature = "mpmc_channel", issue = "126840")] diff --git a/library/std/src/sync/mpmc/zero.rs b/library/std/src/sync/mpmc/zero.rs index f1ecf80fcb9f..c74346250192 100644 --- a/library/std/src/sync/mpmc/zero.rs +++ b/library/std/src/sync/mpmc/zero.rs @@ -316,4 +316,10 @@ pub(crate) fn is_empty(&self) -> bool { pub(crate) fn is_full(&self) -> bool { true } + + /// Returns `true` if the channel is disconnected. + pub(crate) fn is_disconnected(&self) -> bool { + let inner = self.inner.lock().unwrap(); + inner.is_disconnected + } } diff --git a/library/std/src/sync/mpsc.rs b/library/std/src/sync/mpsc.rs index 0ae23f6e13bf..a1c49bb83010 100644 --- a/library/std/src/sync/mpsc.rs +++ b/library/std/src/sync/mpsc.rs @@ -607,6 +607,29 @@ impl Sender { pub fn send(&self, t: T) -> Result<(), SendError> { self.inner.send(t) } + + /// Returns `true` if the channel is disconnected. + /// + /// Note that a return value of `false` does not guarantee the channel will + /// remain connected. The channel may be disconnected immediately after this method + /// returns, so a subsequent [`Sender::send`] may still fail with [`SendError`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpsc_is_disconnected)] + /// + /// use std::sync::mpsc::channel; + /// + /// let (tx, rx) = channel::(); + /// assert!(!tx.is_disconnected()); + /// drop(rx); + /// assert!(tx.is_disconnected()); + /// ``` + #[unstable(feature = "mpsc_is_disconnected", issue = "153668")] + pub fn is_disconnected(&self) -> bool { + self.inner.is_disconnected() + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1038,6 +1061,29 @@ pub fn iter(&self) -> Iter<'_, T> { pub fn try_iter(&self) -> TryIter<'_, T> { TryIter { rx: self } } + + /// Returns `true` if the channel is disconnected. + /// + /// Note that a return value of `false` does not guarantee the channel will + /// remain connected. The channel may be disconnected immediately after this method + /// returns, so a subsequent [`Receiver::recv`] may still fail with [`RecvError`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(mpsc_is_disconnected)] + /// + /// use std::sync::mpsc::channel; + /// + /// let (tx, rx) = channel::(); + /// assert!(!rx.is_disconnected()); + /// drop(tx); + /// assert!(rx.is_disconnected()); + /// ``` + #[unstable(feature = "mpsc_is_disconnected", issue = "153668")] + pub fn is_disconnected(&self) -> bool { + self.inner.is_disconnected() + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 7db474544f04..2a8571871b73 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -634,7 +634,7 @@ pub fn modified(&self) -> io::Result { } #[cfg(any( - target_os = "vxworks", + all(target_os = "vxworks", vxworks_lt_25_09), target_os = "espidf", target_os = "vita", target_os = "rtems", @@ -643,7 +643,12 @@ pub fn modified(&self) -> io::Result { SystemTime::new(self.stat.st_mtime as i64, 0) } - #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))] + #[cfg(any( + target_os = "horizon", + target_os = "hurd", + target_os = "nuttx", + all(target_os = "vxworks", not(vxworks_lt_25_09)) + ))] pub fn modified(&self) -> io::Result { SystemTime::new(self.stat.st_mtim.tv_sec as i64, self.stat.st_mtim.tv_nsec as i64) } @@ -669,7 +674,7 @@ pub fn accessed(&self) -> io::Result { } #[cfg(any( - target_os = "vxworks", + all(target_os = "vxworks", vxworks_lt_25_09), target_os = "espidf", target_os = "vita", target_os = "rtems" @@ -678,7 +683,12 @@ pub fn accessed(&self) -> io::Result { SystemTime::new(self.stat.st_atime as i64, 0) } - #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))] + #[cfg(any( + target_os = "horizon", + target_os = "hurd", + target_os = "nuttx", + all(target_os = "vxworks", not(vxworks_lt_25_09)) + ))] pub fn accessed(&self) -> io::Result { SystemTime::new(self.stat.st_atim.tv_sec as i64, self.stat.st_atim.tv_nsec as i64) } diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index 3f06c9bd1d77..e1d9d80c97b6 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -62,25 +62,7 @@ fn __rust_std_internal_init_fn() -> $t { $init } // by translating it into a `cfg`ed block and recursing. // https://doc.rust-lang.org/reference/conditional-compilation.html#railroad-ConfigurationPredicate - (@align $final_align:ident, cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { - #[cfg(true)] - { - $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*); - } - - $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)? - }, - - (@align $final_align:ident, cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { - #[cfg(false)] - { - $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*); - } - - $($crate::thread::local_impl::thread_local_inner!(@align $final_align, $($attr_rest)+);)? - }, - - (@align $final_align:ident, cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { + (@align $final_align:ident, cfg_attr($cfg_pred:expr, $($cfg_rhs:tt)*) $(, $($attr_rest:tt)+)?) => { #[cfg($cfg_pred)] { $crate::thread::local_impl::thread_local_inner!(@align $final_align, $($cfg_rhs)*); diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 1318d8dc2780..5de001838faf 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -198,41 +198,10 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ); ), - // it's a nested `cfg_attr(true, ...)`; recurse into RHS - ( - [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(true, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; - $($rest:tt)* - ) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; - $($rest)* - ); - ), - - // it's a nested `cfg_attr(false, ...)`; recurse into RHS - ( - [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr(false, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; - $($rest:tt)* - ) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - @processing_cfg_attr { pred: ($($predicate)*), rhs: [$($($attr_rhs)*)?] }; - $($rest)* - ); - ), - - // it's a nested `cfg_attr(..., ...)`; recurse into RHS ( [$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; - @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr($cfg_lhs:meta, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; + @processing_cfg_attr { pred: ($($predicate:tt)*), rhs: [cfg_attr($cfg_lhs:expr, $($cfg_rhs:tt)*) $(, $($attr_rhs:tt)*)?] }; $($rest:tt)* ) => ( $crate::thread::local_impl::thread_local_process_attrs!( @@ -268,28 +237,8 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ); ), - // `cfg_attr(true, ...)` attribute; parse it - ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(true, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: (true), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - $($rest)* - ); - ), - - // `cfg_attr(false, ...)` attribute; parse it - ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr(false, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( - $crate::thread::local_impl::thread_local_process_attrs!( - [] []; - @processing_cfg_attr { pred: (false), rhs: [$($cfg_rhs)*] }; - [$($prev_align_attrs)*] [$($prev_other_attrs)*]; - $($rest)* - ); - ), - // `cfg_attr(..., ...)` attribute; parse it - ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr($cfg_pred:meta, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( + ([$($prev_align_attrs:tt)*] [$($prev_other_attrs:tt)*]; #[cfg_attr($cfg_pred:expr, $($cfg_rhs:tt)*)] $($rest:tt)*) => ( $crate::thread::local_impl::thread_local_process_attrs!( [] []; @processing_cfg_attr { pred: ($cfg_pred), rhs: [$($cfg_rhs)*] }; diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs index f4e6a20ec849..85c23ac18e76 100644 --- a/src/bootstrap/src/core/build_steps/setup.rs +++ b/src/bootstrap/src/core/build_steps/setup.rs @@ -5,14 +5,17 @@ //! allows setting up things that cannot be simply captured inside the bootstrap.toml, in addition to //! leading people away from manually editing most of the bootstrap.toml values. +use std::collections::BTreeMap; use std::env::consts::EXE_SUFFIX; use std::fmt::Write as _; use std::fs::File; use std::io::Write; use std::path::{MAIN_SEPARATOR_STR, Path, PathBuf}; use std::str::FromStr; +use std::sync::LazyLock; use std::{fmt, fs, io}; +use serde_derive::{Deserialize, Serialize}; use sha2::Digest; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step}; @@ -529,7 +532,8 @@ fn install_git_hook_maybe(builder: &Builder<'_>, config: &Config) -> io::Result< } /// Handles editor-specific setup differences -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] enum EditorKind { Emacs, Helix, @@ -538,6 +542,13 @@ enum EditorKind { Zed, } +static PARSED_HASHES: LazyLock>> = LazyLock::new(|| { + const ALL_HASHES: &str = include_str!("setup/hashes.json"); + let mut map: BTreeMap<_, Vec<_>> = serde_json::from_str(ALL_HASHES).unwrap(); + map.insert(EditorKind::Vim, map.get(&EditorKind::VsCode).unwrap().clone()); + map +}); + impl EditorKind { // Used in `./tests.rs`. #[cfg(test)] @@ -588,59 +599,7 @@ fn prompt_user() -> io::Result> { /// New entries should be appended whenever this is updated so we can detect /// outdated vs. user-modified settings files. fn hashes(&self) -> &'static [&'static str] { - match self { - EditorKind::Emacs => &[ - "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0", - "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45", - "b5dd299b93dca3ceeb9b335f929293cb3d4bf4977866fbe7ceeac2a8a9f99088", - "631c837b0e98ae35fd48b0e5f743b1ca60adadf2d0a2b23566ba25df372cf1a9", - "080955765db84bb6cbf178879f489c4e2369397626a6ecb3debedb94a9d0b3ce", - "f501475c6654187091c924ae26187fa5791d74d4a8ab3fb61fbbe4c0275aade1", - "54bc48fe1996177f5eef86d7231b33978e6d8b737cb0a899e622b7e975c95308", - "08d30e455ceec6e01d9bcef8b9449f2ddd14d278ca8627cdad90e02d9f44e938", - ], - EditorKind::Helix => &[ - "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233", - "6736d61409fbebba0933afd2e4c44ff2f97c1cb36cf0299a7f4a7819b8775040", - "f252dcc30ca85a193a699581e5e929d5bd6c19d40d7a7ade5e257a9517a124a5", - "198c195ed0c070d15907b279b8b4ea96198ca71b939f5376454f3d636ab54da5", - "1c43ead340b20792b91d02b08494ee68708e7e09f56b6766629b4b72079208f1", - "eec09a09452682060afd23dd5d3536ccac5615b3cdbf427366446901215fb9f6", - "cb653043852d9d5ff4a5be56407b859ff9928be055ad3f307eb309aad04765e6", - "e28b1930d16d3d8bbdeed7bd4a995613e648b49e08c9b6f5271880f520637fed", - ], - EditorKind::Vim | EditorKind::VsCode => &[ - "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", - "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922", - "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0", - "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541", - "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923", - "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a", - "828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000", - "811fb3b063c739d261fd8590dd30242e117908f5a095d594fa04585daa18ec4d", - "4eecb58a2168b252077369da446c30ed0e658301efe69691979d1ef0443928f4", - "c394386e6133bbf29ffd32c8af0bb3d4aac354cba9ee051f29612aa9350f8f8d", - "e53e9129ca5ee5dcbd6ec8b68c2d87376474eb154992deba3c6d9ab1703e0717", - "f954316090936c7e590c253ca9d524008375882fa13c5b41d7e2547a896ff893", - "701b73751efd7abd6487f2c79348dab698af7ac4427b79fa3d2087c867144b12", - "a61df796c0c007cb6512127330564e49e57d558dec715703916a928b072a1054", - "02a49ac2d31f00ef6e4531c44e00dac51cea895112e480553f1ba060b3942a47", - "0aa4748848de0d1cb7ece92a0123c8897fef6de2f58aff8fda1426f098b7a798", - "e5e357862e5d6d0d9da335e9823c07b8a7dc42bbf18d72cc5206ad1049cd8fcc", - "a68fd5828e75f3e921f265e29ce1e9efa554083c3773fdb4b8e1ab3b2d9dc6cd", - ], - EditorKind::Zed => &[ - "bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c", - "a5380cf5dd9328731aecc5dfb240d16dac46ed272126b9728006151ef42f5909", - "2e96bf0d443852b12f016c8fc9840ab3d0a2b4fe0b0fb3a157e8d74d5e7e0e26", - "4fadd4c87389a601a27db0d3d74a142fa3a2e656ae78982e934dbe24bee32ad6", - "f0bb3d23ab1a49175ab0ef5c4071af95bb03d01d460776cdb716d91333443382", - "5ef83292111d9a8bb63b6afc3abf42d0bc78fe24985f0d2e039e73258b5dab8f", - "74420c13094b530a986b37c4f1d23cb58c0e8e2295f5858ded129fb1574e66f9", - "2d3b592c089b2ad2c528686a1e371af49922edad1c59accd5d5f31612a441568", - "0767a2398ccc253274b184adbb9e018ce931bd0ef45baad06dad19b652c52951", - ], - } + PARSED_HASHES.get(self).unwrap() } fn settings_path(&self, config: &Config) -> PathBuf { diff --git a/src/bootstrap/src/core/build_steps/setup/hashes.json b/src/bootstrap/src/core/build_steps/setup/hashes.json new file mode 100644 index 000000000000..067c8fffe0d5 --- /dev/null +++ b/src/bootstrap/src/core/build_steps/setup/hashes.json @@ -0,0 +1,73 @@ +{ + "emacs": [ + "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0", + "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45", + "b5dd299b93dca3ceeb9b335f929293cb3d4bf4977866fbe7ceeac2a8a9f99088", + "631c837b0e98ae35fd48b0e5f743b1ca60adadf2d0a2b23566ba25df372cf1a9", + "080955765db84bb6cbf178879f489c4e2369397626a6ecb3debedb94a9d0b3ce", + "f501475c6654187091c924ae26187fa5791d74d4a8ab3fb61fbbe4c0275aade1", + "54bc48fe1996177f5eef86d7231b33978e6d8b737cb0a899e622b7e975c95308", + "08d30e455ceec6e01d9bcef8b9449f2ddd14d278ca8627cdad90e02d9f44e938" + ], + "helix": [ + "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233", + "6736d61409fbebba0933afd2e4c44ff2f97c1cb36cf0299a7f4a7819b8775040", + "f252dcc30ca85a193a699581e5e929d5bd6c19d40d7a7ade5e257a9517a124a5", + "198c195ed0c070d15907b279b8b4ea96198ca71b939f5376454f3d636ab54da5", + "1c43ead340b20792b91d02b08494ee68708e7e09f56b6766629b4b72079208f1", + "eec09a09452682060afd23dd5d3536ccac5615b3cdbf427366446901215fb9f6", + "cb653043852d9d5ff4a5be56407b859ff9928be055ad3f307eb309aad04765e6", + "e28b1930d16d3d8bbdeed7bd4a995613e648b49e08c9b6f5271880f520637fed" + ], + "vim": [ + "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", + "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922", + "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0", + "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541", + "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923", + "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a", + "828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000", + "811fb3b063c739d261fd8590dd30242e117908f5a095d594fa04585daa18ec4d", + "4eecb58a2168b252077369da446c30ed0e658301efe69691979d1ef0443928f4", + "c394386e6133bbf29ffd32c8af0bb3d4aac354cba9ee051f29612aa9350f8f8d", + "e53e9129ca5ee5dcbd6ec8b68c2d87376474eb154992deba3c6d9ab1703e0717", + "f954316090936c7e590c253ca9d524008375882fa13c5b41d7e2547a896ff893", + "701b73751efd7abd6487f2c79348dab698af7ac4427b79fa3d2087c867144b12", + "a61df796c0c007cb6512127330564e49e57d558dec715703916a928b072a1054", + "02a49ac2d31f00ef6e4531c44e00dac51cea895112e480553f1ba060b3942a47", + "0aa4748848de0d1cb7ece92a0123c8897fef6de2f58aff8fda1426f098b7a798", + "e5e357862e5d6d0d9da335e9823c07b8a7dc42bbf18d72cc5206ad1049cd8fcc", + "a68fd5828e75f3e921f265e29ce1e9efa554083c3773fdb4b8e1ab3b2d9dc6cd" + ], + "vscode": [ + "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8", + "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922", + "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0", + "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541", + "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923", + "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a", + "828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000", + "811fb3b063c739d261fd8590dd30242e117908f5a095d594fa04585daa18ec4d", + "4eecb58a2168b252077369da446c30ed0e658301efe69691979d1ef0443928f4", + "c394386e6133bbf29ffd32c8af0bb3d4aac354cba9ee051f29612aa9350f8f8d", + "e53e9129ca5ee5dcbd6ec8b68c2d87376474eb154992deba3c6d9ab1703e0717", + "f954316090936c7e590c253ca9d524008375882fa13c5b41d7e2547a896ff893", + "701b73751efd7abd6487f2c79348dab698af7ac4427b79fa3d2087c867144b12", + "a61df796c0c007cb6512127330564e49e57d558dec715703916a928b072a1054", + "02a49ac2d31f00ef6e4531c44e00dac51cea895112e480553f1ba060b3942a47", + "0aa4748848de0d1cb7ece92a0123c8897fef6de2f58aff8fda1426f098b7a798", + "e5e357862e5d6d0d9da335e9823c07b8a7dc42bbf18d72cc5206ad1049cd8fcc", + "a68fd5828e75f3e921f265e29ce1e9efa554083c3773fdb4b8e1ab3b2d9dc6cd" + ], + "zed": [ + "bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c", + "a5380cf5dd9328731aecc5dfb240d16dac46ed272126b9728006151ef42f5909", + "2e96bf0d443852b12f016c8fc9840ab3d0a2b4fe0b0fb3a157e8d74d5e7e0e26", + "4fadd4c87389a601a27db0d3d74a142fa3a2e656ae78982e934dbe24bee32ad6", + "f0bb3d23ab1a49175ab0ef5c4071af95bb03d01d460776cdb716d91333443382", + "5ef83292111d9a8bb63b6afc3abf42d0bc78fe24985f0d2e039e73258b5dab8f", + "74420c13094b530a986b37c4f1d23cb58c0e8e2295f5858ded129fb1574e66f9", + "2d3b592c089b2ad2c528686a1e371af49922edad1c59accd5d5f31612a441568", + "0767a2398ccc253274b184adbb9e018ce931bd0ef45baad06dad19b652c52951" + ] +} diff --git a/src/bootstrap/src/core/build_steps/setup/tests.rs b/src/bootstrap/src/core/build_steps/setup/tests.rs index e8f83ff75e40..57b549286661 100644 --- a/src/bootstrap/src/core/build_steps/setup/tests.rs +++ b/src/bootstrap/src/core/build_steps/setup/tests.rs @@ -1,3 +1,8 @@ +use std::collections::BTreeMap; +use std::fs::File; +use std::io::Write; +use std::path::Path; + use sha2::Digest; use super::EditorKind; @@ -5,16 +10,41 @@ #[test] fn check_matching_settings_hash() { + // Needs to be a btree so we serialize in a deterministic order. + let mut mismatched = BTreeMap::new(); + for editor in EditorKind::ALL { let mut hasher = sha2::Sha256::new(); hasher.update(&editor.settings_template()); - let hash = hex_encode(hasher.finalize().as_slice()); - assert_eq!( - &hash, - editor.hashes().last().unwrap(), - "Update `EditorKind::hashes()` with the new hash of `{}` for `EditorKind::{:?}`", - editor.settings_template(), - editor, - ); + let actual = hex_encode(hasher.finalize().as_slice()); + let expected = *editor.hashes().last().unwrap(); + + if expected != actual { + mismatched.insert(editor, (expected, actual)); + } + } + + if mismatched.is_empty() { + return; + } + + if option_env!("INSTA_UPDATE").is_some_and(|s| s != "0") { + let mut updated = super::PARSED_HASHES.clone(); + for (editor, (_, actual)) in &mismatched { + *updated.get_mut(editor).unwrap().last_mut().unwrap() = actual; + } + let hash_path = + Path::new(env!("CARGO_MANIFEST_DIR")).join("src/core/build_steps/setup/hashes.json"); + let mut hash_file = File::create(hash_path).unwrap(); + serde_json::to_writer_pretty(&mut hash_file, &updated).unwrap(); + hash_file.write_all(b"\n").unwrap(); + } else { + for (editor, (expected, actual)) in &mismatched { + eprintln!("recorded hash did not match actual hash: {expected} != {actual}"); + eprintln!( + "Run `x test --bless -- hash`, or manually update `setup/hashes.json` with the new hash of `{actual}` for `EditorKind::{editor:?}`" + ); + } + panic!("mismatched hashes"); } } diff --git a/src/bootstrap/src/core/config/target_selection.rs b/src/bootstrap/src/core/config/target_selection.rs index 47f6d6f386df..8457607b897d 100644 --- a/src/bootstrap/src/core/config/target_selection.rs +++ b/src/bootstrap/src/core/config/target_selection.rs @@ -142,7 +142,7 @@ impl SplitDebuginfo { pub fn default_for_platform(target: TargetSelection) -> Self { if target.contains("apple") { SplitDebuginfo::Unpacked - } else if target.is_windows() { + } else if target.is_msvc() { SplitDebuginfo::Packed } else { SplitDebuginfo::Off diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index b264c961b659..f9b0d2dbb209 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -7,6 +7,7 @@ //! relevant to command execution in the bootstrap process. This includes settings such as //! dry-run mode, verbosity level, and failure behavior. +use std::backtrace::{Backtrace, BacktraceStatus}; use std::collections::HashMap; use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Formatter}; @@ -930,6 +931,16 @@ enum FailureReason { if stderr.captures() { writeln!(error_message, "\n--- STDERR vvv\n{}", output.stderr().trim()).unwrap(); } + let backtrace = if exec_ctx.verbosity > 1 { + Backtrace::force_capture() + } else if matches!(command.failure_behavior, BehaviorOnFailure::Ignore) { + Backtrace::disabled() + } else { + Backtrace::capture() + }; + if matches!(backtrace.status(), BacktraceStatus::Captured) { + writeln!(error_message, "\n--- BACKTRACE vvv\n{backtrace}").unwrap(); + } match command.failure_behavior { BehaviorOnFailure::DelayFail => { diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-22/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-22/Dockerfile new file mode 100644 index 000000000000..a22e8de90804 --- /dev/null +++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-22/Dockerfile @@ -0,0 +1,66 @@ +FROM ubuntu:26.04 + +ARG DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + bzip2 \ + g++ \ + gcc-multilib \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + llvm-22-tools \ + llvm-22-dev \ + libedit-dev \ + libssl-dev \ + pkg-config \ + zlib1g-dev \ + xz-utils \ + nodejs \ + mingw-w64 \ + # libgccjit dependencies + flex \ + libmpfr-dev \ + libgmp-dev \ + libmpc3 \ + libmpc-dev \ + && rm -rf /var/lib/apt/lists/* + +# Install powershell (universal package) so we can test x.ps1 on Linux +# FIXME: need a "universal" version that supports libicu74, but for now it still works to ignore that dep. +RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \ + dpkg --ignore-depends=libicu72 -i powershell.deb && \ + rm -f powershell.deb + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +# We are disabling CI LLVM since this builder is intentionally using a host +# LLVM, rather than the typical src/llvm-project LLVM. +ENV NO_DOWNLOAD_CI_LLVM 1 +ENV EXTERNAL_LLVM 1 + +# Using llvm-link-shared due to libffi issues -- see #34486 +ENV RUST_CONFIGURE_ARGS \ + --build=x86_64-unknown-linux-gnu \ + --llvm-root=/usr/lib/llvm-22 \ + --enable-llvm-link-shared \ + --set rust.randomize-layout=true \ + --set rust.thin-lto-import-instr-limit=10 + +COPY scripts/shared.sh /scripts/ + +COPY scripts/x86_64-gnu-llvm.sh /scripts/ +COPY scripts/x86_64-gnu-llvm2.sh /scripts/ +COPY scripts/x86_64-gnu-llvm3.sh /scripts/ +COPY scripts/stage_2_test_set1.sh /scripts/ +COPY scripts/stage_2_test_set2.sh /scripts/ + +ENV SCRIPT "Must specify DOCKER_SCRIPT for this image" diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index a70e4acf94a3..7c0f1d3f2930 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -406,6 +406,31 @@ auto: DOCKER_SCRIPT: x86_64-gnu-llvm3.sh <<: *job-linux-4c + # The x86_64-gnu-llvm-22 job is split into multiple jobs to run tests in parallel. + # x86_64-gnu-llvm-22-1 skips tests that run in x86_64-gnu-llvm-22-{2,3}. + - name: x86_64-gnu-llvm-22-1 + env: + RUST_BACKTRACE: 1 + IMAGE: x86_64-gnu-llvm-22 + DOCKER_SCRIPT: stage_2_test_set2.sh + <<: *job-linux-4c + + # Skip tests that run in x86_64-gnu-llvm-22-{1,3} + - name: x86_64-gnu-llvm-22-2 + env: + RUST_BACKTRACE: 1 + IMAGE: x86_64-gnu-llvm-22 + DOCKER_SCRIPT: x86_64-gnu-llvm2.sh + <<: *job-linux-4c + + # Skip tests that run in x86_64-gnu-llvm-22-{1,2} + - name: x86_64-gnu-llvm-22-3 + env: + RUST_BACKTRACE: 1 + IMAGE: x86_64-gnu-llvm-22 + DOCKER_SCRIPT: x86_64-gnu-llvm3.sh + <<: *job-linux-4c + - name: x86_64-gnu-nopt <<: *job-linux-4c diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index e95d7777b87c..a51e46f6191a 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -91021ccc790478a1a89c003e7d32b8d155ae6aae +562dee4820c458d823175268e41601d4c060588a diff --git a/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md b/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md index 988284620d5c..014d1a08af78 100644 --- a/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md +++ b/src/doc/rustc-dev-guide/src/queries/incremental-compilation-in-detail.md @@ -418,6 +418,13 @@ deal with all of the above but so far that seemed like more trouble than it woul ## Query modifiers +> FIXME: Make [`rustc_middle::query::modifiers`] the home for query modifier documentation, +> and migrate all other useful modifier docs there after verifying that they are still accurate. + +[`rustc_middle::query::modifiers`]: + https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/modifiers/index.html + + The query system allows for applying [modifiers][mod] to queries. These modifiers affect certain aspects of how the system treats the query with respect to incremental compilation: @@ -437,6 +444,9 @@ respect to incremental compilation: as an optimization because the system can skip recording dependencies in the first place. + - `no_force` - Never "force" the dep nodes for this query, even if the query's + key type is recoverable. + - `no_hash` - Applying `no_hash` to a query tells the system to not compute the fingerprint of the query's result. This has two consequences: @@ -475,13 +485,6 @@ respect to incremental compilation: For example, it makes no sense to store values from upstream crates in the cache because they are already available in the upstream crate's metadata. - - `anon` - This attribute makes the system use "anonymous" dep-nodes for the given query. - An anonymous dep-node is not identified by the corresponding query key. - Instead, its ID is computed from the IDs of its dependencies. - This allows the red-green system to do its change detection even if there is no - query key available for a given dep-node -- something which is needed for - handling trait selection because it is not based on queries. - [mod]: ../query.html#adding-a-new-kind-of-query diff --git a/src/doc/rustc/src/platform-support/vxworks.md b/src/doc/rustc/src/platform-support/vxworks.md index 091c757a2ee3..0cf3822f759e 100644 --- a/src/doc/rustc/src/platform-support/vxworks.md +++ b/src/doc/rustc/src/platform-support/vxworks.md @@ -28,6 +28,12 @@ Target triplets available: The minimum supported version is VxWorks 7. +### Environment + +#### `WIND_RELEASE_ID` + +In VxWorks build environment, the environment variable `WIND_RELEASE_ID` indicates the VxWorks release version used for the build. The `WIND_RELEASE_ID` can be used to conditionally compile features/code or handle version specific behaviour. + ## Building Rust for each target can be cross-compiled with its specific target vsb configuration. Std support is added but not yet fully tested. diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 40dfdb890d66..a1578ab40209 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -79,8 +79,7 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - | clean::ProvidedAssocConstItem(..) | clean::ImplAssocConstItem(..) | clean::RequiredAssocTypeItem(..) - // check for trait impl - | clean::ImplItem(box clean::Impl { trait_: Some(_), .. }) + | clean::ImplItem(_) ) { return false; diff --git a/src/tools/cargo b/src/tools/cargo index cbb9bb8bd0fb..e84cb639edfe 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit cbb9bb8bd0fb272b1be0d63a010701ecb3d1d6d3 +Subproject commit e84cb639edfea2c42efd563b72a9be0cc5de6523 diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 7c798a3c2e5a..45bfb65b2e67 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,7 +6,103 @@ document. ## Unreleased / Beta / In Rust Nightly -[92b4b68...master](https://github.com/rust-lang/rust-clippy/compare/92b4b68...master) +[500e0ff...master](https://github.com/rust-lang/rust-clippy/compare/500e0ff...master) + +## Rust 1.94 + +Current stable, released 2026-03-05 + +[View all 94 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-11-29T21%3A01%3A29Z..2026-01-08T20%3A33%3A22Z+base%3Amaster) + +### New Lints + +* Added [`same_length_and_capacity`] to `pedantic` + [#15656](https://github.com/rust-lang/rust-clippy/pull/15656) +* Added [`manual_ilog2`] to `pedantic` + [#15865](https://github.com/rust-lang/rust-clippy/pull/15865) +* Added [`needless_type_cast`] to `nursery` + [#16139](https://github.com/rust-lang/rust-clippy/pull/16139) +* Added [`ptr_offset_by_literal`] to `pedantic` + [#15606](https://github.com/rust-lang/rust-clippy/pull/15606) +* Added [`decimal_bitwise_operands`] to `pedantic` + [#15215](https://github.com/rust-lang/rust-clippy/pull/15215) + +### Moves and Deprecations + +* Moved [`multiple_bound_locations`] from `suspicious` to `style` + [#16302](https://github.com/rust-lang/rust-clippy/pull/16302) +* Moved [`collapsible_else_if`] from `style` to `pedantic` + [#16211](https://github.com/rust-lang/rust-clippy/pull/16211) +* Moved [`needless_type_cast`] from `pedantic` to `nursery` + [#16246](https://github.com/rust-lang/rust-clippy/pull/16246) + +### Enhancements + +* [`never_loop`] do not consider `return` as preventing the iterator from looping; lint diverging + iterator reduction closures like `for_each` and `fold` + [#16364](https://github.com/rust-lang/rust-clippy/pull/16364) +* [`single_range_in_vec_init`] don't apply the suggestion automatically + [#16365](https://github.com/rust-lang/rust-clippy/pull/16365) +* [`useless_conversion`] refine `.into_iter()` suggestions to stop at final target type + [#16238](https://github.com/rust-lang/rust-clippy/pull/16238) +* Multiple lints fix wrongly unmangled macros + [#16337](https://github.com/rust-lang/rust-clippy/pull/16337) +* [`large_stack_arrays`] do not warn for libtest harness + [#16347](https://github.com/rust-lang/rust-clippy/pull/16347) +* [`derive_ord_xor_partial_ord`] allow `expect` on `impl` block + [#16303](https://github.com/rust-lang/rust-clippy/pull/16303) +* [`match_bool`] restrict to 2 arms + [#16333](https://github.com/rust-lang/rust-clippy/pull/16333) +* [`multiple_inherent_impl`] fix false negatives for generic impl blocks + [#16284](https://github.com/rust-lang/rust-clippy/pull/16284) +* [`unnecessary_fold`] warn about semantics change and lint `Add::add`/`Mul::mul` folds + [#16324](https://github.com/rust-lang/rust-clippy/pull/16324) +* [`transmuting_null`] check const blocks and const integer casts + [#16260](https://github.com/rust-lang/rust-clippy/pull/16260) +* [`needless_pass_by_ref_mut`] preserve user-provided lifetime information + [#16273](https://github.com/rust-lang/rust-clippy/pull/16273) +* [`while_let_on_iterator`] use reborrow for non-`Sized` trait references + [#16100](https://github.com/rust-lang/rust-clippy/pull/16100) +* [`collapsible_else_if`] prevent emitting when arms only `if {..} else {..}` + [#16286](https://github.com/rust-lang/rust-clippy/pull/16286) +* [`multiple_unsafe_ops_per_block`] count only towards innermost unsafe block + [#16117](https://github.com/rust-lang/rust-clippy/pull/16117) +* [`manual_saturating_arithmetic`] lint `x.checked_sub(y).unwrap_or_default()` + [#15845](https://github.com/rust-lang/rust-clippy/pull/15845) +* [`transmute_ptr_to_ref`] handle pointer in struct + [#15948](https://github.com/rust-lang/rust-clippy/pull/15948) +* [`disallowed_methods`] skip compiler-generated code + [#16186](https://github.com/rust-lang/rust-clippy/pull/16186) +* [`missing_enforced_import_renames`] do not enforce for "as _" + [#16352](https://github.com/rust-lang/rust-clippy/pull/16352) + +### False Positive Fixes + +* [`double_parens`] fix FP on macro repetition patterns + [#16301](https://github.com/rust-lang/rust-clippy/pull/16301) +* [`assertions_on_constants`] fix false positive when there is non-constant value in condition expr + [#16297](https://github.com/rust-lang/rust-clippy/pull/16297) +* [`use_self`] fix FP on type in const generics + [#16172](https://github.com/rust-lang/rust-clippy/pull/16172) +* [`set_contains_or_insert`] fix FP when set is mutated before `insert` + [#16009](https://github.com/rust-lang/rust-clippy/pull/16009) +* [`if_then_some_else_none`] fix FP when then block contains `await` + [#16178](https://github.com/rust-lang/rust-clippy/pull/16178) +* [`match_like_matches_macro`] fix FP with guards containing `if let` + [#15876](https://github.com/rust-lang/rust-clippy/pull/15876) +* [`tuple_array_conversions`] fix FP when binded vars are used before conversion + [#16197](https://github.com/rust-lang/rust-clippy/pull/16197) +* [`map_entry`] fix FP when it would cause `MutexGuard` to be held across await + [#16199](https://github.com/rust-lang/rust-clippy/pull/16199) +* [`panicking_unwrap`] fix FP on field access with implicit deref + [#16196](https://github.com/rust-lang/rust-clippy/pull/16196) +* [`large_stack_frames`] fix FP on compiler generated targets + [#15101](https://github.com/rust-lang/rust-clippy/pull/15101) + +### ICE Fixes + +* [`needless_type_cast`] do not ICE on struct constructor + [#16245](https://github.com/rust-lang/rust-clippy/pull/16245) ### New Lints @@ -6705,6 +6801,7 @@ Released 2018-09-13 [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_option_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_option_as_slice [`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison +[`manual_pop_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pop_if [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns [`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid diff --git a/src/tools/clippy/clippy.toml b/src/tools/clippy/clippy.toml index 4aa0a426e512..6f9db8da6c71 100644 --- a/src/tools/clippy/clippy.toml +++ b/src/tools/clippy/clippy.toml @@ -5,11 +5,11 @@ check-inconsistent-struct-field-initializers = true lint-commented-code = true [[disallowed-methods]] -path = "rustc_lint::context::LintContext::lint" +path = "rustc_lint::context::LintContext::opt_span_lint" reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead" [[disallowed-methods]] -path = "rustc_lint::context::LintContext::span_lint" +path = "rustc_lint::context::LintContext::emit_span_lint" reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead" [[disallowed-methods]] diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs index 359bf17c68c5..99709ed2e4f2 100644 --- a/src/tools/clippy/clippy_dev/src/lib.rs +++ b/src/tools/clippy/clippy_dev/src/lib.rs @@ -5,8 +5,7 @@ os_str_slice, os_string_truncate, pattern, - rustc_private, - slice_split_once + rustc_private )] #![warn( trivial_casts, diff --git a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs index b98a20a90ccb..8b45473687e6 100644 --- a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs +++ b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::HasSession; use clippy_utils::sugg::Sugg; use clippy_utils::{higher, is_else_clause, is_in_const_context, span_contains_comment}; use rustc_ast::LitKind; @@ -60,7 +59,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { && !is_in_const_context(cx) { let ty = cx.typeck_results().expr_ty(then); - let mut applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { + let mut applicability = if span_contains_comment(cx, expr.span) { Applicability::MaybeIncorrect } else { Applicability::MachineApplicable diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index 75761de4ae73..151f9c956c6d 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -691,7 +691,7 @@ /// const SIZE: usize = 15; /// let arr: [u8; SIZE] = [0; SIZE]; /// ``` - #[clippy::version = "1.93.0"] + #[clippy::version = "1.94.0"] pub NEEDLESS_TYPE_CAST, nursery, "binding defined with one type but always cast to another" diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index a76027caebc8..3850c55c49f8 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; +use clippy_utils::source::{HasSession, IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; use clippy_utils::{can_use_if_let_chains, span_contains_non_whitespace, sym, tokenize_with_text}; use rustc_ast::{BinOpKind, MetaItemInner}; use rustc_errors::Applicability; @@ -9,7 +9,6 @@ use rustc_lexer::TokenKind; use rustc_lint::{LateContext, LateLintPass, Level}; use rustc_session::impl_lint_pass; -use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Span, Symbol}; declare_clippy_lint! { @@ -111,10 +110,8 @@ fn check_collapsible_else_if(&self, cx: &LateContext<'_>, then_span: Span, else_ let up_to_else = then_span.between(else_block.span); let else_before_if = else_.span.shrink_to_lo().with_hi(else_if_cond.span.lo() - BytePos(1)); if self.lint_commented_code - && let Some(else_keyword_span) = - span_extract_keyword(cx.tcx.sess.source_map(), up_to_else, "else") - && let Some(else_if_keyword_span) = - span_extract_keyword(cx.tcx.sess.source_map(), else_before_if, "if") + && let Some(else_keyword_span) = span_extract_keyword(cx, up_to_else, "else") + && let Some(else_if_keyword_span) = span_extract_keyword(cx, else_before_if, "if") { let else_keyword_span = else_keyword_span.with_leading_whitespace(cx).into_span(); let else_open_bracket = else_block.span.split_at(1).0.with_leading_whitespace(cx).into_span(); @@ -139,7 +136,7 @@ fn check_collapsible_else_if(&self, cx: &LateContext<'_>, then_span: Span, else_ } // Peel off any parentheses. - let (_, else_block_span, _) = peel_parens(cx.tcx.sess.source_map(), else_.span); + let (_, else_block_span, _) = peel_parens(cx, else_.span); // Prevent "elseif" // Check that the "else" is followed by whitespace @@ -187,7 +184,7 @@ fn check_collapsible_if_if(&self, cx: &LateContext<'_>, expr: &Expr<'_>, check: .with_leading_whitespace(cx) .into_span() }; - let (paren_start, inner_if_span, paren_end) = peel_parens(cx.tcx.sess.source_map(), inner.span); + let (paren_start, inner_if_span, paren_end) = peel_parens(cx, inner.span); let inner_if = inner_if_span.split_at(2).0; let mut sugg = vec![ // Remove the outer then block `{` @@ -320,33 +317,36 @@ pub(super) fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { } } -fn span_extract_keyword(sm: &SourceMap, span: Span, keyword: &str) -> Option { - let snippet = sm.span_to_snippet(span).ok()?; - tokenize_with_text(&snippet) - .filter(|(t, s, _)| matches!(t, TokenKind::Ident if *s == keyword)) - .map(|(_, _, inner)| { - span.split_at(u32::try_from(inner.start).unwrap()) - .1 - .split_at(u32::try_from(inner.end - inner.start).unwrap()) - .0 - }) - .next() +fn span_extract_keyword(cx: &impl HasSession, span: Span, keyword: &str) -> Option { + span.with_source_text(cx, |snippet| { + tokenize_with_text(snippet) + .filter(|(t, s, _)| matches!(t, TokenKind::Ident if *s == keyword)) + .map(|(_, _, inner)| { + span.split_at(u32::try_from(inner.start).unwrap()) + .1 + .split_at(u32::try_from(inner.end - inner.start).unwrap()) + .0 + }) + .next() + }) + .flatten() } /// Peel the parentheses from an `if` expression, e.g. `((if true {} else {}))`. -pub(super) fn peel_parens(sm: &SourceMap, mut span: Span) -> (Span, Span, Span) { +pub(super) fn peel_parens(cx: &impl HasSession, mut span: Span) -> (Span, Span, Span) { use crate::rustc_span::Pos; let start = span.shrink_to_lo(); let end = span.shrink_to_hi(); - let snippet = sm.span_to_snippet(span).unwrap(); - if let Some((trim_start, _, trim_end)) = peel_parens_str(&snippet) { - let mut data = span.data(); - data.lo = data.lo + BytePos::from_usize(trim_start); - data.hi = data.hi - BytePos::from_usize(trim_end); - span = data.span(); - } + span.with_source_text(cx, |snippet| { + if let Some((trim_start, _, trim_end)) = peel_parens_str(snippet) { + let mut data = span.data(); + data.lo = data.lo + BytePos::from_usize(trim_start); + data.hi = data.hi - BytePos::from_usize(trim_end); + span = data.span(); + } + }); (start.with_hi(span.lo()), span, end.with_lo(span.hi())) } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index e16b194c0cad..441b907eaf2f 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -312,6 +312,7 @@ crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO, crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, crate::manual_option_as_slice::MANUAL_OPTION_AS_SLICE_INFO, + crate::manual_pop_if::MANUAL_POP_IF_INFO, crate::manual_range_patterns::MANUAL_RANGE_PATTERNS_INFO, crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO, crate::manual_retain::MANUAL_RETAIN_INFO, diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/custom_abs.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/custom_abs.rs index 6578cf20e637..f476abae708d 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/custom_abs.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/custom_abs.rs @@ -6,8 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; -use rustc_span::SyntaxContext; -use rustc_span::Spanned; +use rustc_span::{Spanned, SyntaxContext}; use super::SUBOPTIMAL_FLOPS; diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 4d67c158c043..719484b30de8 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -210,6 +210,7 @@ mod manual_main_separator_str; mod manual_non_exhaustive; mod manual_option_as_slice; +mod manual_pop_if; mod manual_range_patterns; mod manual_rem_euclid; mod manual_retain; @@ -863,6 +864,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co Box::new(move |tcx| Box::new(duration_suboptimal_units::DurationSuboptimalUnits::new(tcx, conf))), Box::new(move |_| Box::new(manual_take::ManualTake::new(conf))), Box::new(|_| Box::new(manual_checked_ops::ManualCheckedOps)), + Box::new(move |_| Box::new(manual_pop_if::ManualPopIf::new(conf))), // add late passes here, used by `cargo dev new_lint` ]; store.late_passes.extend(late_lints); diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs index 96de118b5233..2c89afc73974 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet_with_applicability}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability}; use clippy_utils::visitors::is_local_used; use clippy_utils::{higher, is_refutable, peel_blocks_with_stmt, span_contains_comment}; use rustc_errors::Applicability; @@ -50,7 +50,7 @@ pub(super) fn check<'tcx>( format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used"); // Prepare the help message - let mut applicability = if span_contains_comment(cx.sess().source_map(), body.span) { + let mut applicability = if span_contains_comment(cx, body.span) { Applicability::MaybeIncorrect } else { Applicability::MachineApplicable diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs index 5d09f755bcd1..ffc6f7186922 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::{HasSession, snippet_with_applicability}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_slice_like}; use clippy_utils::visitors::is_local_used; use clippy_utils::{higher, peel_blocks_with_stmt, span_contains_comment}; @@ -93,7 +93,7 @@ fn sugg<'tcx>( slice_span: rustc_span::Span, assignval_span: rustc_span::Span, ) { - let mut app = if span_contains_comment(cx.sess().source_map(), body.span) { + let mut app = if span_contains_comment(cx, body.span) { Applicability::MaybeIncorrect // Comments may be informational. } else { Applicability::MachineApplicable diff --git a/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs b/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs index e3886517fdd3..400cfcd18f2c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs +++ b/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs @@ -3,7 +3,6 @@ use clippy_utils::higher::If; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::HasSession as _; use clippy_utils::sugg::Sugg; use clippy_utils::ty::peel_and_count_ty_refs; use clippy_utils::{eq_expr_value, peel_blocks, span_contains_comment, sym}; @@ -76,10 +75,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { (a, b) = (b, a); } let applicability = { - let source_map = cx.sess().source_map(); - if span_contains_comment(source_map, if_expr.then.span) - || span_contains_comment(source_map, r#else.span) - { + if span_contains_comment(cx, if_expr.then.span) || span_contains_comment(cx, r#else.span) { Applicability::MaybeIncorrect } else { Applicability::MachineApplicable diff --git a/src/tools/clippy/clippy_lints/src/manual_assert.rs b/src/tools/clippy/clippy_lints/src/manual_assert.rs index c34e0d33e713..c77133130ba4 100644 --- a/src/tools/clippy/clippy_lints/src/manual_assert.rs +++ b/src/tools/clippy/clippy_lints/src/manual_assert.rs @@ -58,7 +58,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { "only a `panic!` in `if`-then statement", |diag| { let mut applicability = Applicability::MachineApplicable; - let mut comments = span_extract_comment(cx.sess().source_map(), expr.span); + let mut comments = span_extract_comment(cx, expr.span); if !comments.is_empty() { comments += "\n"; } diff --git a/src/tools/clippy/clippy_lints/src/manual_ilog2.rs b/src/tools/clippy/clippy_lints/src/manual_ilog2.rs index 7c397bd3f5e8..2c368f15d670 100644 --- a/src/tools/clippy/clippy_lints/src/manual_ilog2.rs +++ b/src/tools/clippy/clippy_lints/src/manual_ilog2.rs @@ -31,7 +31,7 @@ /// let log = x.ilog2(); /// let log = x.ilog2(); /// ``` - #[clippy::version = "1.93.0"] + #[clippy::version = "1.94.0"] pub MANUAL_ILOG2, pedantic, "manually reimplementing `ilog2`" diff --git a/src/tools/clippy/clippy_lints/src/manual_pop_if.rs b/src/tools/clippy/clippy_lints/src/manual_pop_if.rs new file mode 100644 index 000000000000..6662a34bc332 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_pop_if.rs @@ -0,0 +1,434 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::MaybeDef; +use clippy_utils::source::snippet_with_context; +use clippy_utils::visitors::is_local_used; +use clippy_utils::{eq_expr_value, is_else_clause, is_lang_item_or_ctor, peel_blocks_with_stmt, sym}; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem, PatKind, RustcVersion, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::{Span, Symbol}; +use std::fmt; + +declare_clippy_lint! { + /// ### What it does + /// Checks for code to be replaced by `pop_if` methods. + /// + /// ### Why is this bad? + /// Using `pop_if` is more concise and idiomatic. + /// + /// ### Known issues + /// Currently, the lint does not handle the case where the + /// `if` condition is part of an `else if` branch. + /// + /// The lint also does not handle the case where + /// the popped value is assigned and used. + /// + /// ### Examples + /// ```no_run + /// # use std::collections::VecDeque; + /// # let mut vec = vec![1, 2, 3, 4, 5]; + /// # let mut deque: VecDeque = VecDeque::from([1, 2, 3, 4, 5]); + /// if vec.last().is_some_and(|x| *x > 5) { + /// vec.pop().unwrap(); + /// } + /// if deque.back().is_some_and(|x| *x > 5) { + /// deque.pop_back().unwrap(); + /// } + /// if deque.front().is_some_and(|x| *x > 5) { + /// deque.pop_front().unwrap(); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// # use std::collections::VecDeque; + /// # let mut vec = vec![1, 2, 3, 4, 5]; + /// # let mut deque: VecDeque = VecDeque::from([1, 2, 3, 4, 5]); + /// vec.pop_if(|x| *x > 5); + /// deque.pop_back_if(|x| *x > 5); + /// deque.pop_front_if(|x| *x > 5); + /// ``` + #[clippy::version = "1.95.0"] + pub MANUAL_POP_IF, + complexity, + "manual implementation of `pop_if` methods" +} + +impl_lint_pass!(ManualPopIf => [MANUAL_POP_IF]); + +pub struct ManualPopIf { + msrv: Msrv, +} + +impl ManualPopIf { + pub fn new(conf: &'static Conf) -> Self { + Self { msrv: conf.msrv } + } +} + +#[allow(clippy::enum_variant_names)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum ManualPopIfKind { + Vec, + VecDequeBack, + VecDequeFront, +} + +impl ManualPopIfKind { + fn check_method(self) -> Symbol { + match self { + ManualPopIfKind::Vec => sym::last, + ManualPopIfKind::VecDequeBack => sym::back, + ManualPopIfKind::VecDequeFront => sym::front, + } + } + + fn pop_method(self) -> Symbol { + match self { + ManualPopIfKind::Vec => sym::pop, + ManualPopIfKind::VecDequeBack => sym::pop_back, + ManualPopIfKind::VecDequeFront => sym::pop_front, + } + } + + fn pop_if_method(self) -> Symbol { + match self { + ManualPopIfKind::Vec => sym::pop_if, + ManualPopIfKind::VecDequeBack => sym::pop_back_if, + ManualPopIfKind::VecDequeFront => sym::pop_front_if, + } + } + + fn is_diag_item(self, cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + match self { + ManualPopIfKind::Vec => ty.is_diag_item(cx, sym::Vec), + ManualPopIfKind::VecDequeBack | ManualPopIfKind::VecDequeFront => ty.is_diag_item(cx, sym::VecDeque), + } + } + + fn msrv(self) -> RustcVersion { + match self { + ManualPopIfKind::Vec => msrvs::VEC_POP_IF, + ManualPopIfKind::VecDequeBack => msrvs::VEC_DEQUE_POP_BACK_IF, + ManualPopIfKind::VecDequeFront => msrvs::VEC_DEQUE_POP_FRONT_IF, + } + } +} + +impl fmt::Display for ManualPopIfKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ManualPopIfKind::Vec => write!(f, "`Vec::pop_if`"), + ManualPopIfKind::VecDequeBack => write!(f, "`VecDeque::pop_back_if`"), + ManualPopIfKind::VecDequeFront => write!(f, "`VecDeque::pop_front_if`"), + } + } +} + +struct ManualPopIfPattern<'tcx> { + kind: ManualPopIfKind, + + /// The collection (`vec` in `vec.last()`) + collection_expr: &'tcx Expr<'tcx>, + + /// The closure (`*x > 5` in `|x| *x > 5`) + predicate: &'tcx Expr<'tcx>, + + /// Parameter name for the closure (`x` in `|x| *x > 5`) + param_name: Symbol, + + /// Span of the if expression (including the `if` keyword) + if_span: Span, +} + +impl ManualPopIfPattern<'_> { + fn emit_lint(&self, cx: &LateContext<'_>) { + let mut app = Applicability::MachineApplicable; + let ctxt = self.if_span.ctxt(); + let collection_snippet = snippet_with_context(cx, self.collection_expr.span, ctxt, "..", &mut app).0; + let predicate_snippet = snippet_with_context(cx, self.predicate.span, ctxt, "..", &mut app).0; + let param_name = self.param_name; + let pop_if_method = self.kind.pop_if_method(); + + let suggestion = format!("{collection_snippet}.{pop_if_method}(|{param_name}| {predicate_snippet});"); + + span_lint_and_sugg( + cx, + MANUAL_POP_IF, + self.if_span, + format!("manual implementation of {}", self.kind), + "try", + suggestion, + app, + ); + } +} + +fn pop_value_is_used(then_block: &Expr<'_>) -> bool { + let ExprKind::Block(block, _) = then_block.kind else { + return true; + }; + + if block.expr.is_some() { + return true; + } + + match block.stmts { + [stmt] => !matches!(stmt.kind, StmtKind::Semi(_) | StmtKind::Item(_)), + [.., last] => matches!(last.kind, StmtKind::Expr(_)), + [] => false, + } +} + +/// Checks for the pattern: +/// ```ignore +/// if vec.last().is_some_and(|x| *x > 5) { +/// vec.pop().unwrap(); +/// } +/// ``` +fn check_is_some_and_pattern<'tcx>( + cx: &LateContext<'tcx>, + cond: &'tcx Expr<'_>, + then_block: &'tcx Expr<'_>, + if_expr_span: Span, + kind: ManualPopIfKind, +) -> Option> { + if pop_value_is_used(then_block) { + return None; + } + + let check_method = kind.check_method(); + let pop_method = kind.pop_method(); + + if let ExprKind::MethodCall(path, receiver, [closure_arg], _) = cond.kind + && path.ident.name == sym::is_some_and + && let ExprKind::MethodCall(check_path, collection_expr, [], _) = receiver.kind + && check_path.ident.name == check_method + && kind.is_diag_item(cx, collection_expr) + && let ExprKind::Closure(closure) = closure_arg.kind + && let body = cx.tcx.hir_body(closure.body) + && let Some((pop_collection, _pop_span)) = check_pop_unwrap(then_block, pop_method) + && eq_expr_value(cx, collection_expr, pop_collection) + && let Some(param) = body.params.first() + && let Some(ident) = param.pat.simple_ident() + { + return Some(ManualPopIfPattern { + kind, + collection_expr, + predicate: body.value, + param_name: ident.name, + if_span: if_expr_span, + }); + } + + None +} + +/// Checks for the pattern: +/// ```ignore +/// if let Some(x) = vec.last() { +/// if *x > 5 { +/// vec.pop().unwrap(); +/// } +/// } +/// ``` +fn check_if_let_pattern<'tcx>( + cx: &LateContext<'tcx>, + cond: &'tcx Expr<'_>, + then_block: &'tcx Expr<'_>, + if_expr_span: Span, + kind: ManualPopIfKind, +) -> Option> { + let check_method = kind.check_method(); + let pop_method = kind.pop_method(); + + if let ExprKind::Let(let_expr) = cond.kind + && let PatKind::TupleStruct(qpath, [binding_pat], _) = let_expr.pat.kind + { + let res = cx.qpath_res(&qpath, let_expr.pat.hir_id); + + if let Some(def_id) = res.opt_def_id() + && is_lang_item_or_ctor(cx, def_id, LangItem::OptionSome) + && let PatKind::Binding(_, binding_id, binding_name, _) = binding_pat.kind + && let ExprKind::MethodCall(path, collection_expr, [], _) = let_expr.init.kind + && path.ident.name == check_method + && kind.is_diag_item(cx, collection_expr) + && let ExprKind::Block(block, _) = then_block.kind + { + // The inner if can be either a statement or a block expression + let inner_if = match (block.stmts, block.expr) { + ([stmt], _) => match stmt.kind { + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, + _ => return None, + }, + ([], Some(expr)) => expr, + _ => return None, + }; + + if let ExprKind::If(inner_cond, inner_then, None) = inner_if.kind + && is_local_used(cx, inner_cond, binding_id) + && !pop_value_is_used(inner_then) + && let Some((pop_collection, _pop_span)) = check_pop_unwrap(inner_then, pop_method) + && eq_expr_value(cx, collection_expr, pop_collection) + { + return Some(ManualPopIfPattern { + kind, + collection_expr, + predicate: inner_cond, + param_name: binding_name.name, + if_span: if_expr_span, + }); + } + } + } + + None +} + +/// Checks for the pattern: +/// ```ignore +/// if let Some(x) = vec.last() && *x > 5 { +/// vec.pop().unwrap(); +/// } +/// ``` +fn check_let_chain_pattern<'tcx>( + cx: &LateContext<'tcx>, + cond: &'tcx Expr<'_>, + then_block: &'tcx Expr<'_>, + if_expr_span: Span, + kind: ManualPopIfKind, +) -> Option> { + if pop_value_is_used(then_block) { + return None; + } + + let check_method = kind.check_method(); + let pop_method = kind.pop_method(); + + if let ExprKind::Binary(op, left, right) = cond.kind + && op.node == rustc_ast::BinOpKind::And + && let ExprKind::Let(let_expr) = left.kind + && let PatKind::TupleStruct(qpath, [binding_pat], _) = let_expr.pat.kind + { + let res = cx.qpath_res(&qpath, let_expr.pat.hir_id); + + if let Some(def_id) = res.opt_def_id() + && is_lang_item_or_ctor(cx, def_id, LangItem::OptionSome) + && let PatKind::Binding(_, binding_id, binding_name, _) = binding_pat.kind + && let ExprKind::MethodCall(path, collection_expr, [], _) = let_expr.init.kind + && path.ident.name == check_method + && kind.is_diag_item(cx, collection_expr) + && is_local_used(cx, right, binding_id) + && !pop_value_is_used(then_block) + && let Some((pop_collection, _pop_span)) = check_pop_unwrap(then_block, pop_method) + && eq_expr_value(cx, collection_expr, pop_collection) + { + return Some(ManualPopIfPattern { + kind, + collection_expr, + predicate: right, + param_name: binding_name.name, + if_span: if_expr_span, + }); + } + } + + None +} + +/// Checks for the pattern: +/// ```ignore +/// if vec.last().map(|x| *x > 5).unwrap_or(false) { +/// vec.pop().unwrap(); +/// } +/// ``` +fn check_map_unwrap_or_pattern<'tcx>( + cx: &LateContext<'tcx>, + cond: &'tcx Expr<'_>, + then_block: &'tcx Expr<'_>, + if_expr_span: Span, + kind: ManualPopIfKind, +) -> Option> { + if pop_value_is_used(then_block) { + return None; + } + + let check_method = kind.check_method(); + let pop_method = kind.pop_method(); + + if let ExprKind::MethodCall(unwrap_path, receiver, [default_arg], _) = cond.kind + && unwrap_path.ident.name == sym::unwrap_or + && matches!(default_arg.kind, ExprKind::Lit(lit) if matches!(lit.node, LitKind::Bool(false))) + && let ExprKind::MethodCall(map_path, map_receiver, [closure_arg], _) = receiver.kind + && map_path.ident.name == sym::map + && let ExprKind::MethodCall(check_path, collection_expr, [], _) = map_receiver.kind + && check_path.ident.name == check_method + && kind.is_diag_item(cx, collection_expr) + && let ExprKind::Closure(closure) = closure_arg.kind + && let body = cx.tcx.hir_body(closure.body) + && cx.typeck_results().expr_ty(body.value).is_bool() + && let Some((pop_collection, _pop_span)) = check_pop_unwrap(then_block, pop_method) + && eq_expr_value(cx, collection_expr, pop_collection) + && let Some(param) = body.params.first() + && let Some(ident) = param.pat.simple_ident() + { + return Some(ManualPopIfPattern { + kind, + collection_expr, + predicate: body.value, + param_name: ident.name, + if_span: if_expr_span, + }); + } + + None +} + +/// Checks for `collection.().unwrap()` or `collection.().expect(..)` +/// and returns the collection and the span of the peeled expr +fn check_pop_unwrap<'tcx>(expr: &'tcx Expr<'_>, pop_method: Symbol) -> Option<(&'tcx Expr<'tcx>, Span)> { + let inner_expr = peel_blocks_with_stmt(expr); + + if let ExprKind::MethodCall(unwrap_path, receiver, _, _) = inner_expr.kind + && matches!(unwrap_path.ident.name, sym::unwrap | sym::expect) + && let ExprKind::MethodCall(pop_path, collection_expr, [], _) = receiver.kind + && pop_path.ident.name == pop_method + { + return Some((collection_expr, inner_expr.span)); + } + + None +} + +impl<'tcx> LateLintPass<'tcx> for ManualPopIf { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let ExprKind::If(cond, then_block, None) = expr.kind else { + return; + }; + + // Do not lint if we are in an else-if branch. + if is_else_clause(cx.tcx, expr) { + return; + } + + for kind in [ + ManualPopIfKind::Vec, + ManualPopIfKind::VecDequeBack, + ManualPopIfKind::VecDequeFront, + ] { + if let Some(pattern) = check_is_some_and_pattern(cx, cond, then_block, expr.span, kind) + .or_else(|| check_if_let_pattern(cx, cond, then_block, expr.span, kind)) + .or_else(|| check_let_chain_pattern(cx, cond, then_block, expr.span, kind)) + .or_else(|| check_map_unwrap_or_pattern(cx, cond, then_block, expr.span, kind)) + && self.msrv.meets(cx, kind.msrv()) + { + pattern.emit_lint(cx); + return; + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index c95a72da6e29..de5f83b4745f 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -154,7 +154,7 @@ fn check_arm<'tcx>( } else { outer_pat.span.shrink_to_hi() }; - let (paren_start, inner_if_span, paren_end) = peel_parens(cx.tcx.sess.source_map(), inner_expr.span); + let (paren_start, inner_if_span, paren_end) = peel_parens(cx, inner_expr.span); let inner_if = inner_if_span.split_at(2).0; let mut sugg = vec![ (inner.then.span.shrink_to_lo(), "=> ".to_string()), diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs index 1fc8bb9acce2..abbf60019c5c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs @@ -9,7 +9,7 @@ use rustc_hir::LangItem::ResultErr; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath}; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::symbol::Ident; @@ -130,7 +130,7 @@ fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// `err`, depending on `is_ok`. fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok: bool) { let method = if is_ok { "ok" } else { "err" }; - let mut app = if span_contains_comment(cx.sess().source_map(), expr.span) { + let mut app = if span_contains_comment(cx, expr.span) { Applicability::MaybeIncorrect } else { Applicability::MachineApplicable diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index 421c6064284d..e3a112d1f780 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -5,7 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, Pat, PatExpr, PatExprKind, PatKind, QPath}; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::LateContext; use rustc_middle::ty::{GenericArgKind, Ty}; use rustc_span::sym; @@ -101,7 +101,7 @@ fn handle( && local_id == binding_id { // Machine applicable only if there are no comments present - let mut applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { + let mut applicability = if span_contains_comment(cx, expr.span) { Applicability::MaybeIncorrect } else { Applicability::MachineApplicable diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs index 3a16c2ed8790..7b3ad2596b9e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs @@ -8,7 +8,7 @@ use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Pat, PatKind, QPath}; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Spanned; @@ -22,7 +22,7 @@ pub(crate) fn check_if_let<'tcx>( then_expr: &'tcx Expr<'_>, else_expr: &'tcx Expr<'_>, ) { - if !span_contains_comment(cx.sess().source_map(), expr.span) + if !span_contains_comment(cx, expr.span) && cx.typeck_results().expr_ty(expr).is_bool() && let Some(b0) = find_bool_lit(then_expr) && let Some(b1) = find_bool_lit(else_expr) @@ -71,7 +71,7 @@ pub(super) fn check_match<'tcx>( ) -> bool { if let Some((last_arm, arms_without_last)) = arms.split_last() && let Some((first_arm, middle_arms)) = arms_without_last.split_first() - && !span_contains_comment(cx.sess().source_map(), e.span) + && !span_contains_comment(cx, e.span) && cx.typeck_results().expr_ty(e).is_bool() && let Some(b0) = find_bool_lit(first_arm.body) && let Some(b1) = find_bool_lit(last_arm.body) diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index 5b0de80e67fd..a8312a04f36f 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -12,7 +12,7 @@ use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExpr, PatExprKind, PatKind, RangeEnd}; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::{LateContext, LintContext}; -use rustc_middle::ty; +use rustc_middle::ty::{self, TypeckResults}; use rustc_span::{ByteSymbol, ErrorGuaranteed, Span, Symbol}; use super::MATCH_SAME_ARMS; @@ -61,7 +61,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { let check_eq_with_pat = |expr_a: &Expr<'_>, expr_b: &Expr<'_>| { let mut local_map: HirIdMap = HirIdMap::default(); - let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| { + let eq_fallback = |a_typeck_results: &TypeckResults<'tcx>, + a: &Expr<'_>, + b_typeck_results: &TypeckResults<'tcx>, + b: &Expr<'_>| { if let Some(a_id) = a.res_local_id() && let Some(b_id) = b.res_local_id() && let entry = match local_map.entry(a_id) { @@ -71,7 +74,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { } // the names technically don't have to match; this makes the lint more conservative && cx.tcx.hir_name(a_id) == cx.tcx.hir_name(b_id) - && cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b) + && a_typeck_results.expr_ty(a) == b_typeck_results.expr_ty(b) && pat_contains_local(lhs.pat, a_id) && pat_contains_local(rhs.pat, b_id) { diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index dc8c06ffd79e..717c47b8aed3 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -1093,12 +1093,11 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { } redundant_pattern_match::check_match(cx, expr, ex, arms); - let source_map = cx.tcx.sess.source_map(); - let mut match_comments = span_extract_comments(source_map, expr.span); + let mut match_comments = span_extract_comments(cx, expr.span); // We remove comments from inside arms block. if !match_comments.is_empty() { for arm in arms { - for comment in span_extract_comments(source_map, arm.body.span) { + for comment in span_extract_comments(cx, arm.body.span) { if let Some(index) = match_comments .iter() .enumerate() diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 23a0046dec76..229670486db3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -7,8 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::Span; -use rustc_span::Spanned; +use rustc_span::{Span, Spanned}; use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS; diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index 7b10c37de42d..d2e593fc17df 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -9,6 +9,7 @@ use rustc_hir::def::Res; use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp}; use rustc_lint::LateContext; +use rustc_middle::ty::TypeckResults; use rustc_middle::ty::adjustment::Adjust; use rustc_span::Span; use rustc_span::symbol::{Ident, Symbol}; @@ -136,7 +137,9 @@ pub fn check_map_call( // .map(|y| y[.acceptable_method()].unwrap()) && let simple_equal = (receiver.res_local_id() == Some(filter_param_id) && map_arg_peeled.res_local_id() == Some(map_param_id)) - && let eq_fallback = (|a: &Expr<'_>, b: &Expr<'_>| { + && let eq_fallback = + (|a_typeck_results: &TypeckResults<'tcx>, a: &Expr<'_>, + b_typeck_results: &TypeckResults<'tcx>, b: &Expr<'_>| { // in `filter(|x| ..)`, replace `*x` with `x` let a_path = if !is_filter_param_ref && let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind @@ -144,7 +147,7 @@ pub fn check_map_call( // let the filter closure arg and the map closure arg be equal a_path.res_local_id() == Some(filter_param_id) && b.res_local_id() == Some(map_param_id) - && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b) + && a_typeck_results.expr_ty_adjusted(a) == b_typeck_results.expr_ty_adjusted(b) }) && (simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(receiver, map_arg_peeled)) diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs index aaface3aa971..3aa666145b86 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs @@ -1,7 +1,7 @@ use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; use clippy_utils::ty::get_iterator_item_ty; use hir::ExprKind; -use rustc_lint::{LateContext, LintContext}; +use rustc_lint::LateContext; use super::{ITER_FILTER_IS_OK, ITER_FILTER_IS_SOME}; @@ -144,7 +144,7 @@ fn expression_type( ) -> Option { if !cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Iterator) || parent_is_map(cx, expr) - || span_contains_comment(cx.sess().source_map(), filter_span.with_hi(expr.span.hi())) + || span_contains_comment(cx, filter_span.with_hi(expr.span.hi())) { return None; } diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs index e4ae14b6cf59..09733953255e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs @@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_ let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability); let span = expr.span.with_lo(map_span.lo()); // If the methods are separated with comments, we don't apply suggestion automatically. - if span_contains_comment(cx.tcx.sess.source_map(), span) { + if span_contains_comment(cx, span) { applicability = Applicability::Unspecified; } span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 0ed166f8dd5b..b647dbdc8468 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -134,9 +134,8 @@ mod unnecessary_lazy_eval; mod unnecessary_literal_unwrap; mod unnecessary_map_or; +mod unnecessary_map_or_else; mod unnecessary_min_or_max; -mod unnecessary_option_map_or_else; -mod unnecessary_result_map_or_else; mod unnecessary_sort_by; mod unnecessary_to_owned; mod unwrap_expect_used; @@ -3030,7 +3029,7 @@ /// ptr.sub(8); /// } /// ``` - #[clippy::version = "1.92.0"] + #[clippy::version = "1.94.0"] pub PTR_OFFSET_BY_LITERAL, pedantic, "unneeded pointer offset" @@ -4347,6 +4346,7 @@ } declare_clippy_lint! { + /// ### What it does /// Checks for usage of `.map_or_else()` "map closure" for `Option` type. /// /// ### Why is this bad? @@ -5449,8 +5449,7 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { }, (sym::map_or_else, [def, map]) => { result_map_or_else_none::check(cx, expr, recv, def, map); - unnecessary_option_map_or_else::check(cx, expr, recv, def, map); - unnecessary_result_map_or_else::check(cx, expr, recv, def, map); + unnecessary_map_or_else::check(cx, expr, recv, def, map, call_span); }, (sym::next, []) => { if let Some((name2, recv2, args2, _, _)) = method_call(recv) { diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs index 042558d645e6..36dd4e952a2d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs @@ -7,8 +7,7 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use rustc_span::Span; -use rustc_span::Spanned; +use rustc_span::{Span, Spanned}; use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS}; diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs index 98b7834178f2..cbb7da327506 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs @@ -2,8 +2,7 @@ use rustc_ast::LitKind; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::Symbol; -use rustc_span::Spanned; +use rustc_span::{Spanned, Symbol}; use super::SUSPICIOUS_SPLITN; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or_else.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or_else.rs new file mode 100644 index 000000000000..7c1c31d2bd38 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or_else.rs @@ -0,0 +1,39 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_expr_identity_function; +use clippy_utils::res::MaybeDef; +use clippy_utils::source::snippet_with_applicability; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::Span; +use rustc_span::symbol::sym; + +use super::{UNNECESSARY_OPTION_MAP_OR_ELSE, UNNECESSARY_RESULT_MAP_OR_ELSE}; + +/// lint use of `_.map_or_else(|err| err, |n| n)` for `Result`s and `Option`s. +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + def_arg: &Expr<'_>, + map_arg: &Expr<'_>, + call_span: Span, +) { + let (symbol, lint) = match cx.typeck_results().expr_ty(recv).opt_diag_name(cx) { + Some(x @ sym::Result) => (x, UNNECESSARY_RESULT_MAP_OR_ELSE), + Some(x @ sym::Option) => (x, UNNECESSARY_OPTION_MAP_OR_ELSE), + _ => return, + }; + + if is_expr_identity_function(cx, map_arg) { + let msg = format!("unused \"map closure\" when calling `{symbol}::map_or_else` value"); + + span_lint_and_then(cx, lint, expr.span, msg, |diag| { + let mut applicability = Applicability::MachineApplicable; + let err_snippet = snippet_with_applicability(cx, def_arg.span, "..", &mut applicability); + let sugg = format!("unwrap_or_else({err_snippet})"); + + diag.span_suggestion_verbose(call_span, "consider using `unwrap_or_else`", sugg, applicability); + }); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_option_map_or_else.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_option_map_or_else.rs deleted file mode 100644 index 265619e26376..000000000000 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_option_map_or_else.rs +++ /dev/null @@ -1,111 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; -use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{expr_or_init, find_binding_init, peel_blocks}; -use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Body, BodyId, Closure, Expr, ExprKind, HirId, QPath}; -use rustc_lint::LateContext; -use rustc_span::symbol::sym; - -use super::UNNECESSARY_OPTION_MAP_OR_ELSE; -use super::utils::get_last_chain_binding_hir_id; - -fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) { - let msg = "unused \"map closure\" when calling `Option::map_or_else` value"; - let mut applicability = Applicability::MachineApplicable; - let self_snippet = snippet_with_applicability(cx, recv.span, "_", &mut applicability); - let err_snippet = snippet_with_applicability(cx, def_arg.span, "..", &mut applicability); - span_lint_and_sugg( - cx, - UNNECESSARY_OPTION_MAP_OR_ELSE, - expr.span, - msg, - "consider using `unwrap_or_else`", - format!("{self_snippet}.unwrap_or_else({err_snippet})"), - Applicability::MachineApplicable, - ); -} - -fn handle_qpath( - cx: &LateContext<'_>, - expr: &Expr<'_>, - recv: &Expr<'_>, - def_arg: &Expr<'_>, - expected_hir_id: HirId, - qpath: QPath<'_>, -) { - if let QPath::Resolved(_, path) = qpath - && let Res::Local(hir_id) = path.res - && expected_hir_id == hir_id - { - emit_lint(cx, expr, recv, def_arg); - } -} - -fn handle_closure(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, body_id: BodyId) { - let body = cx.tcx.hir_body(body_id); - handle_fn_body(cx, expr, recv, def_arg, body); -} - -fn handle_fn_body(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, body: &Body<'_>) { - if let Some(first_param) = body.params.first() { - let body_expr = peel_blocks(body.value); - match body_expr.kind { - ExprKind::Path(qpath) => { - handle_qpath(cx, expr, recv, def_arg, first_param.pat.hir_id, qpath); - }, - // If this is a block (that wasn't peeled off), then it means there are statements. - ExprKind::Block(block, _) => { - if let Some(block_expr) = block.expr - // First we ensure that this is a "binding chain" (each statement is a binding - // of the previous one) and that it is a binding of the closure argument. - && let Some(last_chain_binding_id) = - get_last_chain_binding_hir_id(first_param.pat.hir_id, block.stmts) - && let ExprKind::Path(qpath) = block_expr.kind - { - handle_qpath(cx, expr, recv, def_arg, last_chain_binding_id, qpath); - } - }, - _ => {}, - } - } -} - -/// lint use of `_.map_or_else(|err| err, |n| n)` for `Option`s. -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>, map_arg: &Expr<'_>) { - // lint if the caller of `map_or_else()` is an `Option` - if !cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option) { - return; - } - match map_arg.kind { - // If the second argument is a closure, we can check its body. - ExprKind::Closure(&Closure { body, .. }) => { - handle_closure(cx, expr, recv, def_arg, body); - }, - ExprKind::Path(qpath) => { - let res = cx.qpath_res(&qpath, map_arg.hir_id); - match res { - // Case 1: Local variable (could be a closure) - Res::Local(hir_id) => { - if let Some(init_expr) = find_binding_init(cx, hir_id) { - let origin = expr_or_init(cx, init_expr); - if let ExprKind::Closure(&Closure { body, .. }) = origin.kind { - handle_closure(cx, expr, recv, def_arg, body); - } - } - }, - // Case 2: Function definition - Res::Def(DefKind::Fn, def_id) => { - if let Some(local_def_id) = def_id.as_local() - && let Some(body) = cx.tcx.hir_maybe_body_owned_by(local_def_id) - { - handle_fn_body(cx, expr, recv, def_arg, body); - } - }, - _ => (), - } - }, - _ => (), - } -} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs deleted file mode 100644 index 1f6bb60414ae..000000000000 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs +++ /dev/null @@ -1,80 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::peel_blocks; -use clippy_utils::res::MaybeDef; -use clippy_utils::source::snippet; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath}; -use rustc_lint::LateContext; -use rustc_span::symbol::sym; - -use super::UNNECESSARY_RESULT_MAP_OR_ELSE; -use super::utils::get_last_chain_binding_hir_id; - -fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) { - let msg = "unused \"map closure\" when calling `Result::map_or_else` value"; - let self_snippet = snippet(cx, recv.span, ".."); - let err_snippet = snippet(cx, def_arg.span, ".."); - span_lint_and_sugg( - cx, - UNNECESSARY_RESULT_MAP_OR_ELSE, - expr.span, - msg, - "consider using `unwrap_or_else`", - format!("{self_snippet}.unwrap_or_else({err_snippet})"), - Applicability::MachineApplicable, - ); -} - -fn handle_qpath( - cx: &LateContext<'_>, - expr: &Expr<'_>, - recv: &Expr<'_>, - def_arg: &Expr<'_>, - expected_hir_id: HirId, - qpath: QPath<'_>, -) { - if let QPath::Resolved(_, path) = qpath - && let hir::def::Res::Local(hir_id) = path.res - && expected_hir_id == hir_id - { - emit_lint(cx, expr, recv, def_arg); - } -} - -/// lint use of `_.map_or_else(|err| err, |n| n)` for `Result`s. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, - recv: &'tcx Expr<'_>, - def_arg: &'tcx Expr<'_>, - map_arg: &'tcx Expr<'_>, -) { - // lint if the caller of `map_or_else()` is a `Result` - if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result) - && let ExprKind::Closure(&Closure { body, .. }) = map_arg.kind - && let body = cx.tcx.hir_body(body) - && let Some(first_param) = body.params.first() - { - let body_expr = peel_blocks(body.value); - - match body_expr.kind { - ExprKind::Path(qpath) => { - handle_qpath(cx, expr, recv, def_arg, first_param.pat.hir_id, qpath); - }, - // If this is a block (that wasn't peeled off), then it means there are statements. - ExprKind::Block(block, _) => { - if let Some(block_expr) = block.expr - // First we ensure that this is a "binding chain" (each statement is a binding - // of the previous one) and that it is a binding of the closure argument. - && let Some(last_chain_binding_id) = - get_last_chain_binding_hir_id(first_param.pat.hir_id, block.stmts) - && let ExprKind::Path(qpath) = block_expr.kind - { - handle_qpath(cx, expr, recv, def_arg, last_chain_binding_id, qpath); - } - }, - _ => {}, - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index 387b6f8bf223..a0ad1fe00c62 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -108,7 +108,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { then, r#else: Some(else_expr), }) = higher::If::hir(e) - && !span_contains_comment(cx.tcx.sess.source_map(), e.span) + && !span_contains_comment(cx, e.span) { let reduce = |ret, not| { let mut applicability = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index 567443b8e86d..b4519d654722 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -221,7 +221,7 @@ /// ```rust,no_run /// let a = 0b1110 & 0b0110; /// ``` - #[clippy::version = "1.93.0"] + #[clippy::version = "1.94.0"] pub DECIMAL_BITWISE_OPERANDS, pedantic, "use binary, hex, or octal literals for bitwise operations" diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index e79ce91d8b32..dfd7834a149b 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -143,7 +143,7 @@ fn init_expr_can_use_question_mark(cx: &LateContext<'_>, init_expr: &Expr<'_>) - && init_expr_can_use_question_mark(cx, init_expr) && let Some(ret) = find_let_else_ret_expression(els) && let Some(inner_pat) = pat_and_expr_can_be_question_mark(cx, pat, ret) - && !span_contains_comment(cx.tcx.sess.source_map(), els.span) + && !span_contains_comment(cx, els.span) && !span_contains_cfg(cx, els.span) { let mut applicability = Applicability::MaybeIncorrect; @@ -501,7 +501,8 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: let mut applicability = Applicability::MachineApplicable; let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability); - let requires_semi = matches!(cx.tcx.parent_hir_node(expr.hir_id), Node::Stmt(_)); + let parent = cx.tcx.parent_hir_node(expr.hir_id); + let requires_semi = matches!(parent, Node::Stmt(_)) || cx.typeck_results().expr_ty(expr).is_unit(); let method_call_str = match by_ref { ByRef::Yes(_, Mutability::Mut) => ".as_mut()", ByRef::Yes(_, Mutability::Not) => ".as_ref()", @@ -512,7 +513,7 @@ fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: "{receiver_str}{method_call_str}?{}", if requires_semi { ";" } else { "" } ); - if is_else_clause(cx.tcx, expr) { + if is_else_clause(cx.tcx, expr) || (requires_semi && !matches!(parent, Node::Stmt(_) | Node::Block(_))) { sugg = format!("{{ {sugg} }}"); } diff --git a/src/tools/clippy/clippy_lints/src/same_length_and_capacity.rs b/src/tools/clippy/clippy_lints/src/same_length_and_capacity.rs index b200fd1fe25f..ebf649c24307 100644 --- a/src/tools/clippy/clippy_lints/src/same_length_and_capacity.rs +++ b/src/tools/clippy/clippy_lints/src/same_length_and_capacity.rs @@ -65,7 +65,7 @@ /// // This time, leverage the previously saved capacity: /// let reconstructed = unsafe { Vec::from_raw_parts(ptr, len, cap) }; /// ``` - #[clippy::version = "1.93.0"] + #[clippy::version = "1.94.0"] pub SAME_LENGTH_AND_CAPACITY, pedantic, "`from_raw_parts` with same length and capacity" diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs index 7c68762b22c0..e806123596b8 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs @@ -170,7 +170,7 @@ fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) { StmtKind::Semi(Expr { kind: ExprKind::Block(block, _), .. - }) if !block.span.from_expansion() => { + }) if !block.span.from_expansion() && !block.targeted_by_break => { let attrs = cx.tcx.hir_attrs(stmt.hir_id); if !attrs.is_empty() && !cx.tcx.features().stmt_expr_attributes() { return; diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index 8524497c387c..c99f2e2fb942 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -208,7 +208,7 @@ fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocati .with_lo(vec_alloc.allocation_expr.span.source_callsite().lo()); // If there is no comment in `span_to_replace`, Clippy can automatically fix the code. - let app = if span_contains_comment(cx.tcx.sess.source_map(), span_to_replace) { + let app = if span_contains_comment(cx, span_to_replace) { Applicability::Unspecified } else { Applicability::MachineApplicable diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index 44e35b8dc71b..ac2d3741338a 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -7,9 +7,8 @@ use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::Span; -use rustc_span::Spanned; use rustc_span::symbol::Ident; +use rustc_span::{Span, Spanned}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 60775d1856eb..2bf1d8be4653 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -826,7 +826,9 @@ fn text_has_safety_comment( // Don't lint if the safety comment is part of a codeblock in a doc comment. // It may or may not be required, and we can't very easily check it (and we shouldn't, since // the safety comment isn't referring to the node we're currently checking) - if line.trim_start_matches("///").trim_start().starts_with("```") { + if let Some(doc) = line.strip_prefix("///").or_else(|| line.strip_prefix("//!")) + && doc.trim_start().starts_with("```") + { in_codeblock = !in_codeblock; } diff --git a/src/tools/clippy/clippy_lints/src/useless_vec.rs b/src/tools/clippy/clippy_lints/src/useless_vec.rs index 28c339ce2b7d..7b6122213d60 100644 --- a/src/tools/clippy/clippy_lints/src/useless_vec.rs +++ b/src/tools/clippy/clippy_lints/src/useless_vec.rs @@ -251,7 +251,7 @@ fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { let help_msg = format!("you can use {} directly", suggest_ty.desc()); // If the `vec!` macro contains comment, better not make the suggestion machine applicable as it // would remove them. - let applicability = if span_contains_comment(cx.tcx.sess.source_map(), span) { + let applicability = if span_contains_comment(cx, span) { Applicability::Unspecified } else { Applicability::MachineApplicable diff --git a/src/tools/clippy/clippy_lints/src/write/empty_string.rs b/src/tools/clippy/clippy_lints/src/write/empty_string.rs index 1291f2489a21..fa2ad4ba94a2 100644 --- a/src/tools/clippy/clippy_lints/src/write/empty_string.rs +++ b/src/tools/clippy/clippy_lints/src/write/empty_string.rs @@ -22,7 +22,7 @@ pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: macro_call.span, format!("empty string literal in `{name}!`"), |diag| { - if span_extract_comments(cx.sess().source_map(), macro_call.span).is_empty() { + if span_extract_comments(cx, macro_call.span).is_empty() { let closing_paren = cx.sess().source_map().span_extend_to_prev_char_before( macro_call.span.shrink_to_hi(), ')', diff --git a/src/tools/clippy/clippy_test_deps/Cargo.lock b/src/tools/clippy/clippy_test_deps/Cargo.lock index b22cf9d107d7..63e41e8abd10 100644 --- a/src/tools/clippy/clippy_test_deps/Cargo.lock +++ b/src/tools/clippy/clippy_test_deps/Cargo.lock @@ -55,9 +55,9 @@ checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cfg-if" diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index d7e40130380d..b5e2e0fbf6c5 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2026-03-05 +nightly-2026-03-21 ``` diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 1a75bdff8124..51db6b6b64c7 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -841,12 +841,18 @@ fn fetch_path(&self, qpath: &QPath<'_>, id: HirId) -> Option { && ty.span.ctxt() == self.ctxt.get() && ty_name.ident.span.ctxt() == self.ctxt.get() && matches!(ty_path.res, Res::PrimTy(_)) - && let Some((DefKind::AssocConst { .. }, did)) = self.typeck.type_dependent_def(id) => + && let Some((DefKind::AssocConst { .. }, did)) = self.typeck.type_dependent_def(id) + && self.tcx.inherent_impl_of_assoc(did).is_some() => { did }, - _ if let Res::Def(DefKind::Const { .. } | DefKind::AssocConst { .. }, did) = - self.typeck.qpath_res(qpath, id) => + // TODO: revisit when feature `min_generic_const_args` is stabilized. In the meantime, + // `TyCtxt::const_eval_resolve()` will trigger an ICE when evaluating the body of the + // `type const` definition. + _ if let Res::Def( + DefKind::Const { is_type_const: false } | DefKind::AssocConst { is_type_const: false }, + did, + ) = self.typeck.qpath_res(qpath, id) => { self.source.set(ConstantSource::NonLocal); did diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index 88dd3d96b266..f3c4ae3500af 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -8,7 +8,7 @@ //! Thank you! //! ~The `INTERNAL_METADATA_COLLECTOR` lint -use rustc_errors::{Applicability, Diag, Diagnostic, DiagCtxtHandle, DiagMessage, Level, MultiSpan}; +use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage, Diagnostic, Level, MultiSpan}; #[cfg(debug_assertions)] use rustc_errors::{EmissionGuarantee, SubstitutionPart, Suggestions}; use rustc_hir::HirId; @@ -252,15 +252,19 @@ fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { let sp = sp.into(); #[expect(clippy::disallowed_methods)] - cx.emit_span_lint(lint, sp.clone(), ClippyDiag(|diag: &mut Diag<'_, ()>| { - diag.primary_message(msg); - diag.span(sp); - f(diag); - docs_link(diag, lint); + cx.emit_span_lint( + lint, + sp.clone(), + ClippyDiag(|diag: &mut Diag<'_, ()>| { + diag.primary_message(msg); + diag.span(sp); + f(diag); + docs_link(diag, lint); - #[cfg(debug_assertions)] - validate_diag(diag); - })); + #[cfg(debug_assertions)] + validate_diag(diag); + }), + ); } /// Like [`span_lint`], but emits the lint at the node identified by the given `HirId`. @@ -326,14 +330,19 @@ pub fn span_lint_hir_and_then( f: impl FnOnce(&mut Diag<'_, ()>), ) { #[expect(clippy::disallowed_methods)] - cx.tcx.emit_node_span_lint(lint, hir_id, sp, rustc_errors::DiagDecorator(|diag| { - diag.primary_message(msg); - f(diag); - docs_link(diag, lint); + cx.tcx.emit_node_span_lint( + lint, + hir_id, + sp, + rustc_errors::DiagDecorator(|diag| { + diag.primary_message(msg); + f(diag); + docs_link(diag, lint); - #[cfg(debug_assertions)] - validate_diag(diag); - })); + #[cfg(debug_assertions)] + validate_diag(diag); + }), + ); } /// Add a span lint with a suggestion on how to fix it. diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 4e9c64dd63c7..a4d8fd20e4d3 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -26,7 +26,8 @@ /// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but /// other conditions would make them equal. -type SpanlessEqCallback<'a> = dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a; +type SpanlessEqCallback<'a, 'tcx> = + dyn FnMut(&TypeckResults<'tcx>, &Expr<'_>, &TypeckResults<'tcx>, &Expr<'_>) -> bool + 'a; /// Determines how paths are hashed and compared for equality. #[derive(Copy, Clone, Debug, Default)] @@ -59,7 +60,7 @@ pub struct SpanlessEq<'a, 'tcx> { cx: &'a LateContext<'tcx>, maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>, allow_side_effects: bool, - expr_fallback: Option>>, + expr_fallback: Option>>, path_check: PathCheck, } @@ -94,7 +95,10 @@ pub fn paths_by_resolution(self) -> Self { } #[must_use] - pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self { + pub fn expr_fallback( + self, + expr_fallback: impl FnMut(&TypeckResults<'tcx>, &Expr<'_>, &TypeckResults<'tcx>, &Expr<'_>) -> bool + 'a, + ) -> Self { Self { expr_fallback: Some(Box::new(expr_fallback)), ..self @@ -639,7 +643,15 @@ pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { ) => false, }; (is_eq && (!self.should_ignore(left) || !self.should_ignore(right))) - || self.inner.expr_fallback.as_mut().is_some_and(|f| f(left, right)) + || self + .inner + .maybe_typeck_results + .is_some_and(|(left_typeck_results, right_typeck_results)| { + self.inner + .expr_fallback + .as_mut() + .is_some_and(|f| f(left_typeck_results, left, right_typeck_results, right)) + }) } fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 1e73e43e0a71..b37558c1a7fc 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1838,13 +1838,38 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// * `|[x, y]| [x, y]` /// * `|Foo(bar, baz)| Foo(bar, baz)` /// * `|Foo { bar, baz }| Foo { bar, baz }` +/// * `|x| { let y = x; ...; let z = y; z }` +/// * `|x| { let y = x; ...; let z = y; return z }` /// /// Consider calling [`is_expr_untyped_identity_function`] or [`is_expr_identity_function`] instead. -fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { +fn is_body_identity_function<'hir>(cx: &LateContext<'_>, func: &Body<'hir>) -> bool { let [param] = func.params else { return false; }; + let mut param_pat = param.pat; + + // Given a sequence of `Stmt`s of the form `let p = e` where `e` is an expr identical to the + // current `param_pat`, advance the current `param_pat` to `p`. + // + // Note: This is similar to `clippy_utils::get_last_chain_binding_hir_id`, but it works + // directly over a `Pattern` rather than a `HirId`. And it checks for compatibility via + // `is_expr_identity_of_pat` rather than `HirId` equality + let mut advance_param_pat_over_stmts = |stmts: &[Stmt<'hir>]| { + for stmt in stmts { + if let StmtKind::Let(local) = stmt.kind + && let Some(init) = local.init + && is_expr_identity_of_pat(cx, param_pat, init, true) + { + param_pat = local.pat; + } else { + return false; + } + } + + true + }; + let mut expr = func.value; loop { match expr.kind { @@ -1873,7 +1898,30 @@ fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool { return false; } }, - _ => return is_expr_identity_of_pat(cx, param.pat, expr, true), + ExprKind::Block( + &Block { + stmts, expr: Some(e), .. + }, + _, + ) => { + if !advance_param_pat_over_stmts(stmts) { + return false; + } + + expr = e; + }, + ExprKind::Block(&Block { stmts, expr: None, .. }, _) => { + if let Some((last_stmt, stmts)) = stmts.split_last() + && advance_param_pat_over_stmts(stmts) + && let StmtKind::Semi(e) | StmtKind::Expr(e) = last_stmt.kind + && let ExprKind::Ret(Some(ret_val)) = e.kind + { + expr = ret_val; + } else { + return false; + } + }, + _ => return is_expr_identity_of_pat(cx, param_pat, expr, true), } } } @@ -2762,16 +2810,15 @@ pub fn tokenize_with_text(s: &str) -> impl Iterator bool { - let Ok(snippet) = sm.span_to_snippet(span) else { - return false; - }; - return tokenize(&snippet, FrontmatterAllowed::No).any(|token| { - matches!( - token.kind, - TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } - ) - }); +pub fn span_contains_comment(cx: &impl source::HasSession, span: Span) -> bool { + span.check_source_text(cx, |snippet| { + tokenize(snippet, FrontmatterAllowed::No).any(|token| { + matches!( + token.kind, + TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } + ) + }) + }) } /// Checks whether a given span has any significant token. A significant token is a non-whitespace @@ -2779,30 +2826,32 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { /// This is useful to determine if there are any actual code tokens in the span that are omitted in /// the late pass, such as platform-specific code. pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool { - matches!(span.get_source_text(cx), Some(snippet) if tokenize_with_text(&snippet).any(|(token, _, _)| - match token { + span.check_source_text(cx, |snippet| { + tokenize_with_text(snippet).any(|(token, _, _)| match token { TokenKind::Whitespace => false, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments, _ => true, - } - )) + }) + }) } /// Returns all the comments a given span contains /// /// Comments are returned wrapped with their relevant delimiters -pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String { - span_extract_comments(sm, span).join("\n") +pub fn span_extract_comment(cx: &impl source::HasSession, span: Span) -> String { + span_extract_comments(cx, span).join("\n") } /// Returns all the comments a given span contains. /// /// Comments are returned wrapped with their relevant delimiters. -pub fn span_extract_comments(sm: &SourceMap, span: Span) -> Vec { - let snippet = sm.span_to_snippet(span).unwrap_or_default(); - tokenize_with_text(&snippet) - .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. })) - .map(|(_, s, _)| s.to_string()) - .collect::>() +pub fn span_extract_comments(cx: &impl source::HasSession, span: Span) -> Vec { + span.with_source_text(cx, |snippet| { + tokenize_with_text(snippet) + .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. })) + .map(|(_, s, _)| s.to_string()) + .collect::>() + }) + .unwrap_or_default() } pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 5aa457b9d19c..ecadc3322e14 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -23,9 +23,11 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { + 1,93,0 { VEC_DEQUE_POP_BACK_IF, VEC_DEQUE_POP_FRONT_IF } 1,91,0 { DURATION_FROM_MINUTES_HOURS } 1,88,0 { LET_CHAINS } 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST } + 1,86,0 { VEC_POP_IF } 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL } 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index d6ba678434bc..bb67e4350d2b 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -140,6 +140,7 @@ macro_rules! generate { as_str, assert_failed, author, + back, binaryheap_iter, bool_then, borrow, @@ -295,6 +296,7 @@ macro_rules! generate { from_str_method, from_str_radix, from_weeks, + front, fs, fs_create_dir, fuse, @@ -464,6 +466,12 @@ macro_rules! generate { peekable, permissions_from_mode, pin_macro, + pop, + pop_back, + pop_back_if, + pop_front, + pop_front_if, + pop_if, position, pow, powf, diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index af514778f968..1ac417a8d692 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -29,8 +29,7 @@ use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::{Obligation, ObligationCause}; use std::collections::hash_map::Entry; -use std::debug_assert_matches; -use std::{iter, mem}; +use std::{debug_assert_matches, iter, mem}; use crate::paths::{PathNS, lookup_path_str}; use crate::res::{MaybeDef, MaybeQPath}; diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index c9bb94cededb..1f8bcdea8251 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2026-03-05" +channel = "nightly-2026-03-21" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs b/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs index cd69a12f89d4..efb57e6a1b00 100644 --- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs +++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs @@ -12,17 +12,26 @@ use rustc_middle::ty::TyCtxt; pub fn a(cx: impl LintContext, lint: &'static Lint, span: impl Into, msg: impl Into) { - cx.span_lint(lint, span, |lint| { + cx.emit_span_lint( //~^ disallowed_methods - lint.primary_message(msg); - }); + lint, + span, + DiagDecorator(|lint| { + lint.primary_message(msg); + }), + ); } pub fn b(tcx: TyCtxt<'_>, lint: &'static Lint, hir_id: HirId, span: impl Into, msg: impl Into) { - tcx.emit_node_span_lint(lint, hir_id, span, DiagDecorator(|lint| { + tcx.emit_node_span_lint( //~^ disallowed_methods - lint.primary_message(msg); - })); + lint, + hir_id, + span, + DiagDecorator(|lint| { + lint.primary_message(msg); + }), + ); } fn main() {} diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr index e9d53c64dd9b..8631ddbb2cf4 100644 --- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr +++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr @@ -1,8 +1,8 @@ -error: use of a disallowed method `rustc_lint::context::LintContext::span_lint` +error: use of a disallowed method `rustc_lint::context::LintContext::emit_span_lint` --> tests/ui-internal/disallow_span_lint.rs:15:8 | -LL | cx.span_lint(lint, span, |lint| { - | ^^^^^^^^^ +LL | cx.emit_span_lint( + | ^^^^^^^^^^^^^^ | = note: this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead note: the lint level is defined here @@ -11,11 +11,11 @@ note: the lint level is defined here LL | #![deny(clippy::disallowed_methods)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint` - --> tests/ui-internal/disallow_span_lint.rs:22:9 +error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::emit_node_span_lint` + --> tests/ui-internal/disallow_span_lint.rs:26:9 | -LL | tcx.node_span_lint(lint, hir_id, span, |lint| { - | ^^^^^^^^^^^^^^ +LL | tcx.emit_node_span_lint( + | ^^^^^^^^^^^^^^^^^^^ | = note: this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead diff --git a/src/tools/clippy/tests/ui/crashes/mgca-16691.rs b/src/tools/clippy/tests/ui/crashes/mgca-16691.rs new file mode 100644 index 000000000000..b7039240770b --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/mgca-16691.rs @@ -0,0 +1,16 @@ +//@ check-pass +#![allow(incomplete_features)] +#![feature(min_generic_const_args)] + +trait Trait { + type const N: usize; + fn process(); +} + +impl Trait for () { + type const N: usize = 3; + fn process() { + const N: usize = <()>::N; + _ = 0..Self::N; + } +} diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed index 41e5bbe3cbed..36300aa56eb3 100644 --- a/src/tools/clippy/tests/ui/eta.fixed +++ b/src/tools/clippy/tests/ui/eta.fixed @@ -13,7 +13,8 @@ clippy::uninlined_format_args, clippy::useless_vec, clippy::unnecessary_map_on_constructor, - clippy::needless_lifetimes + clippy::needless_lifetimes, + clippy::unnecessary_option_map_or_else )] use std::path::{Path, PathBuf}; diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs index de0d3fb63c51..63b441851ec1 100644 --- a/src/tools/clippy/tests/ui/eta.rs +++ b/src/tools/clippy/tests/ui/eta.rs @@ -13,7 +13,8 @@ clippy::uninlined_format_args, clippy::useless_vec, clippy::unnecessary_map_on_constructor, - clippy::needless_lifetimes + clippy::needless_lifetimes, + clippy::unnecessary_option_map_or_else )] use std::path::{Path, PathBuf}; diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr index c19947cddafa..b57038963b7e 100644 --- a/src/tools/clippy/tests/ui/eta.stderr +++ b/src/tools/clippy/tests/ui/eta.stderr @@ -1,5 +1,5 @@ error: redundant closure - --> tests/ui/eta.rs:34:27 + --> tests/ui/eta.rs:35:27 | LL | let a = Some(1u8).map(|a| foo(a)); | ^^^^^^^^^^ help: replace the closure with the function itself: `foo` @@ -8,31 +8,31 @@ LL | let a = Some(1u8).map(|a| foo(a)); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` error: redundant closure - --> tests/ui/eta.rs:39:40 + --> tests/ui/eta.rs:40:40 | LL | let _: Option> = true.then(|| vec![]); // special case vec! | ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new` error: redundant closure - --> tests/ui/eta.rs:42:35 + --> tests/ui/eta.rs:43:35 | LL | let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted? | ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2` error: redundant closure - --> tests/ui/eta.rs:45:26 + --> tests/ui/eta.rs:46:26 | LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` error: redundant closure - --> tests/ui/eta.rs:54:27 + --> tests/ui/eta.rs:55:27 | LL | let e = Some(1u8).map(|a| generic(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic` error: redundant closure - --> tests/ui/eta.rs:107:51 + --> tests/ui/eta.rs:108:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo` @@ -41,229 +41,229 @@ LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo()); = help: to override `-D warnings` add `#[allow(clippy::redundant_closure_for_method_calls)]` error: redundant closure - --> tests/ui/eta.rs:109:51 + --> tests/ui/eta.rs:110:51 | LL | let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo` error: redundant closure - --> tests/ui/eta.rs:112:42 + --> tests/ui/eta.rs:113:42 | LL | let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear()); | ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear` error: redundant closure - --> tests/ui/eta.rs:117:29 + --> tests/ui/eta.rs:118:29 | LL | let e = Some("str").map(|s| s.to_string()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string` error: redundant closure - --> tests/ui/eta.rs:119:27 + --> tests/ui/eta.rs:120:27 | LL | let e = Some('a').map(|s| s.to_uppercase()); | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase` error: redundant closure - --> tests/ui/eta.rs:122:65 + --> tests/ui/eta.rs:123:65 | LL | let e: std::vec::Vec = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase` error: redundant closure - --> tests/ui/eta.rs:139:23 + --> tests/ui/eta.rs:140:23 | LL | let _ = x.map(|x| x.parse::()); | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `str::parse::` error: redundant closure - --> tests/ui/eta.rs:192:22 + --> tests/ui/eta.rs:193:22 | LL | requires_fn_once(|| x()); | ^^^^^^ help: replace the closure with the function itself: `x` error: redundant closure - --> tests/ui/eta.rs:200:27 + --> tests/ui/eta.rs:201:27 | LL | let a = Some(1u8).map(|a| foo_ptr(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr` error: redundant closure - --> tests/ui/eta.rs:206:27 + --> tests/ui/eta.rs:207:27 | LL | let a = Some(1u8).map(|a| closure(a)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure` error: redundant closure - --> tests/ui/eta.rs:239:28 + --> tests/ui/eta.rs:240:28 | LL | x.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> tests/ui/eta.rs:241:28 + --> tests/ui/eta.rs:242:28 | LL | y.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res` error: redundant closure - --> tests/ui/eta.rs:243:28 + --> tests/ui/eta.rs:244:28 | LL | z.into_iter().for_each(|x| add_to_res(x)); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res` error: redundant closure - --> tests/ui/eta.rs:251:21 + --> tests/ui/eta.rs:252:21 | LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> tests/ui/eta.rs:256:21 + --> tests/ui/eta.rs:257:21 | LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` error: redundant closure - --> tests/ui/eta.rs:350:18 + --> tests/ui/eta.rs:351:18 | LL | takes_fn_mut(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> tests/ui/eta.rs:354:19 + --> tests/ui/eta.rs:355:19 | LL | takes_fn_once(|| f()); | ^^^^^^ help: replace the closure with the function itself: `&mut f` error: redundant closure - --> tests/ui/eta.rs:359:26 + --> tests/ui/eta.rs:360:26 | LL | move || takes_fn_mut(|| f_used_once()) | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once` error: redundant closure - --> tests/ui/eta.rs:372:19 + --> tests/ui/eta.rs:373:19 | LL | array_opt.map(|a| a.as_slice()); | ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice` error: redundant closure - --> tests/ui/eta.rs:376:19 + --> tests/ui/eta.rs:377:19 | LL | slice_opt.map(|s| s.len()); | ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len` error: redundant closure - --> tests/ui/eta.rs:380:17 + --> tests/ui/eta.rs:381:17 | LL | ptr_opt.map(|p| p.is_null()); | ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null` error: redundant closure - --> tests/ui/eta.rs:385:17 + --> tests/ui/eta.rs:386:17 | LL | dyn_opt.map(|d| d.method_on_dyn()); | ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `::method_on_dyn` error: redundant closure - --> tests/ui/eta.rs:446:19 + --> tests/ui/eta.rs:447:19 | LL | let _ = f(&0, |x, y| f2(x, y)); | ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2` error: redundant closure - --> tests/ui/eta.rs:475:22 + --> tests/ui/eta.rs:476:22 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `Test::method` error: redundant closure - --> tests/ui/eta.rs:480:22 + --> tests/ui/eta.rs:481:22 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `super::Outer::method` error: redundant closure - --> tests/ui/eta.rs:494:18 + --> tests/ui/eta.rs:495:18 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `test_mod::Test::method` error: redundant closure - --> tests/ui/eta.rs:502:30 + --> tests/ui/eta.rs:503:30 | LL | test.map(|t| t.method()) | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `crate::issue_10854::d::Test::method` error: redundant closure - --> tests/ui/eta.rs:522:38 + --> tests/ui/eta.rs:523:38 | LL | let x = Box::new(|| None.map(|x| f(x))); | ^^^^^^^^ help: replace the closure with the function itself: `&f` error: redundant closure - --> tests/ui/eta.rs:527:38 + --> tests/ui/eta.rs:528:38 | LL | let x = Box::new(|| None.map(|x| f(x))); | ^^^^^^^^ help: replace the closure with the function itself: `f` error: redundant closure - --> tests/ui/eta.rs:545:35 + --> tests/ui/eta.rs:546:35 | LL | let _field = bind.or_else(|| get_default()).unwrap(); | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default` error: redundant closure - --> tests/ui/eta.rs:592:16 + --> tests/ui/eta.rs:593:16 | LL | accepts_fn(|| f()); | ^^^^^^ help: replace the closure with the function itself: `**f` error: redundant closure - --> tests/ui/eta.rs:596:16 + --> tests/ui/eta.rs:597:16 | LL | accepts_fn(|| g()); | ^^^^^^ help: replace the closure with the function itself: `g` error: redundant closure - --> tests/ui/eta.rs:609:16 + --> tests/ui/eta.rs:610:16 | LL | accepts_fn(|| b()); | ^^^^^^ help: replace the closure with the function itself: `***b` error: redundant closure - --> tests/ui/eta.rs:633:14 + --> tests/ui/eta.rs:634:14 | LL | .map(|n| MyError::A(n)) | ^^^^^^^^^^^^^^^^^ help: replace the closure with the tuple variant itself: `MyError::A` error: redundant closure - --> tests/ui/eta.rs:630:14 + --> tests/ui/eta.rs:631:14 | LL | .map(|n| S(n)) | ^^^^^^^^ help: replace the closure with the tuple struct itself: `S` error: redundant closure - --> tests/ui/eta.rs:627:14 + --> tests/ui/eta.rs:628:14 | LL | .map(|n| g(n)) | ^^^^^^^^ help: replace the closure with the function itself: `g` error: redundant closure - --> tests/ui/eta.rs:624:14 + --> tests/ui/eta.rs:625:14 | LL | .map(|n| f(n)) | ^^^^^^^^ help: replace the closure with the function itself: `f` error: redundant closure - --> tests/ui/eta.rs:651:31 + --> tests/ui/eta.rs:652:31 | LL | array.iter().for_each(|item| item.method()); | ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `Self::method` error: redundant closure - --> tests/ui/eta.rs:661:38 + --> tests/ui/eta.rs:662:38 | LL | (0..10).flat_map(|x| (0..10).map(|y| closure(y))).count(); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&*closure` diff --git a/src/tools/clippy/tests/ui/manual_pop_if.fixed b/src/tools/clippy/tests/ui/manual_pop_if.fixed new file mode 100644 index 000000000000..6e30786c8097 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_pop_if.fixed @@ -0,0 +1,247 @@ +#![warn(clippy::manual_pop_if)] +#![allow(clippy::collapsible_if, clippy::redundant_closure)] + +use std::collections::VecDeque; +use std::marker::PhantomData; + +// FakeVec has the same methods as Vec but isn't actually a Vec +struct FakeVec(PhantomData); + +impl FakeVec { + fn last(&self) -> Option<&T> { + None + } + + fn pop(&mut self) -> Option { + None + } +} + +fn is_some_and_pattern_positive(mut vec: Vec, mut deque: VecDeque) { + vec.pop_if(|x| *x > 2); + + vec.pop_if(|x| *x > 2); + + deque.pop_back_if(|x| *x > 2); + + deque.pop_front_if(|x| *x > 2); +} + +fn is_some_and_pattern_negative(mut vec: Vec, mut deque: VecDeque) { + // Do not lint, different vectors + let mut vec2 = vec![0]; + if vec.last().is_some_and(|x| *x > 2) { + vec2.pop().unwrap(); + } + + // Do not lint, non-Vec type + let mut fake_vec: FakeVec = FakeVec(PhantomData); + if fake_vec.last().is_some_and(|x| *x > 2) { + fake_vec.pop().unwrap(); + } + + // Do not lint, else-if branch + if false { + // something + } else if vec.last().is_some_and(|x| *x > 2) { + vec.pop().unwrap(); + } + + // Do not lint, value used in let binding + if vec.last().is_some_and(|x| *x > 2) { + let _value = vec.pop().unwrap(); + println!("Popped: {}", _value); + } + + // Do not lint, value used in expression + if vec.last().is_some_and(|x| *x > 2) { + println!("Popped: {}", vec.pop().unwrap()); + } + + // Do not lint, else block + let _result = if vec.last().is_some_and(|x| *x > 2) { + vec.pop().unwrap() + } else { + 0 + }; +} + +fn if_let_pattern_positive(mut vec: Vec, mut deque: VecDeque) { + vec.pop_if(|x| *x > 2); + + vec.pop_if(|x| *x > 2); + + deque.pop_back_if(|x| *x > 2); + + deque.pop_front_if(|x| *x > 2); +} + +fn if_let_pattern_negative(mut vec: Vec) { + // Do not lint, different vectors + let mut vec2 = vec![0]; + if let Some(x) = vec.last() { + if *x > 2 { + vec2.pop().unwrap(); + } + } + + // Do not lint, intervening statements + if let Some(x) = vec.last() { + println!("Checking {}", x); + if *x > 2 { + vec.pop().unwrap(); + } + } + + // Do not lint, bound variable not used in condition + if let Some(_x) = vec.last() { + if vec.len() > 2 { + vec.pop().unwrap(); + } + } + + // Do not lint, value used in let binding + if let Some(x) = vec.last() { + if *x > 2 { + let _val = vec.pop().unwrap(); + } + } + + // Do not lint, else block + let _result = if let Some(x) = vec.last() { + if *x > 2 { vec.pop().unwrap() } else { 0 } + } else { + 0 + }; +} + +fn let_chain_pattern_positive(mut vec: Vec, mut deque: VecDeque) { + vec.pop_if(|x| *x > 2); + + vec.pop_if(|x| *x > 2); + + deque.pop_back_if(|x| *x > 2); + + deque.pop_front_if(|x| *x > 2); +} + +fn let_chain_pattern_negative(mut vec: Vec) { + // Do not lint, different vectors + let mut vec2 = vec![0]; + if let Some(x) = vec.last() + && *x > 2 + { + vec2.pop().unwrap(); + } + + // Do not lint, bound variable not used in condition + if let Some(_x) = vec.last() + && vec.len() > 2 + { + vec.pop().unwrap(); + } + + // Do not lint, value used in let binding + if let Some(x) = vec.last() + && *x > 2 + { + let _val = vec.pop().unwrap(); + } + + // Do not lint, value used in expression + if let Some(x) = vec.last() + && *x > 2 + { + println!("Popped: {}", vec.pop().unwrap()); + } + + // Do not lint, else block + let _result = if let Some(x) = vec.last() + && *x > 2 + { + vec.pop().unwrap() + } else { + 0 + }; +} + +fn map_unwrap_or_pattern_positive(mut vec: Vec, mut deque: VecDeque) { + vec.pop_if(|x| *x > 2); + + vec.pop_if(|x| *x > 2); + + deque.pop_back_if(|x| *x > 2); + + deque.pop_front_if(|x| *x > 2); +} + +fn map_unwrap_or_pattern_negative(mut vec: Vec) { + // Do not lint, unwrap_or(true) instead of false + if vec.last().map(|x| *x > 2).unwrap_or(true) { + vec.pop().unwrap(); + } + + // Do not lint, different vectors + let mut vec2 = vec![0]; + if vec.last().map(|x| *x > 2).unwrap_or(false) { + vec2.pop().unwrap(); + } + + // Do not lint, non-Vec type + let mut fake_vec: FakeVec = FakeVec(PhantomData); + if fake_vec.last().map(|x| *x > 2).unwrap_or(false) { + fake_vec.pop().unwrap(); + } + + // Do not lint, map returns non-boolean + if vec.last().map(|x| x + 1).unwrap_or(0) > 2 { + vec.pop().unwrap(); + } + + // Do not lint, value used in let binding + if vec.last().map(|x| *x > 2).unwrap_or(false) { + let _val = vec.pop().unwrap(); + } + + // Do not lint, else block + let _result = if vec.last().map(|x| *x > 2).unwrap_or(false) { + vec.pop().unwrap() + } else { + 0 + }; +} + +// this makes sure we do not expand vec![] in the suggestion +fn handle_macro_in_closure(mut vec: Vec>) { + vec.pop_if(|e| *e == vec![1]); +} + +#[clippy::msrv = "1.85.0"] +fn msrv_too_low_vec(mut vec: Vec) { + if vec.last().is_some_and(|x| *x > 2) { + vec.pop().unwrap(); + } +} + +#[clippy::msrv = "1.92.0"] +fn msrv_too_low_vecdeque(mut deque: VecDeque) { + if deque.back().is_some_and(|x| *x > 2) { + deque.pop_back().unwrap(); + } + + if deque.front().is_some_and(|x| *x > 2) { + deque.pop_front().unwrap(); + } +} + +#[clippy::msrv = "1.86.0"] +fn msrv_high_enough_vec(mut vec: Vec) { + vec.pop_if(|x| *x > 2); +} + +#[clippy::msrv = "1.93.0"] +fn msrv_high_enough_vecdeque(mut deque: VecDeque) { + deque.pop_back_if(|x| *x > 2); + + deque.pop_front_if(|x| *x > 2); +} diff --git a/src/tools/clippy/tests/ui/manual_pop_if.rs b/src/tools/clippy/tests/ui/manual_pop_if.rs new file mode 100644 index 000000000000..a2f679dfc6b3 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_pop_if.rs @@ -0,0 +1,319 @@ +#![warn(clippy::manual_pop_if)] +#![allow(clippy::collapsible_if, clippy::redundant_closure)] + +use std::collections::VecDeque; +use std::marker::PhantomData; + +// FakeVec has the same methods as Vec but isn't actually a Vec +struct FakeVec(PhantomData); + +impl FakeVec { + fn last(&self) -> Option<&T> { + None + } + + fn pop(&mut self) -> Option { + None + } +} + +fn is_some_and_pattern_positive(mut vec: Vec, mut deque: VecDeque) { + if vec.last().is_some_and(|x| *x > 2) { + //~^ manual_pop_if + vec.pop().unwrap(); + } + + if vec.last().is_some_and(|x| *x > 2) { + //~^ manual_pop_if + vec.pop().expect("element"); + } + + if deque.back().is_some_and(|x| *x > 2) { + //~^ manual_pop_if + deque.pop_back().unwrap(); + } + + if deque.front().is_some_and(|x| *x > 2) { + //~^ manual_pop_if + deque.pop_front().unwrap(); + } +} + +fn is_some_and_pattern_negative(mut vec: Vec, mut deque: VecDeque) { + // Do not lint, different vectors + let mut vec2 = vec![0]; + if vec.last().is_some_and(|x| *x > 2) { + vec2.pop().unwrap(); + } + + // Do not lint, non-Vec type + let mut fake_vec: FakeVec = FakeVec(PhantomData); + if fake_vec.last().is_some_and(|x| *x > 2) { + fake_vec.pop().unwrap(); + } + + // Do not lint, else-if branch + if false { + // something + } else if vec.last().is_some_and(|x| *x > 2) { + vec.pop().unwrap(); + } + + // Do not lint, value used in let binding + if vec.last().is_some_and(|x| *x > 2) { + let _value = vec.pop().unwrap(); + println!("Popped: {}", _value); + } + + // Do not lint, value used in expression + if vec.last().is_some_and(|x| *x > 2) { + println!("Popped: {}", vec.pop().unwrap()); + } + + // Do not lint, else block + let _result = if vec.last().is_some_and(|x| *x > 2) { + vec.pop().unwrap() + } else { + 0 + }; +} + +fn if_let_pattern_positive(mut vec: Vec, mut deque: VecDeque) { + if let Some(x) = vec.last() { + //~^ manual_pop_if + if *x > 2 { + vec.pop().unwrap(); + } + } + + if let Some(x) = vec.last() { + //~^ manual_pop_if + if *x > 2 { + vec.pop().expect("element"); + } + } + + if let Some(x) = deque.back() { + //~^ manual_pop_if + if *x > 2 { + deque.pop_back().unwrap(); + } + } + + if let Some(x) = deque.front() { + //~^ manual_pop_if + if *x > 2 { + deque.pop_front().unwrap(); + } + } +} + +fn if_let_pattern_negative(mut vec: Vec) { + // Do not lint, different vectors + let mut vec2 = vec![0]; + if let Some(x) = vec.last() { + if *x > 2 { + vec2.pop().unwrap(); + } + } + + // Do not lint, intervening statements + if let Some(x) = vec.last() { + println!("Checking {}", x); + if *x > 2 { + vec.pop().unwrap(); + } + } + + // Do not lint, bound variable not used in condition + if let Some(_x) = vec.last() { + if vec.len() > 2 { + vec.pop().unwrap(); + } + } + + // Do not lint, value used in let binding + if let Some(x) = vec.last() { + if *x > 2 { + let _val = vec.pop().unwrap(); + } + } + + // Do not lint, else block + let _result = if let Some(x) = vec.last() { + if *x > 2 { vec.pop().unwrap() } else { 0 } + } else { + 0 + }; +} + +fn let_chain_pattern_positive(mut vec: Vec, mut deque: VecDeque) { + if let Some(x) = vec.last() //~ manual_pop_if + && *x > 2 + { + vec.pop().unwrap(); + } + + if let Some(x) = vec.last() //~ manual_pop_if + && *x > 2 + { + vec.pop().expect("element"); + } + + if let Some(x) = deque.back() //~ manual_pop_if + && *x > 2 + { + deque.pop_back().unwrap(); + } + + if let Some(x) = deque.front() //~ manual_pop_if + && *x > 2 + { + deque.pop_front().unwrap(); + } +} + +fn let_chain_pattern_negative(mut vec: Vec) { + // Do not lint, different vectors + let mut vec2 = vec![0]; + if let Some(x) = vec.last() + && *x > 2 + { + vec2.pop().unwrap(); + } + + // Do not lint, bound variable not used in condition + if let Some(_x) = vec.last() + && vec.len() > 2 + { + vec.pop().unwrap(); + } + + // Do not lint, value used in let binding + if let Some(x) = vec.last() + && *x > 2 + { + let _val = vec.pop().unwrap(); + } + + // Do not lint, value used in expression + if let Some(x) = vec.last() + && *x > 2 + { + println!("Popped: {}", vec.pop().unwrap()); + } + + // Do not lint, else block + let _result = if let Some(x) = vec.last() + && *x > 2 + { + vec.pop().unwrap() + } else { + 0 + }; +} + +fn map_unwrap_or_pattern_positive(mut vec: Vec, mut deque: VecDeque) { + if vec.last().map(|x| *x > 2).unwrap_or(false) { + //~^ manual_pop_if + vec.pop().unwrap(); + } + + if vec.last().map(|x| *x > 2).unwrap_or(false) { + //~^ manual_pop_if + vec.pop().expect("element"); + } + + if deque.back().map(|x| *x > 2).unwrap_or(false) { + //~^ manual_pop_if + deque.pop_back().unwrap(); + } + + if deque.front().map(|x| *x > 2).unwrap_or(false) { + //~^ manual_pop_if + deque.pop_front().unwrap(); + } +} + +fn map_unwrap_or_pattern_negative(mut vec: Vec) { + // Do not lint, unwrap_or(true) instead of false + if vec.last().map(|x| *x > 2).unwrap_or(true) { + vec.pop().unwrap(); + } + + // Do not lint, different vectors + let mut vec2 = vec![0]; + if vec.last().map(|x| *x > 2).unwrap_or(false) { + vec2.pop().unwrap(); + } + + // Do not lint, non-Vec type + let mut fake_vec: FakeVec = FakeVec(PhantomData); + if fake_vec.last().map(|x| *x > 2).unwrap_or(false) { + fake_vec.pop().unwrap(); + } + + // Do not lint, map returns non-boolean + if vec.last().map(|x| x + 1).unwrap_or(0) > 2 { + vec.pop().unwrap(); + } + + // Do not lint, value used in let binding + if vec.last().map(|x| *x > 2).unwrap_or(false) { + let _val = vec.pop().unwrap(); + } + + // Do not lint, else block + let _result = if vec.last().map(|x| *x > 2).unwrap_or(false) { + vec.pop().unwrap() + } else { + 0 + }; +} + +// this makes sure we do not expand vec![] in the suggestion +fn handle_macro_in_closure(mut vec: Vec>) { + if vec.last().is_some_and(|e| *e == vec![1]) { + //~^ manual_pop_if + vec.pop().unwrap(); + } +} + +#[clippy::msrv = "1.85.0"] +fn msrv_too_low_vec(mut vec: Vec) { + if vec.last().is_some_and(|x| *x > 2) { + vec.pop().unwrap(); + } +} + +#[clippy::msrv = "1.92.0"] +fn msrv_too_low_vecdeque(mut deque: VecDeque) { + if deque.back().is_some_and(|x| *x > 2) { + deque.pop_back().unwrap(); + } + + if deque.front().is_some_and(|x| *x > 2) { + deque.pop_front().unwrap(); + } +} + +#[clippy::msrv = "1.86.0"] +fn msrv_high_enough_vec(mut vec: Vec) { + if vec.last().is_some_and(|x| *x > 2) { + //~^ manual_pop_if + vec.pop().unwrap(); + } +} + +#[clippy::msrv = "1.93.0"] +fn msrv_high_enough_vecdeque(mut deque: VecDeque) { + if deque.back().is_some_and(|x| *x > 2) { + //~^ manual_pop_if + deque.pop_back().unwrap(); + } + + if deque.front().is_some_and(|x| *x > 2) { + //~^ manual_pop_if + deque.pop_front().unwrap(); + } +} diff --git a/src/tools/clippy/tests/ui/manual_pop_if.stderr b/src/tools/clippy/tests/ui/manual_pop_if.stderr new file mode 100644 index 000000000000..d57e20dc6205 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_pop_if.stderr @@ -0,0 +1,197 @@ +error: manual implementation of `Vec::pop_if` + --> tests/ui/manual_pop_if.rs:21:5 + | +LL | / if vec.last().is_some_and(|x| *x > 2) { +LL | | +LL | | vec.pop().unwrap(); +LL | | } + | |_____^ help: try: `vec.pop_if(|x| *x > 2);` + | + = note: `-D clippy::manual-pop-if` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_pop_if)]` + +error: manual implementation of `Vec::pop_if` + --> tests/ui/manual_pop_if.rs:26:5 + | +LL | / if vec.last().is_some_and(|x| *x > 2) { +LL | | +LL | | vec.pop().expect("element"); +LL | | } + | |_____^ help: try: `vec.pop_if(|x| *x > 2);` + +error: manual implementation of `VecDeque::pop_back_if` + --> tests/ui/manual_pop_if.rs:31:5 + | +LL | / if deque.back().is_some_and(|x| *x > 2) { +LL | | +LL | | deque.pop_back().unwrap(); +LL | | } + | |_____^ help: try: `deque.pop_back_if(|x| *x > 2);` + +error: manual implementation of `VecDeque::pop_front_if` + --> tests/ui/manual_pop_if.rs:36:5 + | +LL | / if deque.front().is_some_and(|x| *x > 2) { +LL | | +LL | | deque.pop_front().unwrap(); +LL | | } + | |_____^ help: try: `deque.pop_front_if(|x| *x > 2);` + +error: manual implementation of `Vec::pop_if` + --> tests/ui/manual_pop_if.rs:82:5 + | +LL | / if let Some(x) = vec.last() { +LL | | +LL | | if *x > 2 { +LL | | vec.pop().unwrap(); +LL | | } +LL | | } + | |_____^ help: try: `vec.pop_if(|x| *x > 2);` + +error: manual implementation of `Vec::pop_if` + --> tests/ui/manual_pop_if.rs:89:5 + | +LL | / if let Some(x) = vec.last() { +LL | | +LL | | if *x > 2 { +LL | | vec.pop().expect("element"); +LL | | } +LL | | } + | |_____^ help: try: `vec.pop_if(|x| *x > 2);` + +error: manual implementation of `VecDeque::pop_back_if` + --> tests/ui/manual_pop_if.rs:96:5 + | +LL | / if let Some(x) = deque.back() { +LL | | +LL | | if *x > 2 { +LL | | deque.pop_back().unwrap(); +LL | | } +LL | | } + | |_____^ help: try: `deque.pop_back_if(|x| *x > 2);` + +error: manual implementation of `VecDeque::pop_front_if` + --> tests/ui/manual_pop_if.rs:103:5 + | +LL | / if let Some(x) = deque.front() { +LL | | +LL | | if *x > 2 { +LL | | deque.pop_front().unwrap(); +LL | | } +LL | | } + | |_____^ help: try: `deque.pop_front_if(|x| *x > 2);` + +error: manual implementation of `Vec::pop_if` + --> tests/ui/manual_pop_if.rs:151:5 + | +LL | / if let Some(x) = vec.last() +LL | | && *x > 2 +LL | | { +LL | | vec.pop().unwrap(); +LL | | } + | |_____^ help: try: `vec.pop_if(|x| *x > 2);` + +error: manual implementation of `Vec::pop_if` + --> tests/ui/manual_pop_if.rs:157:5 + | +LL | / if let Some(x) = vec.last() +LL | | && *x > 2 +LL | | { +LL | | vec.pop().expect("element"); +LL | | } + | |_____^ help: try: `vec.pop_if(|x| *x > 2);` + +error: manual implementation of `VecDeque::pop_back_if` + --> tests/ui/manual_pop_if.rs:163:5 + | +LL | / if let Some(x) = deque.back() +LL | | && *x > 2 +LL | | { +LL | | deque.pop_back().unwrap(); +LL | | } + | |_____^ help: try: `deque.pop_back_if(|x| *x > 2);` + +error: manual implementation of `VecDeque::pop_front_if` + --> tests/ui/manual_pop_if.rs:169:5 + | +LL | / if let Some(x) = deque.front() +LL | | && *x > 2 +LL | | { +LL | | deque.pop_front().unwrap(); +LL | | } + | |_____^ help: try: `deque.pop_front_if(|x| *x > 2);` + +error: manual implementation of `Vec::pop_if` + --> tests/ui/manual_pop_if.rs:217:5 + | +LL | / if vec.last().map(|x| *x > 2).unwrap_or(false) { +LL | | +LL | | vec.pop().unwrap(); +LL | | } + | |_____^ help: try: `vec.pop_if(|x| *x > 2);` + +error: manual implementation of `Vec::pop_if` + --> tests/ui/manual_pop_if.rs:222:5 + | +LL | / if vec.last().map(|x| *x > 2).unwrap_or(false) { +LL | | +LL | | vec.pop().expect("element"); +LL | | } + | |_____^ help: try: `vec.pop_if(|x| *x > 2);` + +error: manual implementation of `VecDeque::pop_back_if` + --> tests/ui/manual_pop_if.rs:227:5 + | +LL | / if deque.back().map(|x| *x > 2).unwrap_or(false) { +LL | | +LL | | deque.pop_back().unwrap(); +LL | | } + | |_____^ help: try: `deque.pop_back_if(|x| *x > 2);` + +error: manual implementation of `VecDeque::pop_front_if` + --> tests/ui/manual_pop_if.rs:232:5 + | +LL | / if deque.front().map(|x| *x > 2).unwrap_or(false) { +LL | | +LL | | deque.pop_front().unwrap(); +LL | | } + | |_____^ help: try: `deque.pop_front_if(|x| *x > 2);` + +error: manual implementation of `Vec::pop_if` + --> tests/ui/manual_pop_if.rs:276:5 + | +LL | / if vec.last().is_some_and(|e| *e == vec![1]) { +LL | | +LL | | vec.pop().unwrap(); +LL | | } + | |_____^ help: try: `vec.pop_if(|e| *e == vec![1]);` + +error: manual implementation of `Vec::pop_if` + --> tests/ui/manual_pop_if.rs:302:5 + | +LL | / if vec.last().is_some_and(|x| *x > 2) { +LL | | +LL | | vec.pop().unwrap(); +LL | | } + | |_____^ help: try: `vec.pop_if(|x| *x > 2);` + +error: manual implementation of `VecDeque::pop_back_if` + --> tests/ui/manual_pop_if.rs:310:5 + | +LL | / if deque.back().is_some_and(|x| *x > 2) { +LL | | +LL | | deque.pop_back().unwrap(); +LL | | } + | |_____^ help: try: `deque.pop_back_if(|x| *x > 2);` + +error: manual implementation of `VecDeque::pop_front_if` + --> tests/ui/manual_pop_if.rs:315:5 + | +LL | / if deque.front().is_some_and(|x| *x > 2) { +LL | | +LL | | deque.pop_front().unwrap(); +LL | | } + | |_____^ help: try: `deque.pop_front_if(|x| *x > 2);` + +error: aborting due to 20 previous errors + diff --git a/src/tools/clippy/tests/ui/match_same_arms.fixed b/src/tools/clippy/tests/ui/match_same_arms.fixed index 31684a5759fe..b0ab833c7835 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.fixed +++ b/src/tools/clippy/tests/ui/match_same_arms.fixed @@ -140,3 +140,35 @@ fn main() { _ => false, }; } + +fn issue16678() { + // ICE in Rust 1.94.0 + match true { + true => { + fn wrapper(_arg: ()) { + _arg; + } + }, + false => { + fn wrapper(_arg: ()) { + _arg; + } + }, + } +} + +fn issue16698() { + trait Foo { + const X: u8; + } + impl Foo for u8 { + const X: u8 = 2; + } + impl Foo for i8 { + const X: u8 = 2; + } + match true { + false => u8::X, + true => i8::X, + }; +} diff --git a/src/tools/clippy/tests/ui/match_same_arms.rs b/src/tools/clippy/tests/ui/match_same_arms.rs index 39bee01bac22..9c7899122afb 100644 --- a/src/tools/clippy/tests/ui/match_same_arms.rs +++ b/src/tools/clippy/tests/ui/match_same_arms.rs @@ -149,3 +149,35 @@ fn main() { _ => false, }; } + +fn issue16678() { + // ICE in Rust 1.94.0 + match true { + true => { + fn wrapper(_arg: ()) { + _arg; + } + }, + false => { + fn wrapper(_arg: ()) { + _arg; + } + }, + } +} + +fn issue16698() { + trait Foo { + const X: u8; + } + impl Foo for u8 { + const X: u8 = 2; + } + impl Foo for i8 { + const X: u8 = 2; + } + match true { + false => u8::X, + true => i8::X, + }; +} diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed index 102517d34c61..e209da5c8258 100644 --- a/src/tools/clippy/tests/ui/question_mark.fixed +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -524,3 +524,16 @@ fn issue16429(b: i32) -> Option { Some(0) } + +fn issue16654() -> Result<(), i32> { + let result = func_returning_result(); + + #[allow(clippy::collapsible_if)] + if true { + result?; + } + + _ = [{ result?; }]; + + Ok(()) +} diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs index cfea1277fe76..579b51461d13 100644 --- a/src/tools/clippy/tests/ui/question_mark.rs +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -649,3 +649,22 @@ fn issue16429(b: i32) -> Option { Some(0) } + +fn issue16654() -> Result<(), i32> { + let result = func_returning_result(); + + #[allow(clippy::collapsible_if)] + if true { + if let Err(err) = result { + //~^ question_mark + return Err(err); + } + } + + _ = [if let Err(err) = result { + //~^ question_mark + return Err(err); + }]; + + Ok(()) +} diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr index c243f12de040..1d7f665a2662 100644 --- a/src/tools/clippy/tests/ui/question_mark.stderr +++ b/src/tools/clippy/tests/ui/question_mark.stderr @@ -362,5 +362,24 @@ LL | | return None; LL | | }; | |_____^ help: replace it with: `{ a? }` -error: aborting due to 38 previous errors +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:658:9 + | +LL | / if let Err(err) = result { +LL | | +LL | | return Err(err); +LL | | } + | |_________^ help: replace it with: `result?;` + +error: this block may be rewritten with the `?` operator + --> tests/ui/question_mark.rs:664:10 + | +LL | _ = [if let Err(err) = result { + | __________^ +LL | | +LL | | return Err(err); +LL | | }]; + | |_____^ help: replace it with: `{ result?; }` + +error: aborting due to 40 previous errors diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block.fixed b/src/tools/clippy/tests/ui/semicolon_inside_block.fixed index 468f0a5b1e47..c7174881a4fa 100644 --- a/src/tools/clippy/tests/ui/semicolon_inside_block.fixed +++ b/src/tools/clippy/tests/ui/semicolon_inside_block.fixed @@ -7,6 +7,7 @@ clippy::double_parens )] #![warn(clippy::semicolon_inside_block)] +#![feature(try_blocks)] macro_rules! m { (()) => { @@ -106,3 +107,11 @@ pub fn issue15388() { #[rustfmt::skip] {0; 0}; } + +fn issue_try_blocks() { + // try blocks should NOT trigger semicolon_inside_block: + // moving `;` inside changes the return type (e.g. `Option` -> `Option<()>`) + // which in turn changes the type constraints on `?` operators inside the block, + // causing type errors. + try { Some(1)? }; +} diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block.rs b/src/tools/clippy/tests/ui/semicolon_inside_block.rs index 101374af2647..25be9bee4f53 100644 --- a/src/tools/clippy/tests/ui/semicolon_inside_block.rs +++ b/src/tools/clippy/tests/ui/semicolon_inside_block.rs @@ -7,6 +7,7 @@ clippy::double_parens )] #![warn(clippy::semicolon_inside_block)] +#![feature(try_blocks)] macro_rules! m { (()) => { @@ -106,3 +107,11 @@ pub fn issue15388() { #[rustfmt::skip] {0; 0}; } + +fn issue_try_blocks() { + // try blocks should NOT trigger semicolon_inside_block: + // moving `;` inside changes the return type (e.g. `Option` -> `Option<()>`) + // which in turn changes the type constraints on `?` operators inside the block, + // causing type errors. + try { Some(1)? }; +} diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block.stderr b/src/tools/clippy/tests/ui/semicolon_inside_block.stderr index 2046dd1c36be..e16590374295 100644 --- a/src/tools/clippy/tests/ui/semicolon_inside_block.stderr +++ b/src/tools/clippy/tests/ui/semicolon_inside_block.stderr @@ -1,5 +1,5 @@ error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:39:5 + --> tests/ui/semicolon_inside_block.rs:40:5 | LL | { unit_fn_block() }; | ^^^^^^^^^^^^^^^^^^^^ @@ -13,7 +13,7 @@ LL + { unit_fn_block(); } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:41:5 + --> tests/ui/semicolon_inside_block.rs:42:5 | LL | unsafe { unit_fn_block() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL + unsafe { unit_fn_block(); } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:50:5 + --> tests/ui/semicolon_inside_block.rs:51:5 | LL | / { LL | | @@ -41,7 +41,7 @@ LL ~ } | error: consider moving the `;` inside the block for consistent formatting - --> tests/ui/semicolon_inside_block.rs:64:5 + --> tests/ui/semicolon_inside_block.rs:65:5 | LL | { m!(()) }; | ^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.fixed b/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.fixed index 9974ee2d08eb..8d25efbeea00 100644 --- a/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.fixed @@ -3,13 +3,10 @@ clippy::let_and_return, clippy::let_unit_value, clippy::unnecessary_lazy_evaluations, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::needless_return )] -const fn identity(x: T) -> T { - x -} - const fn double_it(x: i32) -> i32 { x * 2 } @@ -18,31 +15,42 @@ fn main() { // Expected errors // Basic scenario let option = Some(()); - option.unwrap_or_else(|| ()); //~ ERROR: unused "map closure" when calling + option.unwrap_or_else(|| ()); //~ unnecessary_option_map_or_else // Type ascription let option = Some(()); - option.unwrap_or_else(|| ()); //~ ERROR: unused "map closure" when calling + option.unwrap_or_else(|| ()); //~ unnecessary_option_map_or_else // Auto-deref let string = String::new(); let option = Some(&string); - let _: &str = option.unwrap_or_else(|| &string); //~ ERROR: unused "map closure" when calling + let _: &str = option.map_or_else(|| &string, |x| x); + // This should in theory lint with a smarter check // Temporary variable let option = Some(()); option.unwrap_or_else(|| ()); - // Identity + // Temporary variable with pattern + let option = Some(((), ())); + option.unwrap_or_else(|| ((), ())); + + // std::convert::identity let string = String::new(); let option = Some(&string); - let _: &str = option.unwrap_or_else(|| &string); //~ ERROR: unused "map closure" when calling + let _: &str = option.unwrap_or_else(|| &string); //~ unnecessary_option_map_or_else - // Closure bound to a variable - let do_nothing = |x: String| x; - let string = String::new(); - let option = Some(string.clone()); - let _: String = option.unwrap_or_else(|| string); //~ ERROR: unused "map closure" when calling + let x: Option<((), ())> = Some(((), ())); + x.unwrap_or_else(|| ((), ())); + //~^ unnecessary_option_map_or_else + + // Returned temporary variable. + let x: Option<()> = Some(()); + x.unwrap_or_else(|| ()); + + // Returned temporary variable with pattern + let x: Option<((), ())> = Some(((), ())); + x.unwrap_or_else(|| ((), ())); // Correct usages let option = Some(()); diff --git a/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.rs b/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.rs index 9b53f3fcd521..42d4ad6ac1f8 100644 --- a/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.rs +++ b/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.rs @@ -3,13 +3,10 @@ clippy::let_and_return, clippy::let_unit_value, clippy::unnecessary_lazy_evaluations, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::needless_return )] -const fn identity(x: T) -> T { - x -} - const fn double_it(x: i32) -> i32 { x * 2 } @@ -18,21 +15,22 @@ fn main() { // Expected errors // Basic scenario let option = Some(()); - option.map_or_else(|| (), |x| x); //~ ERROR: unused "map closure" when calling + option.map_or_else(|| (), |x| x); //~ unnecessary_option_map_or_else // Type ascription let option = Some(()); - option.map_or_else(|| (), |x: ()| x); //~ ERROR: unused "map closure" when calling + option.map_or_else(|| (), |x: ()| x); //~ unnecessary_option_map_or_else // Auto-deref let string = String::new(); let option = Some(&string); - let _: &str = option.map_or_else(|| &string, |x| x); //~ ERROR: unused "map closure" when calling + let _: &str = option.map_or_else(|| &string, |x| x); + // This should in theory lint with a smarter check // Temporary variable let option = Some(()); option.map_or_else( - //~^ ERROR: unused "map closure" when calling + //~^ unnecessary_option_map_or_else || (), |x| { let tmp = x; @@ -40,16 +38,50 @@ fn main() { }, ); - // Identity + // Temporary variable with pattern + let option = Some(((), ())); + option.map_or_else( + //~^ unnecessary_option_map_or_else + || ((), ()), + |x| { + let tmp = x; + let (a, b) = tmp; + (a, b) + }, + ); + + // std::convert::identity let string = String::new(); let option = Some(&string); - let _: &str = option.map_or_else(|| &string, identity); //~ ERROR: unused "map closure" when calling + let _: &str = option.map_or_else(|| &string, std::convert::identity); //~ unnecessary_option_map_or_else - // Closure bound to a variable - let do_nothing = |x: String| x; - let string = String::new(); - let option = Some(string.clone()); - let _: String = option.map_or_else(|| string, do_nothing); //~ ERROR: unused "map closure" when calling + let x: Option<((), ())> = Some(((), ())); + x.map_or_else(|| ((), ()), |(a, b)| (a, b)); + //~^ unnecessary_option_map_or_else + + // Returned temporary variable. + let x: Option<()> = Some(()); + x.map_or_else( + //~^ unnecessary_option_map_or_else + || (), + |n| { + let tmp = n; + let tmp2 = tmp; + return tmp2; + }, + ); + + // Returned temporary variable with pattern + let x: Option<((), ())> = Some(((), ())); + x.map_or_else( + //~^ unnecessary_option_map_or_else + || ((), ()), + |n| { + let tmp = n; + let (a, b) = tmp; + return (a, b); + }, + ); // Correct usages let option = Some(()); diff --git a/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.stderr b/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.stderr index d90875e4efc7..3b579ebdb289 100644 --- a/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_option_map_or_else.stderr @@ -1,26 +1,31 @@ error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:21:5 + --> tests/ui/unnecessary_option_map_or_else.rs:18:5 | LL | option.map_or_else(|| (), |x| x); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-option-map-or-else` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_option_map_or_else)]` +help: consider using `unwrap_or_else` + | +LL - option.map_or_else(|| (), |x| x); +LL + option.unwrap_or_else(|| ()); + | error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:25:5 + --> tests/ui/unnecessary_option_map_or_else.rs:22:5 | LL | option.map_or_else(|| (), |x: ()| x); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` - -error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:30:19 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `unwrap_or_else` + | +LL - option.map_or_else(|| (), |x: ()| x); +LL + option.unwrap_or_else(|| ()); | -LL | let _: &str = option.map_or_else(|| &string, |x| x); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)` error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:34:5 + --> tests/ui/unnecessary_option_map_or_else.rs:32:5 | LL | / option.map_or_else( LL | | @@ -29,19 +34,122 @@ LL | | |x| { ... | LL | | }, LL | | ); - | |_____^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| ())` + | |_____^ + | +help: consider using `unwrap_or_else` + | +LL - option.map_or_else( +LL - +LL - || (), +LL - |x| { +LL - let tmp = x; +LL - tmp +LL - }, +LL - ); +LL + option.unwrap_or_else(|| ()); + | error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:46:19 + --> tests/ui/unnecessary_option_map_or_else.rs:43:5 + | +LL | / option.map_or_else( +LL | | +LL | | || ((), ()), +LL | | |x| { +... | +LL | | }, +LL | | ); + | |_____^ + | +help: consider using `unwrap_or_else` + | +LL - option.map_or_else( +LL - +LL - || ((), ()), +LL - |x| { +LL - let tmp = x; +LL - let (a, b) = tmp; +LL - (a, b) +LL - }, +LL - ); +LL + option.unwrap_or_else(|| ((), ())); | -LL | let _: &str = option.map_or_else(|| &string, identity); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)` error: unused "map closure" when calling `Option::map_or_else` value - --> tests/ui/unnecessary_option_map_or_else.rs:52:21 + --> tests/ui/unnecessary_option_map_or_else.rs:56:19 + | +LL | let _: &str = option.map_or_else(|| &string, std::convert::identity); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `unwrap_or_else` + | +LL - let _: &str = option.map_or_else(|| &string, std::convert::identity); +LL + let _: &str = option.unwrap_or_else(|| &string); | -LL | let _: String = option.map_or_else(|| string, do_nothing); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| string)` -error: aborting due to 6 previous errors +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:59:5 + | +LL | x.map_or_else(|| ((), ()), |(a, b)| (a, b)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `unwrap_or_else` + | +LL - x.map_or_else(|| ((), ()), |(a, b)| (a, b)); +LL + x.unwrap_or_else(|| ((), ())); + | + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:64:5 + | +LL | / x.map_or_else( +LL | | +LL | | || (), +LL | | |n| { +... | +LL | | }, +LL | | ); + | |_____^ + | +help: consider using `unwrap_or_else` + | +LL - x.map_or_else( +LL - +LL - || (), +LL - |n| { +LL - let tmp = n; +LL - let tmp2 = tmp; +LL - return tmp2; +LL - }, +LL - ); +LL + x.unwrap_or_else(|| ()); + | + +error: unused "map closure" when calling `Option::map_or_else` value + --> tests/ui/unnecessary_option_map_or_else.rs:76:5 + | +LL | / x.map_or_else( +LL | | +LL | | || ((), ()), +LL | | |n| { +... | +LL | | }, +LL | | ); + | |_____^ + | +help: consider using `unwrap_or_else` + | +LL - x.map_or_else( +LL - +LL - || ((), ()), +LL - |n| { +LL - let tmp = n; +LL - let (a, b) = tmp; +LL - return (a, b); +LL - }, +LL - ); +LL + x.unwrap_or_else(|| ((), ())); + | + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.fixed b/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.fixed index 5d7e3fa355fd..55542519bdb7 100644 --- a/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.fixed @@ -1,5 +1,10 @@ #![warn(clippy::unnecessary_result_map_or_else)] -#![allow(clippy::unnecessary_literal_unwrap, clippy::let_and_return, clippy::let_unit_value)] +#![allow( + clippy::unnecessary_literal_unwrap, + clippy::let_and_return, + clippy::let_unit_value, + clippy::needless_return +)] fn main() { let x: Result<(), ()> = Ok(()); @@ -14,13 +19,17 @@ fn main() { // Auto-deref. let y = String::new(); let x: Result<&String, &String> = Ok(&y); - let y: &str = x.unwrap_or_else(|err| err); - //~^ unnecessary_result_map_or_else + let y: &str = x.map_or_else(|err| err, |n| n); + // This should lint with a smarter check // Temporary variable. let x: Result<(), ()> = Ok(()); x.unwrap_or_else(|err| err); + // Temporary variable with pattern + let x: Result<((), ()), ((), ())> = Ok(((), ())); + x.unwrap_or_else(|err| err); + // Should not warn. let x: Result = Ok(0); x.map_or_else(|err| err, |n| n + 1); @@ -61,4 +70,16 @@ fn main() { y }, ); + + let x: Result<((), ()), ((), ())> = Ok(((), ())); + x.unwrap_or_else(|err| err); + //~^ unnecessary_result_map_or_else + + // Returned temporary variable. + let x: Result<(), ()> = Ok(()); + x.unwrap_or_else(|err| err); + + // Returned temporary variable with pattern + let x: Result<((), ()), ((), ())> = Ok(((), ())); + x.unwrap_or_else(|err| err); } diff --git a/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.rs b/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.rs index d2bab0f9d9c1..21a4826911e5 100644 --- a/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.rs +++ b/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.rs @@ -1,5 +1,10 @@ #![warn(clippy::unnecessary_result_map_or_else)] -#![allow(clippy::unnecessary_literal_unwrap, clippy::let_and_return, clippy::let_unit_value)] +#![allow( + clippy::unnecessary_literal_unwrap, + clippy::let_and_return, + clippy::let_unit_value, + clippy::needless_return +)] fn main() { let x: Result<(), ()> = Ok(()); @@ -15,7 +20,7 @@ fn main() { let y = String::new(); let x: Result<&String, &String> = Ok(&y); let y: &str = x.map_or_else(|err| err, |n| n); - //~^ unnecessary_result_map_or_else + // This should lint with a smarter check // Temporary variable. let x: Result<(), ()> = Ok(()); @@ -29,6 +34,18 @@ fn main() { }, ); + // Temporary variable with pattern + let x: Result<((), ()), ((), ())> = Ok(((), ())); + x.map_or_else( + //~^ unnecessary_result_map_or_else + |err| err, + |n| { + let tmp = n; + let (a, b) = tmp; + (a, b) + }, + ); + // Should not warn. let x: Result = Ok(0); x.map_or_else(|err| err, |n| n + 1); @@ -69,4 +86,32 @@ fn main() { y }, ); + + let x: Result<((), ()), ((), ())> = Ok(((), ())); + x.map_or_else(|err| err, |(a, b)| (a, b)); + //~^ unnecessary_result_map_or_else + + // Returned temporary variable. + let x: Result<(), ()> = Ok(()); + x.map_or_else( + //~^ unnecessary_result_map_or_else + |err| err, + |n| { + let tmp = n; + let tmp2 = tmp; + return tmp2; + }, + ); + + // Returned temporary variable with pattern + let x: Result<((), ()), ((), ())> = Ok(((), ())); + x.map_or_else( + //~^ unnecessary_result_map_or_else + |err| err, + |n| { + let tmp = n; + let (a, b) = tmp; + return (a, b); + }, + ); } diff --git a/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.stderr b/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.stderr index e6afa50217cc..f76999127c64 100644 --- a/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.stderr @@ -1,26 +1,31 @@ -error: unused "map closure" when calling `Result::map_or_else` value - --> tests/ui/unnecessary_result_map_or_else.rs:6:5 - | -LL | x.map_or_else(|err| err, |n| n); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `x.unwrap_or_else(|err| err)` - | - = note: `-D clippy::unnecessary-result-map-or-else` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::unnecessary_result_map_or_else)]` - error: unused "map closure" when calling `Result::map_or_else` value --> tests/ui/unnecessary_result_map_or_else.rs:11:5 | -LL | x.map_or_else(|err: ()| err, |n: ()| n); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `x.unwrap_or_else(|err: ()| err)` - -error: unused "map closure" when calling `Result::map_or_else` value - --> tests/ui/unnecessary_result_map_or_else.rs:17:19 +LL | x.map_or_else(|err| err, |n| n); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-result-map-or-else` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_result_map_or_else)]` +help: consider using `unwrap_or_else` + | +LL - x.map_or_else(|err| err, |n| n); +LL + x.unwrap_or_else(|err| err); | -LL | let y: &str = x.map_or_else(|err| err, |n| n); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `x.unwrap_or_else(|err| err)` error: unused "map closure" when calling `Result::map_or_else` value - --> tests/ui/unnecessary_result_map_or_else.rs:22:5 + --> tests/ui/unnecessary_result_map_or_else.rs:16:5 + | +LL | x.map_or_else(|err: ()| err, |n: ()| n); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `unwrap_or_else` + | +LL - x.map_or_else(|err: ()| err, |n: ()| n); +LL + x.unwrap_or_else(|err: ()| err); + | + +error: unused "map closure" when calling `Result::map_or_else` value + --> tests/ui/unnecessary_result_map_or_else.rs:27:5 | LL | / x.map_or_else( LL | | @@ -29,7 +34,111 @@ LL | | |n| { ... | LL | | }, LL | | ); - | |_____^ help: consider using `unwrap_or_else`: `x.unwrap_or_else(|err| err)` + | |_____^ + | +help: consider using `unwrap_or_else` + | +LL - x.map_or_else( +LL - +LL - |err| err, +LL - |n| { +LL - let tmp = n; +LL - let tmp2 = tmp; +LL - tmp2 +LL - }, +LL - ); +LL + x.unwrap_or_else(|err| err); + | -error: aborting due to 4 previous errors +error: unused "map closure" when calling `Result::map_or_else` value + --> tests/ui/unnecessary_result_map_or_else.rs:39:5 + | +LL | / x.map_or_else( +LL | | +LL | | |err| err, +LL | | |n| { +... | +LL | | }, +LL | | ); + | |_____^ + | +help: consider using `unwrap_or_else` + | +LL - x.map_or_else( +LL - +LL - |err| err, +LL - |n| { +LL - let tmp = n; +LL - let (a, b) = tmp; +LL - (a, b) +LL - }, +LL - ); +LL + x.unwrap_or_else(|err| err); + | + +error: unused "map closure" when calling `Result::map_or_else` value + --> tests/ui/unnecessary_result_map_or_else.rs:91:5 + | +LL | x.map_or_else(|err| err, |(a, b)| (a, b)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `unwrap_or_else` + | +LL - x.map_or_else(|err| err, |(a, b)| (a, b)); +LL + x.unwrap_or_else(|err| err); + | + +error: unused "map closure" when calling `Result::map_or_else` value + --> tests/ui/unnecessary_result_map_or_else.rs:96:5 + | +LL | / x.map_or_else( +LL | | +LL | | |err| err, +LL | | |n| { +... | +LL | | }, +LL | | ); + | |_____^ + | +help: consider using `unwrap_or_else` + | +LL - x.map_or_else( +LL - +LL - |err| err, +LL - |n| { +LL - let tmp = n; +LL - let tmp2 = tmp; +LL - return tmp2; +LL - }, +LL - ); +LL + x.unwrap_or_else(|err| err); + | + +error: unused "map closure" when calling `Result::map_or_else` value + --> tests/ui/unnecessary_result_map_or_else.rs:108:5 + | +LL | / x.map_or_else( +LL | | +LL | | |err| err, +LL | | |n| { +... | +LL | | }, +LL | | ); + | |_____^ + | +help: consider using `unwrap_or_else` + | +LL - x.map_or_else( +LL - +LL - |err| err, +LL - |n| { +LL - let tmp = n; +LL - let (a, b) = tmp; +LL - return (a, b); +LL - }, +LL - ); +LL + x.unwrap_or_else(|err| err); + | + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs b/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs index d82a7b969080..75e8315343d3 100644 --- a/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs +++ b/src/tools/clippy/tests/ui/unnecessary_safety_comment.rs @@ -100,3 +100,13 @@ pub fn point_to_five() -> *const u8 { } fn main() {} + +mod issue16553 { + //! ``` + //! // SAFETY: All is well. + //! unsafe { + //! foo() + //! } + //! ``` + mod blah {} +} diff --git a/src/tools/clippy/util/gh-pages/index_template.html b/src/tools/clippy/util/gh-pages/index_template.html index 91a5c1263195..26ca901fcd22 100644 --- a/src/tools/clippy/util/gh-pages/index_template.html +++ b/src/tools/clippy/util/gh-pages/index_template.html @@ -174,13 +174,13 @@ Otherwise, have a great day =^.^=

{# #} {# #} - {{lint.group}} {#+ #} + {{lint.group}} {#+ #} {{lint.level}} {#+ #} @@ -194,24 +194,20 @@ Otherwise, have a great day =^.^= {# Applicability #}
{# #} Applicability: {#+ #} - {{ lint.applicability_str() }} {# #} + {{ lint.applicability_str() }} {# #} (?) {# #}
{# Clippy version #}
{# #} {% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: {#+ #} - {{lint.version}} {# #} + {{lint.version}} {# #}
{# Open related issues #} -
{# #} - Related Issues {# #} -
+ Related Issues {# Jump to source #} {% if let Some(id_location) = lint.id_location %} -
{# #} - View Source {# #} -
+ View Source {% endif %} {# #} {# #} diff --git a/src/tools/clippy/util/gh-pages/style.css b/src/tools/clippy/util/gh-pages/style.css index ce478a3e18d0..4f1935ba972f 100644 --- a/src/tools/clippy/util/gh-pages/style.css +++ b/src/tools/clippy/util/gh-pages/style.css @@ -1,5 +1,6 @@ body { --icon-filter: initial; + --label-background: #777; } body.ayu { @@ -230,7 +231,10 @@ button .caret { .panel-title-name { flex: 1; min-width: 400px;} -.panel-title-name .anchor { display: none; } +.panel-title-name .anchor { + display: none; + background-color: var(--label-background); +} article:hover .panel-title-name .anchor { display: inline;} .search-control { @@ -397,10 +401,6 @@ article:hover .panel-title-name .anchor { display: inline;} text-decoration: none; } -.label-default { - background-color: #777; -} - .lint-level { min-width: 4em; } @@ -420,6 +420,7 @@ article:hover .panel-title-name .anchor { display: inline;} .lint-group { min-width: 8em; + background-color: var(--label-background); } .group-deprecated { opacity: 0.5; @@ -459,7 +460,7 @@ article:hover .panel-title-name .anchor { display: inline;} display: flex; flex-flow: column; } - .lint-additional-info > div + div { + .lint-additional-info > * + * { border-top: 1px solid var(--theme-popup-border); } } @@ -468,12 +469,12 @@ article:hover .panel-title-name .anchor { display: inline;} display: flex; flex-flow: row; } - .lint-additional-info > div + div { + .lint-additional-info > * + * { border-left: 1px solid var(--theme-popup-border); } } -.lint-additional-info > div { +.lint-additional-info > * { display: inline-flex; min-width: 200px; flex-grow: 1; diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index b674c6e29d42..b584d14b0c9f 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2121,7 +2121,7 @@ fn output_base_name(&self) -> Utf8PathBuf { } /// Prints a message to (captured) stdout if `config.verbose` is true. - /// The message is also logged to `tracing::debug!` regardles of verbosity. + /// The message is also logged to `tracing::debug!` regardless of verbosity. /// /// Use `format_args!` as the argument to perform formatting if required. fn logv(&self, message: impl fmt::Display) { @@ -2748,7 +2748,7 @@ fn compare_output( return CompareOutcome::Same; } if expected_lines.is_empty() { - // if we have no lines to check, force a full overwite + // if we have no lines to check, force a full overwrite ("", actual) } else { // this prints/blesses the subset, not the actual diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 112a0519016a..f382502c7068 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -1475,7 +1475,6 @@ ui/lint/issue-97094.rs ui/lint/issue-99387.rs ui/lint/let_underscore/issue-119696-err-on-fn.rs ui/lint/let_underscore/issue-119697-extra-let.rs -ui/lint/must_not_suspend/issue-89562.rs ui/lint/unused/issue-103320-must-use-ops.rs ui/lint/unused/issue-104397.rs ui/lint/unused/issue-105061-array-lint.rs diff --git a/tests/assembly-llvm/naked-functions/aarch64-naked-fn-no-bti-prolog.rs b/tests/assembly-llvm/naked-functions/aarch64-naked-fn-no-bti-prolog.rs index 860ecc3cfcd0..430d4a59da6d 100644 --- a/tests/assembly-llvm/naked-functions/aarch64-naked-fn-no-bti-prolog.rs +++ b/tests/assembly-llvm/naked-functions/aarch64-naked-fn-no-bti-prolog.rs @@ -1,4 +1,4 @@ -//@ compile-flags: -C no-prepopulate-passes -Zbranch-protection=bti +//@ compile-flags: -C no-prepopulate-passes -Zbranch-protection=bti -Cunsafe-allow-abi-mismatch=branch-protection //@ assembly-output: emit-asm //@ needs-asm-support //@ only-aarch64 diff --git a/tests/crashes/147973.rs b/tests/crashes/147973.rs deleted file mode 100644 index 7271c54846f1..000000000000 --- a/tests/crashes/147973.rs +++ /dev/null @@ -1,14 +0,0 @@ -// This is part of series of regression tests for some diagnostics ICEs encountered in the wild with -// suggestions having overlapping parts under https://github.com/rust-lang/rust/pull/146121. -// This is one MCVE from the beta crater run regressions from issue 147973. - -//@ needs-rustc-debug-assertions -//@ known-bug: #147973 - -//@ aux-build: overlapping_spans_helper.rs -extern crate overlapping_spans_helper; - -fn main() { - let _name = Some(1); - overlapping_spans_helper::do_loop!(_name); -} diff --git a/tests/pretty/delegation-inherit-attributes.pp b/tests/pretty/delegation-inherit-attributes.pp index 2525d308553b..242e7161aa84 100644 --- a/tests/pretty/delegation-inherit-attributes.pp +++ b/tests/pretty/delegation-inherit-attributes.pp @@ -117,7 +117,7 @@ mod recursive { fn foo(self: _, arg1: _) -> _ { ::foo(self + 1, arg1) } #[attr = MustUse {reason: "some reason"}] #[attr = Inline(Hint)] - fn bar(self: _, arg1: _) -> _ { foo(self + 1, arg1) } + fn bar(arg0: _, arg1: _) -> _ { foo(self + 1, arg1) } } } diff --git a/tests/pretty/float-trailing-dot.rs b/tests/pretty/float-trailing-dot.rs new file mode 100644 index 000000000000..63934bb8eaaa --- /dev/null +++ b/tests/pretty/float-trailing-dot.rs @@ -0,0 +1,8 @@ +//@ pp-exact + +fn main() { + let _ = 0. ..45.; + let _ = 0. ..=360.; + let _ = 0. ..; + let _ = 0. .to_string(); +} diff --git a/tests/pretty/macro-fragment-specifier-whitespace.pp b/tests/pretty/macro-fragment-specifier-whitespace.pp new file mode 100644 index 000000000000..ee5a0f7a7c05 --- /dev/null +++ b/tests/pretty/macro-fragment-specifier-whitespace.pp @@ -0,0 +1,24 @@ +#![feature(prelude_import)] +#![no_std] +extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; +//@ pretty-mode:expanded +//@ pp-exact:macro-fragment-specifier-whitespace.pp + +// Test that fragment specifier names in macro definitions are properly +// separated from the following keyword/identifier token when pretty-printed. +// This is a regression test for a bug where `$x:ident` followed by `where` +// was pretty-printed as `$x:identwhere` (an invalid fragment specifier). + +macro_rules! outer { + ($d:tt $($params:tt)*) => + { + #[macro_export] macro_rules! inner + { ($($params)* where $d($rest:tt)*) => {}; } + }; +} +#[macro_export] +macro_rules! inner { ($x:ident where $ ($rest : tt)*) => {}; } + +fn main() {} diff --git a/tests/pretty/macro-fragment-specifier-whitespace.rs b/tests/pretty/macro-fragment-specifier-whitespace.rs new file mode 100644 index 000000000000..54c6debd9a27 --- /dev/null +++ b/tests/pretty/macro-fragment-specifier-whitespace.rs @@ -0,0 +1,19 @@ +//@ pretty-mode:expanded +//@ pp-exact:macro-fragment-specifier-whitespace.pp + +// Test that fragment specifier names in macro definitions are properly +// separated from the following keyword/identifier token when pretty-printed. +// This is a regression test for a bug where `$x:ident` followed by `where` +// was pretty-printed as `$x:identwhere` (an invalid fragment specifier). + +macro_rules! outer { + ($d:tt $($params:tt)*) => { + #[macro_export] + macro_rules! inner { + ($($params)* where $d($rest:tt)*) => {}; + } + }; +} +outer!($ $x:ident); + +fn main() {} diff --git a/tests/pretty/use-self-braces.rs b/tests/pretty/use-self-braces.rs new file mode 100644 index 000000000000..8db256894361 --- /dev/null +++ b/tests/pretty/use-self-braces.rs @@ -0,0 +1,10 @@ +//@ pp-exact +//@ edition:2021 + +#![allow(unused_imports)] + +// Braces around `self` must be preserved, because `use foo::self` is not valid Rust. +use std::io::{self}; +use std::fmt::{self, Debug}; + +fn main() {} diff --git a/tests/run-make/pointer-auth-link-with-c-lto-clang/rmake.rs b/tests/run-make/pointer-auth-link-with-c-lto-clang/rmake.rs index 2ac5fdee063c..1d06fe118e06 100644 --- a/tests/run-make/pointer-auth-link-with-c-lto-clang/rmake.rs +++ b/tests/run-make/pointer-auth-link-with-c-lto-clang/rmake.rs @@ -32,6 +32,7 @@ fn main() { .opt_level("2") .linker(&env_var("CLANG")) .link_arg("-fuse-ld=lld") + .arg("-Cunsafe-allow-abi-mismatch=branch-protection") .arg("-Zbranch-protection=bti,gcs,pac-ret,leaf") .input("test.rs") .output("test.bin") diff --git a/tests/run-make/pointer-auth-link-with-c/rmake.rs b/tests/run-make/pointer-auth-link-with-c/rmake.rs index 1ddcb79d64ff..1ac68c95559c 100644 --- a/tests/run-make/pointer-auth-link-with-c/rmake.rs +++ b/tests/run-make/pointer-auth-link-with-c/rmake.rs @@ -15,7 +15,11 @@ fn main() { build_native_static_lib("test"); - rustc().arg("-Zbranch-protection=bti,gcs,pac-ret,leaf").input("test.rs").run(); + rustc() + .arg("-Cunsafe-allow-abi-mismatch=branch-protection") + .arg("-Zbranch-protection=bti,gcs,pac-ret,leaf") + .input("test.rs") + .run(); run("test"); cc().arg("-v") .arg("-c") @@ -25,7 +29,11 @@ fn main() { .run(); let obj_file = if is_windows_msvc() { "test.obj" } else { "test" }; llvm_ar().obj_to_ar().output_input("libtest.a", &obj_file).run(); - rustc().arg("-Zbranch-protection=bti,gcs,pac-ret,leaf").input("test.rs").run(); + rustc() + .arg("-Cunsafe-allow-abi-mismatch=branch-protection") + .arg("-Zbranch-protection=bti,gcs,pac-ret,leaf") + .input("test.rs") + .run(); run("test"); // FIXME: +pc was only recently added to LLVM @@ -37,6 +45,10 @@ fn main() { // .run(); // let obj_file = if is_windows_msvc() { "test.obj" } else { "test" }; // llvm_ar().obj_to_ar().output_input("libtest.a", &obj_file).run(); - // rustc().arg("-Zbranch-protection=bti,pac-ret,pc,leaf").input("test.rs").run(); + // rustc() + // .arg("-Cunsafe-allow-abi-mismatch=branch-protection") + // .arg("-Zbranch-protection=bti,pac-ret,pc,leaf") + // .input("test.rs") + // .run(); // run("test"); } diff --git a/tests/rustdoc-ui/lints/lint-missing-doc-code-example.rs b/tests/rustdoc-ui/lints/lint-missing-doc-code-example.rs index 8e5c31d50edc..731d19bffec5 100644 --- a/tests/rustdoc-ui/lints/lint-missing-doc-code-example.rs +++ b/tests/rustdoc-ui/lints/lint-missing-doc-code-example.rs @@ -78,7 +78,11 @@ fn clone(&self) -> Self { } } - +impl Struct { // No doc or code example and it's fine! + pub fn bar() {} + //~^ ERROR missing code example in this documentation + //~| ERROR missing documentation for an associated function +} /// doc /// diff --git a/tests/rustdoc-ui/lints/lint-missing-doc-code-example.stderr b/tests/rustdoc-ui/lints/lint-missing-doc-code-example.stderr index 22533b9816a7..6382c5926e8f 100644 --- a/tests/rustdoc-ui/lints/lint-missing-doc-code-example.stderr +++ b/tests/rustdoc-ui/lints/lint-missing-doc-code-example.stderr @@ -1,3 +1,15 @@ +error: missing documentation for an associated function + --> $DIR/lint-missing-doc-code-example.rs:82:5 + | +LL | pub fn bar() {} + | ^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-missing-doc-code-example.rs:2:9 + | +LL | #![deny(missing_docs)] + | ^^^^^^^^^^^^ + error: missing code example in this documentation --> $DIR/lint-missing-doc-code-example.rs:38:3 | @@ -28,5 +40,11 @@ error: missing code example in this documentation LL | /// Doc | ^^^^^^^ -error: aborting due to 4 previous errors +error: missing code example in this documentation + --> $DIR/lint-missing-doc-code-example.rs:82:5 + | +LL | pub fn bar() {} + | ^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors diff --git a/tests/ui-fulldeps/rustc_public/check_vtable.rs b/tests/ui-fulldeps/rustc_public/check_vtable.rs new file mode 100644 index 000000000000..9b72eff7f6c9 --- /dev/null +++ b/tests/ui-fulldeps/rustc_public/check_vtable.rs @@ -0,0 +1,149 @@ +//@ run-pass +// Test that users are able to use rustc_public to retrieve vtable info. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote + +#![feature(rustc_private)] + +extern crate rustc_middle; +extern crate rustc_driver; +extern crate rustc_interface; +#[macro_use] +extern crate rustc_public; + +use rustc_public::ty::VtblEntry; +use rustc_public::CrateDef; +use std::io::Write; +use std::ops::ControlFlow; + +const CRATE_NAME: &str = "vtable_test"; + +/// This function uses the rustc_public APIs to test the `vtable_entries()`. +fn test_vtable_entries() -> ControlFlow<()> { + let local_crate = rustc_public::local_crate(); + let local_impls = local_crate.trait_impls(); + let child_impl = local_impls + .iter() + .find(|i| i.trimmed_name() == "") + .expect("Could not find "); + + let child_trait_ref = child_impl.trait_impl().value; + let entries = child_trait_ref.vtable_entries(); + match &entries[..] { + [ + VtblEntry::MetadataDropInPlace, + VtblEntry::MetadataSize, + VtblEntry::MetadataAlign, + VtblEntry::Method(primary), + VtblEntry::Method(secondary), + VtblEntry::TraitVPtr(secondary_vptr), + VtblEntry::Method(child), + ] => { + assert!( + primary.name().contains("primary"), + "Expected primary method at index 3" + ); + assert!( + secondary.name().contains("secondary"), + "Expected secondary method at index 4" + ); + let vptr_str = secondary_vptr.def_id.name(); + assert!( + vptr_str.contains("Secondary"), + "Expected Secondary VPtr at index 5" + ); + assert!( + child.name().contains("child"), + "Expected child method at index 6" + ); + } + _ => panic!( + "Unexpected vtable layout for . Found: {:#?}", + entries + ), + } + let vacant_impl = local_impls + .iter() + .find(|i| i.trimmed_name() == "") + .expect("Could not find "); + let vacant_trait_ref = vacant_impl.trait_impl().value; + let vacant_entries = vacant_trait_ref.vtable_entries(); + match &vacant_entries[..] { + [ + VtblEntry::MetadataDropInPlace, + VtblEntry::MetadataSize, + VtblEntry::MetadataAlign, + VtblEntry::Method(valid), + ] => { + assert!(valid.name().contains("valid"), "Expected valid method"); + } + _ => panic!( + "Unexpected vtable layout for . Found: {:#?}", + vacant_entries + ), + } + ControlFlow::Continue(()) +} + +fn main() { + let path = "vtable_input.rs"; + generate_input(&path).unwrap(); + let args = &[ + "rustc".to_string(), + "--crate-type=lib".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, test_vtable_entries).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + pub struct Concrete; + + pub trait Primary {{ + fn primary(&self); + }} + + pub trait Secondary {{ + fn secondary(&self); + }} + + pub trait Child: Primary + Secondary {{ + fn child(&self); + }} + + impl Primary for Concrete {{ + fn primary(&self) {{}} + }} + + impl Secondary for Concrete {{ + fn secondary(&self) {{}} + }} + + impl Child for Concrete {{ + fn child(&self) {{}} + }} + + pub trait WithVacant {{ + fn valid(&self); + + fn excluded(&self, meow: T) where Self: Sized; + }} + + impl WithVacant for Concrete {{ + fn valid(&self) {{}} + fn excluded(&self, meow: T) {{}} + }} + + fn main() {{}} + "# + )?; + Ok(()) +} diff --git a/tests/ui/README.md b/tests/ui/README.md index 45728822b3cf..c14cbd9b4387 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -133,7 +133,7 @@ Runtime panics and error handling generate backtraces to assist in debugging and This directory was originally meant to contain tests related to time complexity and benchmarking. -However, only a single test was ever added to this category: https://github.com/rust-lang/rust/pull/32062 +However, only a single test was ever added to this category: **FIXME**: It is also unclear what would happen were this test to "fail" - would it cause the test suite to remain stuck on this test for a much greater duration than normal? @@ -694,9 +694,7 @@ This test category revolves around trait objects with `Sized` having illegal ope Tests on lifetime elision in impl function signatures. See [Lifetime elision | Nomicon](https://doc.rust-lang.org/nomicon/lifetime-elision.html). ## `tests/ui/impl-restriction/` -Tests for `#![feature(impl_restriction)]`. See [Tracking issue for restrictions #105077 -](https://github.com/rust-lang/rust/issues/105077). - +Tests for `#![feature(impl_restriction)]`. See [Tracking issue for restrictions #105077](https://github.com/rust-lang/rust/issues/105077). ## `tests/ui/impl-trait/` @@ -778,10 +776,6 @@ These tests revolve around the `--json` compiler flag. See [JSON Output](https:/ Tests exercising keywords, such as attempting to use them as identifiers when not contextual keywords. -## `tests/ui/kindck/` - -**FIXME**: `kindck` is no longer a thing, these tests probably need to be audited and rehomed. - ## `tests/ui/label/` Exercises block and loop `'label`s. @@ -800,8 +794,7 @@ See [Type Layout | Reference](https://doc.rust-lang.org/reference/type-layout.ht ## `tests/ui/lazy-type-alias/` -Tests for `#![feature(lazy_type_alias)]`. See [Tracking issue for lazy type aliases #112792 -](https://github.com/rust-lang/rust/issues/112792). +Tests for `#![feature(lazy_type_alias)]`. See [Tracking issue for lazy type aliases #112792](https://github.com/rust-lang/rust/issues/112792). ## `tests/ui/lazy-type-alias-impl-trait/` @@ -856,7 +849,7 @@ See: Tests exercising analysis for unused variables, unreachable statements, functions which are supposed to return a value but do not, as well as values moved elsewhere before they could be used by a function. -**FIXME**: This seems unrelated to "liveness" as defined in the rustc compiler guide. Is this misleadingly named? https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/lifetime_parameters.html#liveness-and-universal-regions +**FIXME**: This seems unrelated to "liveness" as defined in the rustc compiler guide. Is this misleadingly named? ## `tests/ui/loop-match` diff --git a/tests/ui/async-await/drop-option-future.rs b/tests/ui/async-await/drop-option-future.rs new file mode 100644 index 000000000000..155b1d76e44a --- /dev/null +++ b/tests/ui/async-await/drop-option-future.rs @@ -0,0 +1,16 @@ +//! Regression test for . + +//@ edition:2018 +//@ check-pass + +#![allow(dead_code)] +#![allow(unused_assignments)] + +async fn foo() { + let mut f = None; + let value = 0; + f = Some(async { value }); + core::mem::drop(f); +} + +fn main() { } diff --git a/tests/ui/borrowck/kindck-implicit-close-over-mut-var.rs b/tests/ui/borrowck/kindck-implicit-close-over-mut-var.rs deleted file mode 100644 index 22ed8bd3beef..000000000000 --- a/tests/ui/borrowck/kindck-implicit-close-over-mut-var.rs +++ /dev/null @@ -1,49 +0,0 @@ -//@ run-pass - -#![allow(unused_must_use)] -#![allow(dead_code)] -use std::thread; - -fn user(_i: isize) {} - -fn foo() { - // Here, i is *copied* into the proc (heap closure). - // Requires allocation. The proc's copy is not mutable. - let mut i = 0; - let t = thread::spawn(move|| { - user(i); - println!("spawned {}", i) - }); - i += 1; - println!("original {}", i); - t.join(); -} - -fn bar() { - // Here, the original i has not been moved, only copied, so is still - // mutable outside of the proc. - let mut i = 0; - while i < 10 { - let t = thread::spawn(move|| { - user(i); - }); - i += 1; - t.join(); - } -} - -fn car() { - // Here, i must be shadowed in the proc to be mutable. - let mut i = 0; - while i < 10 { - let t = thread::spawn(move|| { - let mut i = i; - i += 1; - user(i); - }); - i += 1; - t.join(); - } -} - -pub fn main() {} diff --git a/tests/ui/issues/issue-52049.rs b/tests/ui/borrowck/non-promotable-static-ref.rs similarity index 53% rename from tests/ui/issues/issue-52049.rs rename to tests/ui/borrowck/non-promotable-static-ref.rs index efdcc4493056..44733493ec06 100644 --- a/tests/ui/issues/issue-52049.rs +++ b/tests/ui/borrowck/non-promotable-static-ref.rs @@ -1,3 +1,5 @@ +//Test for https://github.com/rust-lang/rust/issues/52049 +//Tests that a non-promotable temp variable cannot be used as a static reference. fn foo(_: &'static u32) {} fn unpromotable(t: T) -> T { t } diff --git a/tests/ui/issues/issue-52049.stderr b/tests/ui/borrowck/non-promotable-static-ref.stderr similarity index 90% rename from tests/ui/issues/issue-52049.stderr rename to tests/ui/borrowck/non-promotable-static-ref.stderr index 1d8e136f217b..979b349b7c00 100644 --- a/tests/ui/issues/issue-52049.stderr +++ b/tests/ui/borrowck/non-promotable-static-ref.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/issue-52049.rs:6:10 + --> $DIR/non-promotable-static-ref.rs:8:10 | LL | foo(&unpromotable(5u32)); | -----^^^^^^^^^^^^^^^^^^-- temporary value is freed at the end of this statement diff --git a/tests/ui/coercion/fudge-inference/input-ty-closure-param-fn-trait-bounds.rs b/tests/ui/coercion/fudge-inference/input-ty-closure-param-fn-trait-bounds.rs new file mode 100644 index 000000000000..91dc8e394563 --- /dev/null +++ b/tests/ui/coercion/fudge-inference/input-ty-closure-param-fn-trait-bounds.rs @@ -0,0 +1,17 @@ +//@ check-pass + +// Regression test for + +struct Inv(*mut (T, U)); + +fn pass_through(_: F) -> Inv { + todo!() +} + +fn map(_: Inv) {} + +fn traverse() { + map(pass_through(|| ())) +} + +fn main() {} diff --git a/tests/ui/coercion/fudge-inference/input-ty-higher-ranked-fn-trait.rs b/tests/ui/coercion/fudge-inference/input-ty-higher-ranked-fn-trait.rs new file mode 100644 index 000000000000..241a177e4c15 --- /dev/null +++ b/tests/ui/coercion/fudge-inference/input-ty-higher-ranked-fn-trait.rs @@ -0,0 +1,21 @@ +//@ check-pass + +// Regression test for + +#[expect(dead_code)] +// Must be invariant +pub struct Server(*mut T); +impl Server { + fn new(_: T) -> Self + where + // Must be higher-ranked + T: Fn(&mut i32), + { + todo!() + } +} + +fn main() { + // Must have a type annotation + let _: Server<_> = Server::new(|_| ()); +} diff --git a/tests/ui/const-generics/const-generic-function.rs b/tests/ui/const-generics/const-generic-function.rs index c8d2683e53f4..03c94d90d105 100644 --- a/tests/ui/const-generics/const-generic-function.rs +++ b/tests/ui/const-generics/const-generic-function.rs @@ -14,7 +14,7 @@ const fn baz() -> i32 { fn main() { foo::(); //~ ERROR expected type, found function `baz` - //~| ERROR unresolved item provided when a constant was expected + //~^ ERROR unresolved item provided when a constant was expected foo::(); //~ ERROR expected type, found `1` foo::(); //~ ERROR expected type, found `1` foo::(); //~ ERROR expected type, found `2` diff --git a/tests/ui/const-generics/early/invalid-const-arguments.rs b/tests/ui/const-generics/early/invalid-const-arguments.rs index 68e6b2ac458e..82cc948129a6 100644 --- a/tests/ui/const-generics/early/invalid-const-arguments.rs +++ b/tests/ui/const-generics/early/invalid-const-arguments.rs @@ -1,10 +1,10 @@ -#![crate_type="lib"] +#![crate_type = "lib"] struct A; trait Foo {} impl Foo for A {} -//~^ ERROR cannot find type -//~| ERROR unresolved item provided when a constant +//~^ ERROR cannot find const `N` in this scope +//~| ERROR unresolved item provided when a constant was expected struct B; impl Foo for B {} @@ -12,5 +12,16 @@ impl Foo for B {} struct C; impl Foo for C {} -//~^ ERROR cannot find type -//~| ERROR unresolved item provided when a constant +//~^ ERROR cannot find const `T` in this scope +//~| ERROR unresolved item provided when a constant was expected + +struct D; +impl Foo for D {} +//~^ ERROR cannot find const `E` in this scope +//~| ERROR unresolved item provided when a constant was expected +//~| ERROR cannot find const `X` in this scope +//~| ERROR cannot find const `P` in this scope +struct R; +impl Foo for D {} +//~^ ERROR cannot find const `Q` in this scope +//~| ERROR unresolved item provided when a constant was expected diff --git a/tests/ui/const-generics/early/invalid-const-arguments.stderr b/tests/ui/const-generics/early/invalid-const-arguments.stderr index a0a6d8cc8679..4d8ab68fd71d 100644 --- a/tests/ui/const-generics/early/invalid-const-arguments.stderr +++ b/tests/ui/const-generics/early/invalid-const-arguments.stderr @@ -1,40 +1,82 @@ -error[E0425]: cannot find type `N` in this scope +error[E0425]: cannot find const `N` in this scope --> $DIR/invalid-const-arguments.rs:5:16 | LL | struct A; - | ---------------------- similarly named struct `A` defined here + | ----------- corresponding const parameter on the type defined here LL | trait Foo {} LL | impl Foo for A {} - | ^ + | ^ not found in this scope | -help: a struct with a similar name exists +help: you might have meant to introduce a const parameter `N` on the impl | -LL - impl Foo for A {} -LL + impl Foo for A {} - | -help: you might be missing a type parameter - | -LL | impl Foo for A {} - | +++ +LL | impl Foo for A {} + | +++++++++++++ -error[E0425]: cannot find type `T` in this scope +error[E0425]: cannot find const `T` in this scope --> $DIR/invalid-const-arguments.rs:14:32 | -LL | struct A; - | ---------------------- similarly named struct `A` defined here -... +LL | struct C; + | ----------- corresponding const parameter on the type defined here LL | impl Foo for C {} - | ^ + | ^ not found in this scope | -help: a struct with a similar name exists +help: you might have meant to introduce a const parameter `T` on the impl | -LL - impl Foo for C {} -LL + impl Foo for C {} +LL | impl Foo for C {} + | +++++++++++++ + +error[E0425]: cannot find const `E` in this scope + --> $DIR/invalid-const-arguments.rs:19:16 | -help: you might be missing a type parameter +LL | struct D; + | ----------- corresponding const parameter on the type defined here +LL | impl Foo for D {} + | ^ not found in this scope | -LL | impl Foo for C {} - | +++ +help: you might have meant to introduce a const parameter `E` on the impl + | +LL | impl Foo for D {} + | +++++++++++++ + +error[E0425]: cannot find const `X` in this scope + --> $DIR/invalid-const-arguments.rs:19:19 + | +LL | struct D; + | ----------- corresponding const parameter on the type defined here +LL | impl Foo for D {} + | ^ not found in this scope + | +help: you might have meant to introduce a const parameter `X` on the impl + | +LL | impl Foo for D {} + | +++++++++++++ + +error[E0425]: cannot find const `P` in this scope + --> $DIR/invalid-const-arguments.rs:19:22 + | +LL | struct D; + | ------------ corresponding const parameter on the type defined here +LL | impl Foo for D {} + | ^ not found in this scope + | +help: you might have meant to introduce a const parameter `P` on the impl + | +LL | impl Foo for D {} + | ++++++++++++++ + +error[E0425]: cannot find const `Q` in this scope + --> $DIR/invalid-const-arguments.rs:25:46 + | +LL | struct D; + | ----------- corresponding const parameter on the type defined here +... +LL | impl Foo for D {} + | ^ not found in this scope + | +help: you might have meant to introduce a const parameter `Q` on the impl + | +LL | impl Foo for D {} + | +++++++++++++ error[E0747]: unresolved item provided when a constant was expected --> $DIR/invalid-const-arguments.rs:5:16 @@ -70,7 +112,29 @@ help: if this generic argument was intended as a const parameter, surround it wi LL | impl Foo for C {} | + + -error: aborting due to 5 previous errors +error[E0747]: unresolved item provided when a constant was expected + --> $DIR/invalid-const-arguments.rs:19:16 + | +LL | impl Foo for D {} + | ^ + | +help: if this generic argument was intended as a const parameter, surround it with braces + | +LL | impl Foo for D<{ E }, X, P> {} + | + + + +error[E0747]: unresolved item provided when a constant was expected + --> $DIR/invalid-const-arguments.rs:25:46 + | +LL | impl Foo for D {} + | ^ + | +help: if this generic argument was intended as a const parameter, surround it with braces + | +LL | impl Foo for D {} + | + + + +error: aborting due to 11 previous errors Some errors have detailed explanations: E0425, E0747. For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/const-generics/invalid-enum.rs b/tests/ui/const-generics/invalid-enum.rs index 56fac3e2eb6c..09d06e0df78d 100644 --- a/tests/ui/const-generics/invalid-enum.rs +++ b/tests/ui/const-generics/invalid-enum.rs @@ -12,27 +12,27 @@ enum CompileFlag { pub fn test_1() {} pub fn test_2(x: T) {} -pub struct Example{ +pub struct Example { x: T, } impl Example { - const ASSOC_FLAG: CompileFlag = CompileFlag::A; + const ASSOC_FLAG: CompileFlag = CompileFlag::A; } pub fn main() { - test_1::(); - //~^ ERROR: expected type, found variant - //~| ERROR: unresolved item provided when a constant was expected + test_1::(); + //~^ ERROR: expected type, found variant + //~| ERROR: unresolved item provided when a constant was expected - test_2::<_, CompileFlag::A>(0); - //~^ ERROR: expected type, found variant - //~| ERROR: unresolved item provided when a constant was expected + test_2::<_, CompileFlag::A>(0); + //~^ ERROR: expected type, found variant + //~| ERROR: unresolved item provided when a constant was expected - let _: Example = Example { x: 0 }; - //~^ ERROR: expected type, found variant - //~| ERROR: unresolved item provided when a constant was expected + let _: Example = Example { x: 0 }; + //~^ ERROR: expected type, found variant + //~| ERROR: unresolved item provided when a constant was expected - let _: Example = Example { x: 0 }; - //~^ ERROR: type provided when a constant was expected + let _: Example = Example { x: 0 }; + //~^ ERROR: type provided when a constant was expected } diff --git a/tests/ui/const-generics/invalid-enum.stderr b/tests/ui/const-generics/invalid-enum.stderr index 911052370f43..a557927cb490 100644 --- a/tests/ui/const-generics/invalid-enum.stderr +++ b/tests/ui/const-generics/invalid-enum.stderr @@ -1,73 +1,73 @@ error[E0573]: expected type, found variant `CompileFlag::A` - --> $DIR/invalid-enum.rs:24:12 + --> $DIR/invalid-enum.rs:24:14 | -LL | test_1::(); - | ^^^^^^^^^^^^^^ - | | - | not a type - | help: try using the variant's enum: `CompileFlag` +LL | test_1::(); + | ^^^^^^^^^^^^^^ + | | + | not a type + | help: try using the variant's enum: `CompileFlag` error[E0573]: expected type, found variant `CompileFlag::A` - --> $DIR/invalid-enum.rs:28:15 + --> $DIR/invalid-enum.rs:28:17 | -LL | test_2::<_, CompileFlag::A>(0); - | ^^^^^^^^^^^^^^ - | | - | not a type - | help: try using the variant's enum: `CompileFlag` +LL | test_2::<_, CompileFlag::A>(0); + | ^^^^^^^^^^^^^^ + | | + | not a type + | help: try using the variant's enum: `CompileFlag` error[E0573]: expected type, found variant `CompileFlag::A` - --> $DIR/invalid-enum.rs:32:18 + --> $DIR/invalid-enum.rs:32:20 | -LL | let _: Example = Example { x: 0 }; - | ^^^^^^^^^^^^^^ - | | - | not a type - | help: try using the variant's enum: `CompileFlag` +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^ + | | + | not a type + | help: try using the variant's enum: `CompileFlag` error[E0747]: unresolved item provided when a constant was expected - --> $DIR/invalid-enum.rs:24:12 + --> $DIR/invalid-enum.rs:24:14 | -LL | test_1::(); - | ^^^^^^^^^^^^^^ +LL | test_1::(); + | ^^^^^^^^^^^^^^ | help: if this generic argument was intended as a const parameter, surround it with braces | -LL | test_1::<{ CompileFlag::A }>(); - | + + +LL | test_1::<{ CompileFlag::A }>(); + | + + error[E0747]: unresolved item provided when a constant was expected - --> $DIR/invalid-enum.rs:28:15 + --> $DIR/invalid-enum.rs:28:17 | -LL | test_2::<_, CompileFlag::A>(0); - | ^^^^^^^^^^^^^^ +LL | test_2::<_, CompileFlag::A>(0); + | ^^^^^^^^^^^^^^ | help: if this generic argument was intended as a const parameter, surround it with braces | -LL | test_2::<_, { CompileFlag::A }>(0); - | + + +LL | test_2::<_, { CompileFlag::A }>(0); + | + + error[E0747]: unresolved item provided when a constant was expected - --> $DIR/invalid-enum.rs:32:18 + --> $DIR/invalid-enum.rs:32:20 | -LL | let _: Example = Example { x: 0 }; - | ^^^^^^^^^^^^^^ +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^ | help: if this generic argument was intended as a const parameter, surround it with braces | -LL | let _: Example<{ CompileFlag::A }, _> = Example { x: 0 }; - | + + +LL | let _: Example<{ CompileFlag::A }, _> = Example { x: 0 }; + | + + error[E0747]: type provided when a constant was expected - --> $DIR/invalid-enum.rs:36:18 + --> $DIR/invalid-enum.rs:36:20 | -LL | let _: Example = Example { x: 0 }; - | ^^^^^^^^^^^^^^^^^^^ +LL | let _: Example = Example { x: 0 }; + | ^^^^^^^^^^^^^^^^^^^ | help: if this generic argument was intended as a const parameter, surround it with braces | -LL | let _: Example<{ Example::ASSOC_FLAG }, _> = Example { x: 0 }; - | + + +LL | let _: Example<{ Example::ASSOC_FLAG }, _> = Example { x: 0 }; + | + + error: aborting due to 7 previous errors diff --git a/tests/ui/const-generics/issues/issue-83249.stderr b/tests/ui/const-generics/issues/issue-83249.stderr index 2668348613a4..0f00f70700f5 100644 --- a/tests/ui/const-generics/issues/issue-83249.stderr +++ b/tests/ui/const-generics/issues/issue-83249.stderr @@ -17,10 +17,10 @@ note: required by a bound in `foo` | LL | fn foo(_: [u8; T::N]) -> T { | ^^^ required by this bound in `foo` -help: consider giving this pattern a type +help: consider giving this pattern a type, where the type for type parameter `T` is specified | -LL | let _: /* Type */ = foo([0; 1]); - | ++++++++++++ +LL | let _: u8 = foo([0; 1]); + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/mgca/braced-const-infer.rs b/tests/ui/const-generics/mgca/braced-const-infer.rs new file mode 100644 index 000000000000..9b007d338d1d --- /dev/null +++ b/tests/ui/const-generics/mgca/braced-const-infer.rs @@ -0,0 +1,9 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/153198 +#![feature(min_generic_const_args)] +#![allow(incomplete_features, rust_2021_compatibility)] + +trait Trait {} + +impl dyn Trait<{_}> {} //~ ERROR: the placeholder `_` is not allowed within types on item signatures + +fn main() {} diff --git a/tests/ui/const-generics/mgca/braced-const-infer.stderr b/tests/ui/const-generics/mgca/braced-const-infer.stderr new file mode 100644 index 000000000000..d8ffee4cd0e8 --- /dev/null +++ b/tests/ui/const-generics/mgca/braced-const-infer.stderr @@ -0,0 +1,9 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for implementations + --> $DIR/braced-const-infer.rs:7:17 + | +LL | impl dyn Trait<{_}> {} + | ^ not allowed in type signatures + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0121`. diff --git a/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.rs b/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.rs new file mode 100644 index 000000000000..eeb76683eedf --- /dev/null +++ b/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.rs @@ -0,0 +1,21 @@ +//! This test ensures compilation failure when trying to pass literals +//! without explicitly stated type as inference variables in generic arguments. +//! +//! See https://github.com/rust-lang/rust/pull/153557 + +#![allow(incomplete_features)] +#![feature(adt_const_params, min_generic_const_args, generic_const_parameter_types)] + +fn main() { + foo::<_, { 2 }>(); + //~^ ERROR: type annotations needed for the literal + let _: PC<_, { 42 }> = PC { a: 1, b: 1 }; + //~^ ERROR: type annotations needed for the literal +} + +struct PC { +//~^ ERROR: `T` can't be used as a const parameter type [E0741] + a: T, +} + +fn foo() {} diff --git a/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.stderr b/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.stderr new file mode 100644 index 000000000000..a4b13f41aef0 --- /dev/null +++ b/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.stderr @@ -0,0 +1,21 @@ +error[E0741]: `T` can't be used as a const parameter type + --> $DIR/infer-vars-in-const-args-conflicting.rs:16:23 + | +LL | struct PC { + | ^ + +error: type annotations needed for the literal + --> $DIR/infer-vars-in-const-args-conflicting.rs:10:16 + | +LL | foo::<_, { 2 }>(); + | ^ + +error: type annotations needed for the literal + --> $DIR/infer-vars-in-const-args-conflicting.rs:12:20 + | +LL | let _: PC<_, { 42 }> = PC { a: 1, b: 1 }; + | ^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0741`. diff --git a/tests/ui/const-generics/mgca/infer-vars-in-const-args-correct.rs b/tests/ui/const-generics/mgca/infer-vars-in-const-args-correct.rs new file mode 100644 index 000000000000..f1c0e0b2b4c3 --- /dev/null +++ b/tests/ui/const-generics/mgca/infer-vars-in-const-args-correct.rs @@ -0,0 +1,22 @@ +//! This test ensures no errors are emitted when lowering literals with +//! explicitly stated types and inference variables in the type of the const +//! generic parameter. +//! +//! See https://github.com/rust-lang/rust/pull/153557 + +//@check-pass + +#![allow(incomplete_features)] +#![feature(adt_const_params, + min_generic_const_args, + generic_const_parameter_types, + unsized_const_params +)] + +use std::marker::ConstParamTy_; + +fn main() { + foo::<_, 2_i32>(); +} + +fn foo() {} diff --git a/tests/ui/const-generics/mgca/macro-const-arg-infer.rs b/tests/ui/const-generics/mgca/macro-const-arg-infer.rs new file mode 100644 index 000000000000..017c63d8b0db --- /dev/null +++ b/tests/ui/const-generics/mgca/macro-const-arg-infer.rs @@ -0,0 +1,20 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/153198 +#![feature(min_generic_const_args)] +#![allow(incomplete_features)] +macro_rules! y { + ( $($matcher:tt)*) => { + _ //~ ERROR: the placeholder `_` is not allowed within types on item signatures + }; +} + +struct A; //~ ERROR: type parameter `T` is never used + +const y: A< + { + y! { + x + } + }, +> = 1; //~ ERROR: mismatched types + +fn main() {} diff --git a/tests/ui/const-generics/mgca/macro-const-arg-infer.stderr b/tests/ui/const-generics/mgca/macro-const-arg-infer.stderr new file mode 100644 index 000000000000..406206c59e18 --- /dev/null +++ b/tests/ui/const-generics/mgca/macro-const-arg-infer.stderr @@ -0,0 +1,44 @@ +error[E0392]: type parameter `T` is never used + --> $DIR/macro-const-arg-infer.rs:10:10 + | +LL | struct A; + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error[E0308]: mismatched types + --> $DIR/macro-const-arg-infer.rs:18:5 + | +LL | const y: A< + | __________- +LL | | { +LL | | y! { +LL | | x +LL | | } +LL | | }, +LL | | > = 1; + | | - ^ expected `A<_>`, found integer + | |_| + | expected because of the type of the constant + | + = note: expected struct `A<_>` + found type `{integer}` + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants + --> $DIR/macro-const-arg-infer.rs:6:9 + | +LL | _ + | ^ not allowed in type signatures +... +LL | / y! { +LL | | x +LL | | } + | |_________- in this macro invocation + | + = note: this error originates in the macro `y` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0121, E0308, E0392. +For more information about an error, try `rustc --explain E0121`. diff --git a/tests/ui/delegation/generics/free-fn-to-free-fn.rs b/tests/ui/delegation/generics/free-fn-to-free-fn.rs index 285059e63a75..67f7a12d710d 100644 --- a/tests/ui/delegation/generics/free-fn-to-free-fn.rs +++ b/tests/ui/delegation/generics/free-fn-to-free-fn.rs @@ -16,8 +16,8 @@ pub fn bounds(_: T) {} reuse to_reuse::bounds; fn main() { - // FIXME(fn_delegation): proper support for late bound lifetimes. late::<'static>(&0u8); + //~^ ERROR: function takes 0 lifetime arguments but 1 lifetime argument was supplied struct S; bounds(S); diff --git a/tests/ui/delegation/generics/free-fn-to-free-fn.stderr b/tests/ui/delegation/generics/free-fn-to-free-fn.stderr index 6b6acfc3afff..db88b8d4f8b7 100644 --- a/tests/ui/delegation/generics/free-fn-to-free-fn.stderr +++ b/tests/ui/delegation/generics/free-fn-to-free-fn.stderr @@ -1,3 +1,17 @@ +error[E0107]: function takes 0 lifetime arguments but 1 lifetime argument was supplied + --> $DIR/free-fn-to-free-fn.rs:19:5 + | +LL | late::<'static>(&0u8); + | ^^^^----------- help: remove the unnecessary generics + | | + | expected 0 lifetime arguments + | +note: function defined here, with 0 lifetime parameters + --> $DIR/free-fn-to-free-fn.rs:15:17 + | +LL | reuse to_reuse::late; + | ^^^^ + error[E0277]: the trait bound `S: Clone` is not satisfied --> $DIR/free-fn-to-free-fn.rs:23:12 | @@ -20,6 +34,7 @@ LL + #[derive(Clone)] LL | struct S; | -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0107, E0277. +For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/delegation/generics/free-fn-to-trait-infer.stderr b/tests/ui/delegation/generics/free-fn-to-trait-infer.stderr index ead3c30a6886..a20b28c16402 100644 --- a/tests/ui/delegation/generics/free-fn-to-trait-infer.stderr +++ b/tests/ui/delegation/generics/free-fn-to-trait-infer.stderr @@ -23,7 +23,6 @@ LL | reuse >::foo as generic_arguments2; | ^^^ | | | expected `&u8`, found `&Self` - | found this type parameter | arguments to this function are incorrect | = note: expected reference `&u8` diff --git a/tests/ui/delegation/generics/generics-gen-args-errors.stderr b/tests/ui/delegation/generics/generics-gen-args-errors.stderr index 78aefbc6604c..6c57c3bc8d51 100644 --- a/tests/ui/delegation/generics/generics-gen-args-errors.stderr +++ b/tests/ui/delegation/generics/generics-gen-args-errors.stderr @@ -537,10 +537,8 @@ LL | bar::<1, 2, 3, 4, 5, 6>(); note: function defined here, with 3 generic parameters: `T`, `U`, `N` --> $DIR/generics-gen-args-errors.rs:8:18 | -LL | fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>() {} - | - - -------------- LL | reuse foo as bar; - | ^^^ + | --- ^^^ error[E0107]: function takes 2 lifetime arguments but 5 lifetime arguments were supplied --> $DIR/generics-gen-args-errors.rs:17:9 @@ -553,10 +551,8 @@ LL | bar::<'static, 'static, 'static, 'static, 'static>(); note: function defined here, with 2 lifetime parameters: `'a`, `'b` --> $DIR/generics-gen-args-errors.rs:8:18 | -LL | fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>() {} - | -- -- LL | reuse foo as bar; - | ^^^ + | --- ^^^ error[E0747]: constant provided when a type was expected --> $DIR/generics-gen-args-errors.rs:20:23 @@ -575,10 +571,8 @@ LL | bar::<_, _, _, _, _>(); note: function defined here, with 3 generic parameters: `T`, `U`, `N` --> $DIR/generics-gen-args-errors.rs:8:18 | -LL | fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>() {} - | - - -------------- LL | reuse foo as bar; - | ^^^ + | --- ^^^ error[E0747]: unresolved item provided when a constant was expected --> $DIR/generics-gen-args-errors.rs:28:25 diff --git a/tests/ui/delegation/generics/trait-impl-wrong-args-count.stderr b/tests/ui/delegation/generics/trait-impl-wrong-args-count.stderr index 00dfedef47b5..9234771ca11f 100644 --- a/tests/ui/delegation/generics/trait-impl-wrong-args-count.stderr +++ b/tests/ui/delegation/generics/trait-impl-wrong-args-count.stderr @@ -1,11 +1,11 @@ error[E0107]: function takes at most 2 generic arguments but 3 generic arguments were supplied --> $DIR/trait-impl-wrong-args-count.rs:25:25 | -LL | fn bar<'x: 'x, 'y: 'y, AA, BB, const NN: usize>(&self) {} - | ----------------- help: remove the unnecessary generic argument -... LL | reuse to_reuse::bar; - | ^^^ expected at most 2 generic arguments + | ^^^ + | | + | expected at most 2 generic arguments + | help: remove the unnecessary generic argument | note: function defined here, with at most 2 generic parameters: `A`, `B` --> $DIR/trait-impl-wrong-args-count.rs:7:16 @@ -138,11 +138,11 @@ LL | reuse >::foo as bar2; error[E0107]: associated function takes at most 2 generic arguments but 3 generic arguments were supplied --> $DIR/trait-impl-wrong-args-count.rs:105:40 | -LL | fn bar3() {} - | --- help: remove the unnecessary generic argument -... LL | reuse >::foo as bar3; - | ^^^ expected at most 2 generic arguments + | ^^^ + | | + | expected at most 2 generic arguments + | help: remove the unnecessary generic argument | note: associated function defined here, with at most 2 generic parameters: `X`, `Y` --> $DIR/trait-impl-wrong-args-count.rs:88:12 diff --git a/tests/ui/diagnostic_namespace/on_move/auxiliary/other.rs b/tests/ui/diagnostic_namespace/on_move/auxiliary/other.rs new file mode 100644 index 000000000000..6cff8f4a19b5 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/auxiliary/other.rs @@ -0,0 +1,8 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "Foo", + label = "Bar", +)] +#[derive(Debug)] +pub struct Foo; diff --git a/tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.rs b/tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.rs new file mode 100644 index 000000000000..69c61e62cfc6 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.rs @@ -0,0 +1,14 @@ +//@ aux-build:other.rs + +extern crate other; + +use other::Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.stderr b/tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.stderr new file mode 100644 index 000000000000..8cfe8d6af3f6 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/error_is_shown_in_downstream_crates.stderr @@ -0,0 +1,21 @@ +error[E0382]: Foo + --> $DIR/error_is_shown_in_downstream_crates.rs:12:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/error_is_shown_in_downstream_crates.rs:7:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ 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`. diff --git a/tests/ui/diagnostic_namespace/on_move/on_move_simple.rs b/tests/ui/diagnostic_namespace/on_move/on_move_simple.rs new file mode 100644 index 000000000000..e6112e783bd1 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/on_move_simple.rs @@ -0,0 +1,17 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "Foo", + label = "Bar", +)] +#[derive(Debug)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/on_move_simple.stderr b/tests/ui/diagnostic_namespace/on_move/on_move_simple.stderr new file mode 100644 index 000000000000..483ff5a407bb --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/on_move_simple.stderr @@ -0,0 +1,29 @@ +error[E0382]: Foo + --> $DIR/on_move_simple.rs:15:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/on_move_simple.rs:10:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/on_move_simple.rs:8:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/on_move_with_format.rs b/tests/ui/diagnostic_namespace/on_move/on_move_with_format.rs new file mode 100644 index 000000000000..f2025c2b2271 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/on_move_with_format.rs @@ -0,0 +1,32 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "Foo for {Self}", + label = "Bar for {Self}", +)] +#[derive(Debug)] +struct Foo; + +#[diagnostic::on_move( + message="Foo for {X}", + label="Bar for {X}", +)] +struct MyType { + _x: X, +} + +fn takes_foo(_: Foo) {} + +fn takes_mytype(_: MyType) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo for Foo + + let mytype = MyType { _x: 0 }; + takes_mytype(mytype); + let baz = mytype; + //~^ERROR Foo for i32 +} diff --git a/tests/ui/diagnostic_namespace/on_move/on_move_with_format.stderr b/tests/ui/diagnostic_namespace/on_move/on_move_with_format.stderr new file mode 100644 index 000000000000..6868f1571be8 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/on_move_with_format.stderr @@ -0,0 +1,55 @@ +error[E0382]: Foo for Foo + --> $DIR/on_move_with_format.rs:25:15 + | +LL | let foo = Foo; + | --- Bar for Foo +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/on_move_with_format.rs:18:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/on_move_with_format.rs:8:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error[E0382]: Foo for i32 + --> $DIR/on_move_with_format.rs:30:15 + | +LL | let mytype = MyType { _x: 0 }; + | ------ Bar for i32 +LL | takes_mytype(mytype); + | ------ value moved here +LL | let baz = mytype; + | ^^^^^^ value used here after move + | +note: consider changing this parameter type in function `takes_mytype` to borrow instead if owning the value isn't necessary + --> $DIR/on_move_with_format.rs:20:23 + | +LL | fn takes_mytype(_: MyType) {} + | ------------ ^^^^^^^^^ this parameter takes ownership of the value + | | + | in this function +note: if `MyType` implemented `Clone`, you could clone the value + --> $DIR/on_move_with_format.rs:14:1 + | +LL | struct MyType { + | ^^^^^^^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_mytype(mytype); + | ------ you could clone this value + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.rs new file mode 100644 index 000000000000..1af3daf0f315 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.rs @@ -0,0 +1,22 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "first message", + label = "first label", +)] +#[diagnostic::on_move( + message = "second message", + //~^ WARN `message` is ignored due to previous definition of `message` [malformed_diagnostic_attributes] + label = "second label", + //~^ WARN `label` is ignored due to previous definition of `label` [malformed_diagnostic_attributes] +)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR first message +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.stderr new file mode 100644 index 000000000000..f02f97014981 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_duplicated_options.stderr @@ -0,0 +1,49 @@ +warning: `message` is ignored due to previous definition of `message` + --> $DIR/report_warning_on_duplicated_options.rs:8:5 + | +LL | message = "first message", + | ------------------------- `message` is first declared here +... +LL | message = "second message", + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `message` is later redundantly declared here + | + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: `label` is ignored due to previous definition of `label` + --> $DIR/report_warning_on_duplicated_options.rs:10:5 + | +LL | label = "first label", + | --------------------- `label` is first declared here +... +LL | label = "second label", + | ^^^^^^^^^^^^^^^^^^^^^^ `label` is later redundantly declared here + +error[E0382]: first message + --> $DIR/report_warning_on_duplicated_options.rs:20:15 + | +LL | let foo = Foo; + | --- first label +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_duplicated_options.rs:15:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_duplicated_options.rs:13:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 2 warnings emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.rs new file mode 100644 index 000000000000..f403ad1ff480 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.rs @@ -0,0 +1,18 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "Foo {Baz}", + //~^WARN unknown parameter `Baz` + label = "Bar", +)] +#[derive(Debug)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.stderr new file mode 100644 index 000000000000..efb43c3562a4 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_formats.stderr @@ -0,0 +1,38 @@ +warning: unknown parameter `Baz` + --> $DIR/report_warning_on_invalid_formats.rs:4:21 + | +LL | message = "Foo {Baz}", + | ^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: Foo {Baz} + --> $DIR/report_warning_on_invalid_formats.rs:16:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_invalid_formats.rs:11:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_invalid_formats.rs:9:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.rs new file mode 100644 index 000000000000..2050403210d5 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.rs @@ -0,0 +1,14 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move = "foo"] +//~^WARN missing options for `on_move` attribute [malformed_diagnostic_attributes] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR use of moved value: `foo` +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.stderr new file mode 100644 index 000000000000..39992b02e580 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_invalid_meta_item_syntax.stderr @@ -0,0 +1,38 @@ +warning: missing options for `on_move` attribute + --> $DIR/report_warning_on_invalid_meta_item_syntax.rs:3:1 + | +LL | #[diagnostic::on_move = "foo"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least one of the `message`, `note` and `label` options are expected + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: use of moved value: `foo` + --> $DIR/report_warning_on_invalid_meta_item_syntax.rs:12:15 + | +LL | let foo = Foo; + | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_invalid_meta_item_syntax.rs:7:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_invalid_meta_item_syntax.rs:5:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.rs new file mode 100644 index 000000000000..b5bb103d3f27 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.rs @@ -0,0 +1,18 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( +//~^ WARN expected a literal or missing delimiter [malformed_diagnostic_attributes] +//~| HELP only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma + message = "Foo" + label = "Bar", +)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR use of moved value: `foo` +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.stderr new file mode 100644 index 000000000000..00d0657bfcbd --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_delimiters.stderr @@ -0,0 +1,44 @@ +warning: expected a literal or missing delimiter + --> $DIR/report_warning_on_malformed_options_without_delimiters.rs:3:22 + | +LL | #[diagnostic::on_move( + | ______________________^ +LL | | +LL | | +LL | | message = "Foo" +LL | | label = "Bar", +LL | | )] + | |_^ + | + = help: only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: use of moved value: `foo` + --> $DIR/report_warning_on_malformed_options_without_delimiters.rs:16:15 + | +LL | let foo = Foo; + | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_malformed_options_without_delimiters.rs:11:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_malformed_options_without_delimiters.rs:9:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.rs new file mode 100644 index 000000000000..c676b87ad857 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.rs @@ -0,0 +1,18 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( +//~^ WARN expected a literal or missing delimiter [malformed_diagnostic_attributes] +//~| HELP only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma + message = Foo, + label = "Bar", +)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR use of moved value: `foo` +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.stderr new file mode 100644 index 000000000000..6dc230b68c5f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_malformed_options_without_literals.stderr @@ -0,0 +1,44 @@ +warning: expected a literal or missing delimiter + --> $DIR/report_warning_on_malformed_options_without_literals.rs:3:22 + | +LL | #[diagnostic::on_move( + | ______________________^ +LL | | +LL | | +LL | | message = Foo, +LL | | label = "Bar", +LL | | )] + | |_^ + | + = help: only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: use of moved value: `foo` + --> $DIR/report_warning_on_malformed_options_without_literals.rs:16:15 + | +LL | let foo = Foo; + | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_malformed_options_without_literals.rs:11:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_malformed_options_without_literals.rs:9:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.rs new file mode 100644 index 000000000000..e5603fd24ec9 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.rs @@ -0,0 +1,14 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move] +//~^WARN missing options for `on_move` attribute [malformed_diagnostic_attributes] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR use of moved value: `foo` +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.stderr new file mode 100644 index 000000000000..f4e6d69faecb --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_missing_options.stderr @@ -0,0 +1,38 @@ +warning: missing options for `on_move` attribute + --> $DIR/report_warning_on_missing_options.rs:3:1 + | +LL | #[diagnostic::on_move] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least one of the `message`, `note` and `label` options are expected + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: use of moved value: `foo` + --> $DIR/report_warning_on_missing_options.rs:12:15 + | +LL | let foo = Foo; + | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_missing_options.rs:7:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_missing_options.rs:5:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.rs new file mode 100644 index 000000000000..33e78ea5fc1f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.rs @@ -0,0 +1,23 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "Foo", + label = "Bar", +)] +struct Foo; + +#[diagnostic::on_move( +//~^WARN `#[diagnostic::on_move]` can only be applied to enums, structs or unions + message = "Foo", + label = "Bar", +)] +trait MyTrait {} + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.stderr new file mode 100644 index 000000000000..29c987fba48f --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_non_adt.stderr @@ -0,0 +1,41 @@ +warning: `#[diagnostic::on_move]` can only be applied to enums, structs or unions + --> $DIR/report_warning_on_non_adt.rs:9:1 + | +LL | / #[diagnostic::on_move( +LL | | +LL | | message = "Foo", +LL | | label = "Bar", +LL | | )] + | |__^ + | + = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: Foo + --> $DIR/report_warning_on_non_adt.rs:21:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_non_adt.rs:16:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_non_adt.rs:7:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.rs b/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.rs new file mode 100644 index 000000000000..651f6184cfac --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.rs @@ -0,0 +1,19 @@ +#![feature(diagnostic_on_move)] + +#[diagnostic::on_move( + message = "Foo", + label = "Bar", + baz="Baz" + //~^WARN unknown or malformed `on_move` attribute + //~|HELP only `message`, `note` and `label` are allowed as options. Their values must be string literals +)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR Foo +} diff --git a/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.stderr b/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.stderr new file mode 100644 index 000000000000..a09b8a96d2d1 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_move/report_warning_on_unknown_options.stderr @@ -0,0 +1,38 @@ +warning: unknown or malformed `on_move` attribute + --> $DIR/report_warning_on_unknown_options.rs:6:5 + | +LL | baz="Baz" + | ^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options. Their values must be string literals + = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +error[E0382]: Foo + --> $DIR/report_warning_on_unknown_options.rs:17:15 + | +LL | let foo = Foo; + | --- Bar +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/report_warning_on_unknown_options.rs:12:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/report_warning_on_unknown_options.rs:10:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs index e64e60f30c0c..09c2a05cd009 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs @@ -26,12 +26,15 @@ impl Bar for i32 {} // cannot use special rustc_on_unimplement symbols // in the format string #[diagnostic::on_unimplemented( - message = "{from_desugaring}{direct}{cause}{integral}{integer}", + message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}", //~^WARN there is no parameter `from_desugaring` on trait `Baz` //~|WARN there is no parameter `direct` on trait `Baz` //~|WARN there is no parameter `cause` on trait `Baz` //~|WARN there is no parameter `integral` on trait `Baz` //~|WARN there is no parameter `integer` on trait `Baz` + //~|WARN there is no parameter `r#struct` on trait `Baz` + //~|WARN there is no parameter `r#enum` on trait `Baz` + //~|WARN there is no parameter `union` on trait `Baz` label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" //~^WARN there is no parameter `float` on trait `Baz` //~|WARN there is no parameter `_Self` on trait `Baz` @@ -52,5 +55,5 @@ fn main() { takes_bar(()); //~^ERROR the trait bound `(): Bar` is not satisfied takes_baz(()); - //~^ERROR {from_desugaring}{direct}{cause}{integral}{integer} + //~^ERROR {from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union} } diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr index 862f805e15ba..58d2bdbfc4d6 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr @@ -9,7 +9,7 @@ LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl warning: there is no parameter `from_desugaring` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:17 | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}", | ^^^^^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -18,7 +18,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", warning: there is no parameter `direct` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:34 | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}", | ^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -26,7 +26,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", warning: there is no parameter `cause` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:42 | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}", | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -34,7 +34,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", warning: there is no parameter `integral` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:49 | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}", | ^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -42,13 +42,37 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", warning: there is no parameter `integer` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:59 | -LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}", +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}", | ^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument +warning: there is no parameter `r#struct` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:68 + | +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}", + | ^^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: there is no parameter `r#enum` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:76 + | +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}", + | ^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: there is no parameter `union` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:82 + | +LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}", + | ^^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + warning: there is no parameter `float` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:15 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ @@ -56,7 +80,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `_Self` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:22 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ @@ -64,7 +88,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `crate_local` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:29 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:29 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^^^^^^^ @@ -72,7 +96,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `Trait` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:42 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:42 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^ @@ -80,7 +104,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `ItemContext` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:49 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:49 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^^^^^^^^ @@ -88,7 +112,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `This` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:62 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:62 | LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" | ^^^^ @@ -129,7 +153,7 @@ LL | #[diagnostic::on_unimplemented = "Message"] = help: only `message`, `note` and `label` are allowed as options error[E0277]: trait has `()` and `i32` as params - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:50:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:53:15 | LL | takes_foo(()); | --------- ^^ trait has `()` and `i32` as params @@ -144,13 +168,13 @@ help: this trait has no implementations, consider adding one LL | trait Foo {} | ^^^^^^^^^^^^ note: required by a bound in `takes_foo` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:45:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:48:22 | LL | fn takes_foo(_: impl Foo) {} | ^^^^^^^^ required by this bound in `takes_foo` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:52:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:55:15 | LL | takes_bar(()); | --------- ^^ the trait `Bar` is not implemented for `()` @@ -163,13 +187,13 @@ help: the trait `Bar` is implemented for `i32` LL | impl Bar for i32 {} | ^^^^^^^^^^^^^^^^ note: required by a bound in `takes_bar` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:46:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:49:22 | LL | fn takes_bar(_: impl Bar) {} | ^^^ required by this bound in `takes_bar` -error[E0277]: {from_desugaring}{direct}{cause}{integral}{integer} - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:54:15 +error[E0277]: {from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union} + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:57:15 | LL | takes_baz(()); | --------- ^^ {float}{_Self}{crate_local}{Trait}{ItemContext}{This} @@ -178,16 +202,16 @@ LL | takes_baz(()); | = help: the trait `Baz` is not implemented for `()` help: this trait has no implementations, consider adding one - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:43:1 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:46:1 | LL | trait Baz {} | ^^^^^^^^^ note: required by a bound in `takes_baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:47:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:50:22 | LL | fn takes_baz(_: impl Baz) {} | ^^^ required by this bound in `takes_baz` -error: aborting due to 3 previous errors; 16 warnings emitted +error: aborting due to 3 previous errors; 19 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs new file mode 100644 index 000000000000..a1f3b1fbbc86 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.rs @@ -0,0 +1,18 @@ +//! This is an unusual feature gate test, as it doesn't test the feature +//! gate, but the fact that not adding the feature gate will cause the +//! diagnostic to not emit the custom diagnostic message +//! +#[diagnostic::on_move( + message = "Foo" +)] +#[derive(Debug)] +struct Foo; + +fn takes_foo(_: Foo) {} + +fn main() { + let foo = Foo; + takes_foo(foo); + let bar = foo; + //~^ERROR use of moved value: `foo` +} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr new file mode 100644 index 000000000000..9ba6f272cf92 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-on-move.stderr @@ -0,0 +1,29 @@ +error[E0382]: use of moved value: `foo` + --> $DIR/feature-gate-diagnostic-on-move.rs:16:15 + | +LL | let foo = Foo; + | --- move occurs because `foo` has type `Foo`, which does not implement the `Copy` trait +LL | takes_foo(foo); + | --- value moved here +LL | let bar = foo; + | ^^^ value used here after move + | +note: consider changing this parameter type in function `takes_foo` to borrow instead if owning the value isn't necessary + --> $DIR/feature-gate-diagnostic-on-move.rs:11:17 + | +LL | fn takes_foo(_: Foo) {} + | --------- ^^^ this parameter takes ownership of the value + | | + | in this function +note: if `Foo` implemented `Clone`, you could clone the value + --> $DIR/feature-gate-diagnostic-on-move.rs:9:1 + | +LL | struct Foo; + | ^^^^^^^^^^ consider implementing `Clone` for this type +... +LL | takes_foo(foo); + | --- you could clone this value + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/hygiene/unpretty-debug-lifetimes.stdout b/tests/ui/hygiene/unpretty-debug-lifetimes.stdout index 28a5c70a02d7..689453326c0b 100644 --- a/tests/ui/hygiene/unpretty-debug-lifetimes.stdout +++ b/tests/ui/hygiene/unpretty-debug-lifetimes.stdout @@ -7,15 +7,17 @@ // Don't break whenever Symbol numbering changes //@ normalize-stdout: "\d+#" -> "0#" -#![feature /* 0#0 */(decl_macro)] -#![feature /* 0#0 */(no_core)] +#![feature /* 0#0 */(decl_macro /* 0#0 */)] +#![feature /* 0#0 */(no_core /* 0#0 */)] #![no_core /* 0#0 */] macro lifetime_hygiene /* 0#0 */ { - ($f:ident<$a:lifetime>) => { fn $f<$a, 'a>() {} } + ($f /* 0#0 */:ident /* 0#0 */<$a /* 0#0 */:lifetime /* 0#0 */>) + => + { fn /* 0#0 */ $f /* 0#0 */<$a /* 0#0 */, 'a /* 0#0 */>() {} } } fn f /* 0#0 */<'a /* 0#0 */, 'a /* 0#1 */>() {} diff --git a/tests/ui/hygiene/unpretty-debug-metavars.rs b/tests/ui/hygiene/unpretty-debug-metavars.rs new file mode 100644 index 000000000000..41bf75cd0d98 --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-metavars.rs @@ -0,0 +1,26 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test for token hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, metavar parameters in macro-generated macro_rules! definitions +// were missing hygiene annotations, making identical `$marg` bindings +// indistinguishable. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature(no_core)] +#![no_core] + +macro_rules! make_macro { + (@inner $name:ident ($dol:tt) $a:ident) => { + macro_rules! $name { + ($dol $a : expr, $dol marg : expr) => {} + } + }; + ($name:ident) => { + make_macro!{@inner $name ($) marg} + }; +} + +make_macro!(add2); diff --git a/tests/ui/hygiene/unpretty-debug-metavars.stdout b/tests/ui/hygiene/unpretty-debug-metavars.stdout new file mode 100644 index 000000000000..89658bc909a1 --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-metavars.stdout @@ -0,0 +1,53 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test for token hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, metavar parameters in macro-generated macro_rules! definitions +// were missing hygiene annotations, making identical `$marg` bindings +// indistinguishable. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature /* 0#0 */(no_core /* 0#0 */)] +#![no_core /* 0#0 */] + +macro_rules! make_macro + /* + 0#0 + */ { + (@inner /* 0#0 */ $name /* 0#0 */:ident /* 0#0 + */($dol /* 0#0 */:tt /* 0#0 */) $a /* 0#0 */:ident /* 0#0 */) + => + { + macro_rules /* 0#0 */! $name /* 0#0 */ + { + ($dol /* 0#0 */ $a /* 0#0 */ : expr /* 0#0 */, $dol /* + 0#0 */ marg /* 0#0 */ : expr /* 0#0 */) => {} + } + }; ($name /* 0#0 */:ident /* 0#0 */) => + { + make_macro /* 0#0 + */!{@inner /* 0#0 */ $name /* 0#0 */($) marg /* 0#0 */} + }; +} +macro_rules! add2 + /* + 0#0 + */ { + ($ marg /* 0#1 */ : expr /* 0#2 */, $marg /* 0#2 */ : expr /* + 0#2 */) => {} +} + + +/* +Expansions: +crate0::{{expn0}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Root +crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "make_macro") +crate0::{{expn2}}: parent: crate0::{{expn1}}, call_site_ctxt: #1, def_site_ctxt: #0, kind: Macro(Bang, "make_macro") + +SyntaxContexts: +#0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque) +#1: parent: #0, outer_mark: (crate0::{{expn1}}, SemiOpaque) +#2: parent: #0, outer_mark: (crate0::{{expn2}}, SemiOpaque) +*/ diff --git a/tests/ui/hygiene/unpretty-debug-shadow.rs b/tests/ui/hygiene/unpretty-debug-shadow.rs new file mode 100644 index 000000000000..2aa68c8aec11 --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-shadow.rs @@ -0,0 +1,20 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test for token hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, tokens in macro_rules! bodies were missing hygiene annotations, +// making it impossible to see how a macro's reference to a shadowed variable +// is distinguished from the shadowing binding. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature(no_core)] +#![no_core] + +fn f() { + let x = 0; + macro_rules! use_x { () => { x }; } + let x = 1; + use_x!(); +} diff --git a/tests/ui/hygiene/unpretty-debug-shadow.stdout b/tests/ui/hygiene/unpretty-debug-shadow.stdout new file mode 100644 index 000000000000..36076b6a968f --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-shadow.stdout @@ -0,0 +1,30 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test for token hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, tokens in macro_rules! bodies were missing hygiene annotations, +// making it impossible to see how a macro's reference to a shadowed variable +// is distinguished from the shadowing binding. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature /* 0#0 */(no_core /* 0#0 */)] +#![no_core /* 0#0 */] + +fn f /* 0#0 */() { + let x /* 0#0 */ = 0; + macro_rules! use_x /* 0#0 */ { () => { x /* 0#0 */ }; } + let x /* 0#0 */ = 1; + x /* 0#1 */; +} + +/* +Expansions: +crate0::{{expn0}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Root +crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "use_x") + +SyntaxContexts: +#0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque) +#1: parent: #0, outer_mark: (crate0::{{expn1}}, SemiOpaque) +*/ diff --git a/tests/ui/hygiene/unpretty-debug.stdout b/tests/ui/hygiene/unpretty-debug.stdout index f35bd7a7cb2c..ac6051e2d542 100644 --- a/tests/ui/hygiene/unpretty-debug.stdout +++ b/tests/ui/hygiene/unpretty-debug.stdout @@ -5,10 +5,16 @@ //@ normalize-stdout: "\d+#" -> "0#" // minimal junk -#![feature /* 0#0 */(no_core)] +#![feature /* 0#0 */(no_core /* 0#0 */)] #![no_core /* 0#0 */] -macro_rules! foo /* 0#0 */ { ($x: ident) => { y + $x } } +macro_rules! foo + /* + 0#0 + */ { + ($x /* 0#0 */: ident /* 0#0 */) => + { y /* 0#0 */ + $x /* 0#0 */ } +} fn bar /* 0#0 */() { let x /* 0#0 */ = 1; diff --git a/tests/ui/impl-restriction/restriction_resolution_errors.rs b/tests/ui/impl-restriction/restriction_resolution_errors.rs new file mode 100644 index 000000000000..b36f2cf9bdfb --- /dev/null +++ b/tests/ui/impl-restriction/restriction_resolution_errors.rs @@ -0,0 +1,85 @@ +#![feature(impl_restriction)] +#![expect(incomplete_features)] + +pub mod a { + pub enum E {} + pub mod d {} + pub mod b { + pub mod c {} + + // We do not use crate-relative paths here, since we follow the + // "uniform paths" approach used for type/expression paths. + pub impl(in a::b) trait T1 {} //~ ERROR cannot find module or crate `a` in this scope [E0433] + + pub impl(in ::std) trait T2 {} //~ ERROR trait implementation can only be restricted to ancestor modules + + pub impl(in self::c) trait T3 {} //~ ERROR trait implementation can only be restricted to ancestor modules + + pub impl(in super::d) trait T4 {} //~ ERROR trait implementation can only be restricted to ancestor modules + + pub impl(in crate::c) trait T5 {} //~ ERROR cannot find module `c` in the crate root [E0433] + + pub impl(in super::E) trait T6 {} //~ ERROR expected module, found enum `super::E` [E0577] + + pub impl(in super::super::super) trait T7 {} //~ ERROR too many leading `super` keywords [E0433] + + // OK paths + pub impl(crate) trait T8 {} + pub impl(self) trait T9 {} + pub impl(super) trait T10 {} + pub impl(in crate::a) trait T11 {} + pub impl(in super::super) trait T12 {} + + // Check if we can resolve paths referring to modules declared later. + pub impl(in self::f) trait L1 {} //~ ERROR trait implementation can only be restricted to ancestor modules + + pub impl(in super::G) trait L2 {} //~ ERROR expected module, found enum `super::G` [E0577] + + pub impl(in super::h) trait L3 {} //~ ERROR trait implementation can only be restricted to ancestor modules + + pub mod f {} + } + + pub enum G {} + pub mod h {} +} + +pub impl(in crate::a) trait T13 {} //~ ERROR trait implementation can only be restricted to ancestor modules + +pub impl(in crate::a::E) trait T14 {} //~ ERROR expected module, found enum `crate::a::E` [E0577] + +pub impl(crate) trait T15 {} +pub impl(self) trait T16 {} + +pub impl(super) trait T17 {} //~ ERROR too many leading `super` keywords [E0433] + +// Check if we can resolve paths referring to modules declared later. +pub impl(in crate::j) trait L4 {} //~ ERROR trait implementation can only be restricted to ancestor modules + +pub impl(in crate::I) trait L5 {} //~ ERROR expected module, found enum `crate::I` [E0577] + +pub enum I {} +pub mod j {} + +// Check if we can resolve `use`d paths. +mod m1 { + pub impl(in crate::m2) trait U1 {} // OK +} + +use m1 as m2; + +mod m3 { + mod m4 { + pub impl(in crate::m2) trait U2 {} //~ ERROR trait implementation can only be restricted to ancestor modules + pub impl(in m6) trait U3 {} // OK + pub impl(in m6::m5) trait U4 {} //~ ERROR trait implementation can only be restricted to ancestor modules + pub impl(in m7) trait U5 {} //~ ERROR expected module, found enum `m7` [E0577] + + use crate::m3 as m6; + use crate::m3::E as m7; + } + mod m5 {} + pub enum E {} +} + +fn main() {} diff --git a/tests/ui/impl-restriction/restriction_resolution_errors.stderr b/tests/ui/impl-restriction/restriction_resolution_errors.stderr new file mode 100644 index 000000000000..540803285c1b --- /dev/null +++ b/tests/ui/impl-restriction/restriction_resolution_errors.stderr @@ -0,0 +1,140 @@ +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:14:21 + | +LL | pub impl(in ::std) trait T2 {} + | ^^^^^ + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:16:21 + | +LL | pub impl(in self::c) trait T3 {} + | ^^^^^^^ + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:18:21 + | +LL | pub impl(in super::d) trait T4 {} + | ^^^^^^^^ + +error[E0433]: too many leading `super` keywords + --> $DIR/restriction_resolution_errors.rs:24:35 + | +LL | pub impl(in super::super::super) trait T7 {} + | ^^^^^ there are too many leading `super` keywords + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:34:21 + | +LL | pub impl(in self::f) trait L1 {} + | ^^^^^^^ + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:38:21 + | +LL | pub impl(in super::h) trait L3 {} + | ^^^^^^^^ + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:47:13 + | +LL | pub impl(in crate::a) trait T13 {} + | ^^^^^^^^ + +error[E0433]: too many leading `super` keywords + --> $DIR/restriction_resolution_errors.rs:54:10 + | +LL | pub impl(super) trait T17 {} + | ^^^^^ there are too many leading `super` keywords + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:57:13 + | +LL | pub impl(in crate::j) trait L4 {} + | ^^^^^^^^ + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:73:21 + | +LL | pub impl(in crate::m2) trait U2 {} + | ^^^^^^^^^ + +error: trait implementation can only be restricted to ancestor modules + --> $DIR/restriction_resolution_errors.rs:75:21 + | +LL | pub impl(in m6::m5) trait U4 {} + | ^^^^^^ + +error[E0433]: cannot find module or crate `a` in this scope + --> $DIR/restriction_resolution_errors.rs:12:21 + | +LL | pub impl(in a::b) trait T1 {} + | ^ use of unresolved module or unlinked crate `a` + | +help: there is a crate or module with a similar name + | +LL - pub impl(in a::b) trait T1 {} +LL + pub impl(in c::b) trait T1 {} + | +help: consider importing this module + | +LL + use a; + | + +error[E0433]: cannot find module `c` in the crate root + --> $DIR/restriction_resolution_errors.rs:20:28 + | +LL | pub impl(in crate::c) trait T5 {} + | ^ not found in the crate root + +error[E0577]: expected module, found enum `super::E` + --> $DIR/restriction_resolution_errors.rs:22:21 + | +LL | pub impl(in super::E) trait T6 {} + | ^^^^^^^^ not a module + +error[E0577]: expected module, found enum `super::G` + --> $DIR/restriction_resolution_errors.rs:36:21 + | +LL | pub impl(in super::G) trait L2 {} + | ^^^^^^^^ not a module + +error[E0577]: expected module, found enum `crate::a::E` + --> $DIR/restriction_resolution_errors.rs:49:13 + | +LL | pub mod b { + | --------- similarly named module `b` defined here +... +LL | pub impl(in crate::a::E) trait T14 {} + | ^^^^^^^^^^^ + | +help: a module with a similar name exists + | +LL - pub impl(in crate::a::E) trait T14 {} +LL + pub impl(in crate::a::b) trait T14 {} + | + +error[E0577]: expected module, found enum `crate::I` + --> $DIR/restriction_resolution_errors.rs:59:13 + | +LL | pub mod a { + | --------- similarly named module `a` defined here +... +LL | pub impl(in crate::I) trait L5 {} + | ^^^^^^^^ + | +help: a module with a similar name exists + | +LL - pub impl(in crate::I) trait L5 {} +LL + pub impl(in crate::a) trait L5 {} + | + +error[E0577]: expected module, found enum `m7` + --> $DIR/restriction_resolution_errors.rs:76:21 + | +LL | pub impl(in m7) trait U5 {} + | ^^ not a module + +error: aborting due to 18 previous errors + +Some errors have detailed explanations: E0433, E0577. +For more information about an error, try `rustc --explain E0433`. diff --git a/tests/ui/impl-trait/unsized_coercion.next.stderr b/tests/ui/impl-trait/unsized_coercion.next.stderr deleted file mode 100644 index bea5ddb0aefc..000000000000 --- a/tests/ui/impl-trait/unsized_coercion.next.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time - --> $DIR/unsized_coercion.rs:14:17 - | -LL | let x = hello(); - | ^^^^^^^ doesn't have a size known at compile-time - | - = help: the trait `Sized` is not implemented for `dyn Trait` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/unsized_coercion.rs b/tests/ui/impl-trait/unsized_coercion.rs index 2cbf0d25d7ec..f77f2198be0e 100644 --- a/tests/ui/impl-trait/unsized_coercion.rs +++ b/tests/ui/impl-trait/unsized_coercion.rs @@ -3,7 +3,7 @@ //@ revisions: next old //@[next] compile-flags: -Znext-solver -//@[old] check-pass +//@ check-pass trait Trait {} @@ -12,7 +12,6 @@ impl Trait for u32 {} fn hello() -> Box { if true { let x = hello(); - //[next]~^ ERROR: the size for values of type `dyn Trait` cannot be known at compilation time let y: Box = x; } Box::new(1u32) diff --git a/tests/ui/impl-trait/unsized_coercion3.next.stderr b/tests/ui/impl-trait/unsized_coercion3.next.stderr index a480a69a3864..db758761d795 100644 --- a/tests/ui/impl-trait/unsized_coercion3.next.stderr +++ b/tests/ui/impl-trait/unsized_coercion3.next.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `dyn Send: Trait` is not satisfied - --> $DIR/unsized_coercion3.rs:13:17 + --> $DIR/unsized_coercion3.rs:14:17 | LL | let x = hello(); | ^^^^^^^ the trait `Trait` is not implemented for `dyn Send` @@ -9,7 +9,37 @@ help: the trait `Trait` is implemented for `u32` | LL | impl Trait for u32 {} | ^^^^^^^^^^^^^^^^^^ +note: required by a bound in `Box` + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL -error: aborting due to 1 previous error +error[E0308]: mismatched types + --> $DIR/unsized_coercion3.rs:19:5 + | +LL | fn hello() -> Box { + | ------------------------ + | | | + | | the expected opaque type + | expected `Box` because of return type +... +LL | Box::new(1u32) + | ^^^^^^^^^^^^^^ types differ + | + = note: expected struct `Box` + found struct `Box` -For more information about this error, try `rustc --explain E0277`. +error[E0277]: the trait bound `dyn Send: Trait` is not satisfied + --> $DIR/unsized_coercion3.rs:11:1 + | +LL | fn hello() -> Box { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `dyn Send` + | +help: the trait `Trait` is implemented for `u32` + --> $DIR/unsized_coercion3.rs:9:1 + | +LL | impl Trait for u32 {} + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/unsized_coercion3.old.stderr b/tests/ui/impl-trait/unsized_coercion3.old.stderr index 52a72b84a8dd..3bb9f9c20951 100644 --- a/tests/ui/impl-trait/unsized_coercion3.old.stderr +++ b/tests/ui/impl-trait/unsized_coercion3.old.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `impl Trait + ?Sized` cannot be known at compilation time - --> $DIR/unsized_coercion3.rs:15:32 + --> $DIR/unsized_coercion3.rs:16:32 | LL | let y: Box = x; | ^ doesn't have a size known at compile-time diff --git a/tests/ui/impl-trait/unsized_coercion3.rs b/tests/ui/impl-trait/unsized_coercion3.rs index ebfbb2955de5..c1dd5350e229 100644 --- a/tests/ui/impl-trait/unsized_coercion3.rs +++ b/tests/ui/impl-trait/unsized_coercion3.rs @@ -9,6 +9,7 @@ trait Trait {} impl Trait for u32 {} fn hello() -> Box { + //[next]~^ ERROR: the trait bound `dyn Send: Trait` is not satisfied if true { let x = hello(); //[next]~^ ERROR: the trait bound `dyn Send: Trait` is not satisfied @@ -16,6 +17,7 @@ fn hello() -> Box { //[old]~^ ERROR: the size for values of type `impl Trait + ?Sized` cannot be know } Box::new(1u32) + //[next]~^ ERROR: mismatched types } fn main() {} diff --git a/tests/ui/inference/cannot-infer-iterator-sum-return-type.fixed b/tests/ui/inference/cannot-infer-iterator-sum-return-type.fixed new file mode 100644 index 000000000000..9a494391f0e4 --- /dev/null +++ b/tests/ui/inference/cannot-infer-iterator-sum-return-type.fixed @@ -0,0 +1,13 @@ +//@ run-rustfix +fn main() { + let v = vec![1, 2]; + let sum: i32 = v //~ ERROR: type annotations needed + .iter() + .map(|val| *val) + .sum(); // `sum::` needs `T` to be specified + // In this case any integer would fit, but we resolve to `i32` because that's what `{integer}` + // got coerced to. If the user needs further hinting that they can change the integer type, that + // can come from other suggestions. (#100802) + let bool = sum > 0; + assert_eq!(bool, true); +} diff --git a/tests/ui/inference/cannot-infer-iterator-sum-return-type.rs b/tests/ui/inference/cannot-infer-iterator-sum-return-type.rs new file mode 100644 index 000000000000..013a2f914858 --- /dev/null +++ b/tests/ui/inference/cannot-infer-iterator-sum-return-type.rs @@ -0,0 +1,13 @@ +//@ run-rustfix +fn main() { + let v = vec![1, 2]; + let sum = v //~ ERROR: type annotations needed + .iter() + .map(|val| *val) + .sum(); // `sum::` needs `T` to be specified + // In this case any integer would fit, but we resolve to `i32` because that's what `{integer}` + // got coerced to. If the user needs further hinting that they can change the integer type, that + // can come from other suggestions. (#100802) + let bool = sum > 0; + assert_eq!(bool, true); +} diff --git a/tests/ui/inference/cannot-infer-iterator-sum-return-type.stderr b/tests/ui/inference/cannot-infer-iterator-sum-return-type.stderr new file mode 100644 index 000000000000..594b6f0181db --- /dev/null +++ b/tests/ui/inference/cannot-infer-iterator-sum-return-type.stderr @@ -0,0 +1,26 @@ +error[E0283]: type annotations needed + --> $DIR/cannot-infer-iterator-sum-return-type.rs:4:9 + | +LL | let sum = v + | ^^^ +... +LL | .sum(); // `sum::` needs `T` to be specified + | --- type must be known at this point + | + = note: the type must implement `Sum` +help: the trait `Sum` is implemented for `i32` + --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL + ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL + | + = note: in this macro invocation +note: required by a bound in `std::iter::Iterator::sum` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + = note: this error originates in the macro `integer_sum_product` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider giving `sum` an explicit type, where the type for type parameter `S` is specified + | +LL | let sum: i32 = v + | +++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/kindck/kindck-impl-type-params-2.rs b/tests/ui/kindck/kindck-impl-type-params-2.rs deleted file mode 100644 index 8b0771985dc3..000000000000 --- a/tests/ui/kindck/kindck-impl-type-params-2.rs +++ /dev/null @@ -1,15 +0,0 @@ -trait Foo { -} - - - -impl Foo for T { -} - -fn take_param(foo: &T) { } - -fn main() { - let x: Box<_> = Box::new(3); - take_param(&x); - //~^ ERROR the trait bound `Box<{integer}>: Foo` is not satisfied -} diff --git a/tests/ui/kindck/kindck-impl-type-params-2.stderr b/tests/ui/kindck/kindck-impl-type-params-2.stderr deleted file mode 100644 index 38dc94f9104d..000000000000 --- a/tests/ui/kindck/kindck-impl-type-params-2.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error[E0277]: the trait bound `Box<{integer}>: Foo` is not satisfied - --> $DIR/kindck-impl-type-params-2.rs:13:16 - | -LL | take_param(&x); - | ---------- ^^ the trait `Copy` is not implemented for `Box<{integer}>` - | | - | required by a bound introduced by this call - | -note: required for `Box<{integer}>` to implement `Foo` - --> $DIR/kindck-impl-type-params-2.rs:6:14 - | -LL | impl Foo for T { - | ---- ^^^ ^ - | | - | unsatisfied trait bound introduced here -note: required by a bound in `take_param` - --> $DIR/kindck-impl-type-params-2.rs:9:17 - | -LL | fn take_param(foo: &T) { } - | ^^^ required by this bound in `take_param` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/kindck/kindck-inherited-copy-bound.curr.stderr b/tests/ui/kindck/kindck-inherited-copy-bound.curr.stderr deleted file mode 100644 index 95048c4454b3..000000000000 --- a/tests/ui/kindck/kindck-inherited-copy-bound.curr.stderr +++ /dev/null @@ -1,56 +0,0 @@ -error[E0277]: the trait bound `Box<{integer}>: Foo` is not satisfied - --> $DIR/kindck-inherited-copy-bound.rs:21:16 - | -LL | take_param(&x); - | ---------- ^^ the trait `Copy` is not implemented for `Box<{integer}>` - | | - | required by a bound introduced by this call - | -note: required for `Box<{integer}>` to implement `Foo` - --> $DIR/kindck-inherited-copy-bound.rs:14:14 - | -LL | impl Foo for T { - | ---- ^^^ ^ - | | - | unsatisfied trait bound introduced here -note: required by a bound in `take_param` - --> $DIR/kindck-inherited-copy-bound.rs:17:17 - | -LL | fn take_param(foo: &T) { } - | ^^^ required by this bound in `take_param` - -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/kindck-inherited-copy-bound.rs:28:19 - | -LL | let z = &x as &dyn Foo; - | ^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/kindck-inherited-copy-bound.rs:10:13 - | -LL | trait Foo : Copy { - | --- ^^^^ ...because it requires `Self: Sized` - | | - | this trait is not dyn compatible... - -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/kindck-inherited-copy-bound.rs:28:13 - | -LL | let z = &x as &dyn Foo; - | ^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/kindck-inherited-copy-bound.rs:10:13 - | -LL | trait Foo : Copy { - | --- ^^^^ ...because it requires `Self: Sized` - | | - | this trait is not dyn compatible... - = note: required for the cast from `&Box<{integer}>` to `&dyn Foo` - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0038, E0277. -For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/kindck/kindck-nonsendable-1.stderr b/tests/ui/kindck/kindck-nonsendable-1.stderr deleted file mode 100644 index 8bb784d1d496..000000000000 --- a/tests/ui/kindck/kindck-nonsendable-1.stderr +++ /dev/null @@ -1,25 +0,0 @@ -error[E0277]: `Rc` cannot be sent between threads safely - --> $DIR/kindck-nonsendable-1.rs:9:9 - | -LL | bar(move|| foo(x)); - | --- ------^^^^^^^ - | | | - | | `Rc` cannot be sent between threads safely - | | within this `{closure@$DIR/kindck-nonsendable-1.rs:9:9: 9:15}` - | required by a bound introduced by this call - | - = help: within `{closure@$DIR/kindck-nonsendable-1.rs:9:9: 9:15}`, the trait `Send` is not implemented for `Rc` -note: required because it's used within this closure - --> $DIR/kindck-nonsendable-1.rs:9:9 - | -LL | bar(move|| foo(x)); - | ^^^^^^ -note: required by a bound in `bar` - --> $DIR/kindck-nonsendable-1.rs:5:21 - | -LL | fn bar(_: F) { } - | ^^^^ required by this bound in `bar` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/kindck/kindck-send-object.rs b/tests/ui/kindck/kindck-send-object.rs deleted file mode 100644 index f5d44246efe5..000000000000 --- a/tests/ui/kindck/kindck-send-object.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Test which of the builtin types are considered sendable. The tests -// in this file all test the "kind" violates detected during kindck. -// See all `regions-bounded-by-send.rs` - -fn assert_send() { } -trait Dummy { } -trait Message : Send { } - -// careful with object types, who knows what they close over... - -fn object_ref_with_static_bound_not_ok() { - assert_send::<&'static (dyn Dummy + 'static)>(); - //~^ ERROR `&'static (dyn Dummy + 'static)` cannot be sent between threads safely [E0277] -} - -fn box_object_with_no_bound_not_ok<'a>() { - assert_send::>(); - //~^ ERROR `dyn Dummy` cannot be sent between threads safely -} - -fn object_with_send_bound_ok() { - assert_send::<&'static (dyn Dummy + Sync)>(); - assert_send::>(); -} - -fn main() { } diff --git a/tests/ui/kindck/kindck-send-object.stderr b/tests/ui/kindck/kindck-send-object.stderr deleted file mode 100644 index b71d4029350e..000000000000 --- a/tests/ui/kindck/kindck-send-object.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error[E0277]: `&'static (dyn Dummy + 'static)` cannot be sent between threads safely - --> $DIR/kindck-send-object.rs:12:19 - | -LL | assert_send::<&'static (dyn Dummy + 'static)>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&'static (dyn Dummy + 'static)` cannot be sent between threads safely - | - = help: the trait `Sync` is not implemented for `(dyn Dummy + 'static)` - = note: required for `&'static (dyn Dummy + 'static)` to implement `Send` -note: required by a bound in `assert_send` - --> $DIR/kindck-send-object.rs:5:18 - | -LL | fn assert_send() { } - | ^^^^ required by this bound in `assert_send` - -error[E0277]: `dyn Dummy` cannot be sent between threads safely - --> $DIR/kindck-send-object.rs:17:19 - | -LL | assert_send::>(); - | ^^^^^^^^^^^^^^ `dyn Dummy` cannot be sent between threads safely - | - = help: the trait `Send` is not implemented for `dyn Dummy` - = note: required for `std::ptr::Unique` to implement `Send` -note: required because it appears within the type `Box` - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL -note: required by a bound in `assert_send` - --> $DIR/kindck-send-object.rs:5:18 - | -LL | fn assert_send() { } - | ^^^^ required by this bound in `assert_send` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/kindck/kindck-send-object1.rs b/tests/ui/kindck/kindck-send-object1.rs deleted file mode 100644 index 76a9fc6019ab..000000000000 --- a/tests/ui/kindck/kindck-send-object1.rs +++ /dev/null @@ -1,33 +0,0 @@ -// Test which object types are considered sendable. This test -// is broken into two parts because some errors occur in distinct -// phases in the compiler. See kindck-send-object2.rs as well! - -fn assert_send() { } -trait Dummy { } - -// careful with object types, who knows what they close over... -fn test51<'a>() { - assert_send::<&'a dyn Dummy>(); - //~^ ERROR `&'a (dyn Dummy + 'a)` cannot be sent between threads safely [E0277] -} -fn test52<'a>() { - assert_send::<&'a (dyn Dummy + Sync)>(); - //~^ ERROR: lifetime may not live long enough -} - -// ...unless they are properly bounded -fn test60() { - assert_send::<&'static (dyn Dummy + Sync)>(); -} -fn test61() { - assert_send::>(); -} - -// closure and object types can have lifetime bounds which make -// them not ok -fn test_71<'a>() { - assert_send::>(); - //~^ ERROR `(dyn Dummy + 'a)` cannot be sent between threads safely -} - -fn main() { } diff --git a/tests/ui/kindck/kindck-send-object1.stderr b/tests/ui/kindck/kindck-send-object1.stderr deleted file mode 100644 index 2184ae704673..000000000000 --- a/tests/ui/kindck/kindck-send-object1.stderr +++ /dev/null @@ -1,41 +0,0 @@ -error[E0277]: `&'a (dyn Dummy + 'a)` cannot be sent between threads safely - --> $DIR/kindck-send-object1.rs:10:19 - | -LL | assert_send::<&'a dyn Dummy>(); - | ^^^^^^^^^^^^^ `&'a (dyn Dummy + 'a)` cannot be sent between threads safely - | - = help: the trait `Sync` is not implemented for `(dyn Dummy + 'a)` - = note: required for `&'a (dyn Dummy + 'a)` to implement `Send` -note: required by a bound in `assert_send` - --> $DIR/kindck-send-object1.rs:5:18 - | -LL | fn assert_send() { } - | ^^^^ required by this bound in `assert_send` - -error[E0277]: `(dyn Dummy + 'a)` cannot be sent between threads safely - --> $DIR/kindck-send-object1.rs:29:19 - | -LL | assert_send::>(); - | ^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'a)` cannot be sent between threads safely - | - = help: the trait `Send` is not implemented for `(dyn Dummy + 'a)` - = note: required for `std::ptr::Unique<(dyn Dummy + 'a)>` to implement `Send` -note: required because it appears within the type `Box<(dyn Dummy + 'a)>` - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL -note: required by a bound in `assert_send` - --> $DIR/kindck-send-object1.rs:5:18 - | -LL | fn assert_send() { } - | ^^^^ required by this bound in `assert_send` - -error: lifetime may not live long enough - --> $DIR/kindck-send-object1.rs:14:5 - | -LL | fn test52<'a>() { - | -- lifetime `'a` defined here -LL | assert_send::<&'a (dyn Dummy + Sync)>(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/kindck/kindck-send-object2.rs b/tests/ui/kindck/kindck-send-object2.rs deleted file mode 100644 index d37074e65746..000000000000 --- a/tests/ui/kindck/kindck-send-object2.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Continue kindck-send-object1.rs. - -fn assert_send() { } -trait Dummy { } - -fn test50() { - assert_send::<&'static dyn Dummy>(); - //~^ ERROR `&'static (dyn Dummy + 'static)` cannot be sent between threads safely [E0277] -} - -fn test53() { - assert_send::>(); - //~^ ERROR `dyn Dummy` cannot be sent between threads safely -} - -// ...unless they are properly bounded -fn test60() { - assert_send::<&'static (dyn Dummy + Sync)>(); -} -fn test61() { - assert_send::>(); -} - -fn main() { } diff --git a/tests/ui/kindck/kindck-send-object2.stderr b/tests/ui/kindck/kindck-send-object2.stderr deleted file mode 100644 index 52a7055b4229..000000000000 --- a/tests/ui/kindck/kindck-send-object2.stderr +++ /dev/null @@ -1,33 +0,0 @@ -error[E0277]: `&'static (dyn Dummy + 'static)` cannot be sent between threads safely - --> $DIR/kindck-send-object2.rs:7:19 - | -LL | assert_send::<&'static dyn Dummy>(); - | ^^^^^^^^^^^^^^^^^^ `&'static (dyn Dummy + 'static)` cannot be sent between threads safely - | - = help: the trait `Sync` is not implemented for `(dyn Dummy + 'static)` - = note: required for `&'static (dyn Dummy + 'static)` to implement `Send` -note: required by a bound in `assert_send` - --> $DIR/kindck-send-object2.rs:3:18 - | -LL | fn assert_send() { } - | ^^^^ required by this bound in `assert_send` - -error[E0277]: `dyn Dummy` cannot be sent between threads safely - --> $DIR/kindck-send-object2.rs:12:19 - | -LL | assert_send::>(); - | ^^^^^^^^^^^^^^ `dyn Dummy` cannot be sent between threads safely - | - = help: the trait `Send` is not implemented for `dyn Dummy` - = note: required for `std::ptr::Unique` to implement `Send` -note: required because it appears within the type `Box` - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL -note: required by a bound in `assert_send` - --> $DIR/kindck-send-object2.rs:3:18 - | -LL | fn assert_send() { } - | ^^^^ required by this bound in `assert_send` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/kindck/kindck-send-owned.rs b/tests/ui/kindck/kindck-send-owned.rs deleted file mode 100644 index 65efb69041d5..000000000000 --- a/tests/ui/kindck/kindck-send-owned.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Test which of the builtin types are considered sendable. - -fn assert_send() { } - -// owned content are ok -fn test30() { assert_send::>(); } -fn test31() { assert_send::(); } -fn test32() { assert_send:: >(); } - -// but not if they own a bad thing -fn test40() { - assert_send::>(); - //~^ ERROR `*mut u8` cannot be sent between threads safely -} - -fn main() { } diff --git a/tests/ui/kindck/kindck-send-owned.stderr b/tests/ui/kindck/kindck-send-owned.stderr deleted file mode 100644 index c433d80cf140..000000000000 --- a/tests/ui/kindck/kindck-send-owned.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0277]: `*mut u8` cannot be sent between threads safely - --> $DIR/kindck-send-owned.rs:12:19 - | -LL | assert_send::>(); - | ^^^^^^^^^^^^ `*mut u8` cannot be sent between threads safely - | - = help: the trait `Send` is not implemented for `*mut u8` - = note: required for `std::ptr::Unique<*mut u8>` to implement `Send` -note: required because it appears within the type `Box<*mut u8>` - --> $SRC_DIR/alloc/src/boxed.rs:LL:COL -note: required by a bound in `assert_send` - --> $DIR/kindck-send-owned.rs:3:18 - | -LL | fn assert_send() { } - | ^^^^ required by this bound in `assert_send` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/kindck/kindck-send-unsafe.rs b/tests/ui/kindck/kindck-send-unsafe.rs deleted file mode 100644 index eb1f2a549b16..000000000000 --- a/tests/ui/kindck/kindck-send-unsafe.rs +++ /dev/null @@ -1,15 +0,0 @@ -extern crate core; - -fn assert_send() {} - -fn test70() { - assert_send::<*mut isize>(); - //~^ ERROR `*mut isize` cannot be sent between threads safely -} - -fn test71<'a>() { - assert_send::<*mut &'a isize>(); - //~^ ERROR `*mut &'a isize` cannot be sent between threads safely -} - -fn main() {} diff --git a/tests/ui/kindck/kindck-send-unsafe.stderr b/tests/ui/kindck/kindck-send-unsafe.stderr deleted file mode 100644 index f1a5054abbc4..000000000000 --- a/tests/ui/kindck/kindck-send-unsafe.stderr +++ /dev/null @@ -1,29 +0,0 @@ -error[E0277]: `*mut isize` cannot be sent between threads safely - --> $DIR/kindck-send-unsafe.rs:6:19 - | -LL | assert_send::<*mut isize>(); - | ^^^^^^^^^^ `*mut isize` cannot be sent between threads safely - | - = help: the trait `Send` is not implemented for `*mut isize` -note: required by a bound in `assert_send` - --> $DIR/kindck-send-unsafe.rs:3:19 - | -LL | fn assert_send() {} - | ^^^^ required by this bound in `assert_send` - -error[E0277]: `*mut &'a isize` cannot be sent between threads safely - --> $DIR/kindck-send-unsafe.rs:11:19 - | -LL | assert_send::<*mut &'a isize>(); - | ^^^^^^^^^^^^^^ `*mut &'a isize` cannot be sent between threads safely - | - = help: the trait `Send` is not implemented for `*mut &'a isize` -note: required by a bound in `assert_send` - --> $DIR/kindck-send-unsafe.rs:3:19 - | -LL | fn assert_send() {} - | ^^^^ required by this bound in `assert_send` - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/lint/for-loops-over-fallibles/auxiliary/external-macro-issue-148114.rs b/tests/ui/lint/for-loops-over-fallibles/auxiliary/external-macro-issue-148114.rs new file mode 100644 index 000000000000..68eff33cb3f1 --- /dev/null +++ b/tests/ui/lint/for-loops-over-fallibles/auxiliary/external-macro-issue-148114.rs @@ -0,0 +1,13 @@ +#[macro_export] +macro_rules! identity { + ($x:ident) => { + $x + }; +} + +#[macro_export] +macro_rules! do_loop { + ($x:ident) => { + for $crate::identity!($x) in $x {} + }; +} diff --git a/tests/ui/lint/for-loops-over-fallibles/external-macro-issue-148114.rs b/tests/ui/lint/for-loops-over-fallibles/external-macro-issue-148114.rs new file mode 100644 index 000000000000..ca3ac3b9a864 --- /dev/null +++ b/tests/ui/lint/for-loops-over-fallibles/external-macro-issue-148114.rs @@ -0,0 +1,14 @@ +//@ check-pass +//@ aux-build:external-macro-issue-148114.rs + +// This test ensures we do not trigger the lint on external macros +// ref. + +#![deny(for_loops_over_fallibles)] + +extern crate external_macro_issue_148114 as dep; + +fn main() { + let _name = Some(1); + dep::do_loop!(_name); +} diff --git a/tests/ui/lint/for-loops-over-falibles/macro-issue-140747.rs b/tests/ui/lint/for-loops-over-fallibles/macro-issue-140747.rs similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/macro-issue-140747.rs rename to tests/ui/lint/for-loops-over-fallibles/macro-issue-140747.rs diff --git a/tests/ui/lint/for-loops-over-falibles/macro-issue-140747.stderr b/tests/ui/lint/for-loops-over-fallibles/macro-issue-140747.stderr similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/macro-issue-140747.stderr rename to tests/ui/lint/for-loops-over-fallibles/macro-issue-140747.stderr diff --git a/tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.rs b/tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.rs new file mode 100644 index 000000000000..0fe88734099c --- /dev/null +++ b/tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.rs @@ -0,0 +1,21 @@ +// This test ensures that the `for-loops-over-fallibles` lint doesn't suggest +// removing `next`. +// ref. + +#![forbid(for_loops_over_fallibles)] +//~^ NOTE: the lint level is defined here + +fn main() { + macro_rules! mac { + (next $e:expr) => { + $e.iter().next() + }; + } + + for _ in mac!(next [1, 2]) {} + //~^ ERROR: for loop over an `Option`. This is more readably written as an `if let` statement + //~| NOTE: in this expansion of desugaring of `for` loop + //~| NOTE: in this expansion of desugaring of `for` loop + //~| HELP: to check pattern in a loop use `while let` + //~| HELP: consider using `if let` to clear intent +} diff --git a/tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.stderr b/tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.stderr new file mode 100644 index 000000000000..36848fffa439 --- /dev/null +++ b/tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.stderr @@ -0,0 +1,24 @@ +error: for loop over an `Option`. This is more readably written as an `if let` statement + --> $DIR/macro-iterator-next.rs:15:14 + | +LL | for _ in mac!(next [1, 2]) {} + | ^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/macro-iterator-next.rs:5:11 + | +LL | #![forbid(for_loops_over_fallibles)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: to check pattern in a loop use `while let` + | +LL - for _ in mac!(next [1, 2]) {} +LL + while let Some(_) = mac!(next [1, 2]) {} + | +help: consider using `if let` to clear intent + | +LL - for _ in mac!(next [1, 2]) {} +LL + if let Some(_) = mac!(next [1, 2]) {} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/lint/must_not_suspend/issue-89562.rs b/tests/ui/lint/must_not_suspend/mutex-guard-dropped-before-await.rs similarity index 67% rename from tests/ui/lint/must_not_suspend/issue-89562.rs rename to tests/ui/lint/must_not_suspend/mutex-guard-dropped-before-await.rs index 99a548130720..d1c48c3c5145 100644 --- a/tests/ui/lint/must_not_suspend/issue-89562.rs +++ b/tests/ui/lint/must_not_suspend/mutex-guard-dropped-before-await.rs @@ -1,9 +1,13 @@ +//! Regression test for + //@ edition:2018 //@ run-pass +#![feature(must_not_suspend)] +#![deny(must_not_suspend)] + use std::sync::Mutex; -// Copied from the issue. Allow-by-default for now, so run-pass pub async fn foo() { let foo = Mutex::new(1); let lock = foo.lock().unwrap(); diff --git a/tests/ui/macros/attr-expr.rs b/tests/ui/macros/attr-expr.rs new file mode 100644 index 000000000000..a2bee8728ac5 --- /dev/null +++ b/tests/ui/macros/attr-expr.rs @@ -0,0 +1,19 @@ +macro_rules! foo { + ($e:expr) => { + #[$e] + //~^ ERROR expected identifier, found metavariable + fn foo() {} + }; +} +foo!(inline); + +macro_rules! bar { + ($e:expr) => { + #[inline($e)] + //~^ ERROR expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `expr` metavariable + fn bar() {} + }; +} +bar!(always); + +fn main() {} diff --git a/tests/ui/macros/attr-expr.stderr b/tests/ui/macros/attr-expr.stderr new file mode 100644 index 000000000000..eaad41c44fb9 --- /dev/null +++ b/tests/ui/macros/attr-expr.stderr @@ -0,0 +1,24 @@ +error: expected identifier, found metavariable + --> $DIR/attr-expr.rs:3:11 + | +LL | #[$e] + | ^^ expected identifier, found metavariable +... +LL | foo!(inline); + | ------------ in this macro invocation + | + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found `expr` metavariable + --> $DIR/attr-expr.rs:12:18 + | +LL | #[inline($e)] + | ^^ +... +LL | bar!(always); + | ------------ in this macro invocation + | + = note: this error originates in the macro `bar` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui/macros/cfg-expr.rs b/tests/ui/macros/cfg-expr.rs new file mode 100644 index 000000000000..e9ca6e3d7b10 --- /dev/null +++ b/tests/ui/macros/cfg-expr.rs @@ -0,0 +1,44 @@ +//@ run-pass +macro_rules! foo { + ($e:expr, $n:ident) => { + #[cfg($e)] + macro_rules! $n { + () => {} + } + + #[cfg_attr($e, allow(non_snake_case))] + #[cfg($e)] + fn $n() { + #[cfg($e)] + $n!(); + } + + #[cfg_attr(not($e), allow(unused))] + #[cfg(not($e))] + fn $n() { + panic!() + } + } +} +foo!(true, BAR); +foo!(any(true, unix, target_pointer_width = "64"), baz); +foo!(target_pointer_width = "64", quux); +foo!(false, haha); + +fn main() { + BAR(); + BAR!(); + baz(); + baz!(); + #[cfg(target_pointer_width = "64")] + quux(); + #[cfg(target_pointer_width = "64")] + quux!(); + #[cfg(panic = "unwind")] + { + let result = std::panic::catch_unwind(|| { + haha(); + }); + assert!(result.is_err()); + } +} diff --git a/tests/ui/macros/cfg_attr-expr.rs b/tests/ui/macros/cfg_attr-expr.rs new file mode 100644 index 000000000000..1dab2cae59fa --- /dev/null +++ b/tests/ui/macros/cfg_attr-expr.rs @@ -0,0 +1,9 @@ +macro_rules! foo { + ($e:expr) => { + #[cfg_attr(true, $e)] + //~^ ERROR expected identifier, found metavariable + fn foo() {} + } +} +foo!(inline); +fn main() {} diff --git a/tests/ui/macros/cfg_attr-expr.stderr b/tests/ui/macros/cfg_attr-expr.stderr new file mode 100644 index 000000000000..a46ea104b939 --- /dev/null +++ b/tests/ui/macros/cfg_attr-expr.stderr @@ -0,0 +1,17 @@ +error: expected identifier, found metavariable + --> $DIR/cfg_attr-expr.rs:3:26 + | +LL | #[cfg_attr(true, $e)] + | -----------------^^-- + | | | + | | expected identifier, found metavariable + | help: must be of the form: `#[cfg_attr(predicate, attr1, attr2, ...)]` +... +LL | foo!(inline); + | ------------ in this macro invocation + | + = note: for more information, visit + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + diff --git a/tests/ui/macros/cfg_select-expr.rs b/tests/ui/macros/cfg_select-expr.rs new file mode 100644 index 000000000000..90e894562a16 --- /dev/null +++ b/tests/ui/macros/cfg_select-expr.rs @@ -0,0 +1,57 @@ +//@ run-pass +#![allow(unreachable_cfg_select_predicates)] + +macro_rules! foo { + ($e:expr, $n:ident) => { + cfg_select! { + $e => { + macro_rules! $n { + () => {} + } + } + _ => {} + } + + cfg_select! { + $e => { + #[cfg_attr($e, allow(non_snake_case))] + fn $n() { + cfg_select! { + $e => { + $n!(); + } + _ => {} + } + } + } + not($e) => { + #[cfg_attr(not($e), allow(unused))] + fn $n() { + panic!() + } + } + } + } +} +foo!(true, BAR); +foo!(any(true, unix, target_pointer_width = "64"), baz); +foo!(target_pointer_width = "64", quux); +foo!(false, haha); + +fn main() { + BAR(); + BAR!(); + baz(); + baz!(); + #[cfg(target_pointer_width = "64")] + quux(); + #[cfg(target_pointer_width = "64")] + quux!(); + #[cfg(panic = "unwind")] + { + let result = std::panic::catch_unwind(|| { + haha(); + }); + assert!(result.is_err()); + } +} diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr b/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr index afacb3a7d521..4652bf5e3c58 100644 --- a/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr @@ -9,7 +9,7 @@ note: candidate #1 is defined in the trait `Trait1` | LL | fn method(&self) { | ^^^^^^^^^^^^^^^^ -note: candidate #2 is defined in the trait `Trait2` +note: candidate #2 is defined in an impl of the trait `Trait2` for the type `T` --> $DIR/rigid-alias-bound-is-not-inherent.rs:27:5 | LL | fn method(&self) { diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed index ba46a447802c..1c45a2c0adb3 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed @@ -8,6 +8,5 @@ fn main() { //[next]~^^ ERROR expected a `FnMut(& as Iterator>::Item)` closure, found let _ = (-10..=10).find(|x: &i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}` - //[next]~| ERROR expected a `FnMut(& as Iterator>::Item)` closure, found + //[next]~^^ ERROR expected a `FnMut(& as Iterator>::Item)` closure, found } diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr index 7912ed4d7071..36e49c20c433 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr @@ -12,12 +12,6 @@ LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0); note: required by a bound in `find` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error[E0271]: expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}` - --> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:24 - | -LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); - | ^^^^ expected `&&i32`, found integer - error[E0277]: expected a `FnMut(& as Iterator>::Item)` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:9:29: 9:40}` --> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:29 | @@ -32,7 +26,6 @@ LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); note: required by a bound in `find` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0271, E0277. -For more information about an error, try `rustc --explain E0271`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs index 0fd56707763e..20d6fed3b35b 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs @@ -8,6 +8,5 @@ fn main() { //[next]~^^ ERROR expected a `FnMut(& as Iterator>::Item)` closure, found let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments - //[next]~^^ ERROR expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}` - //[next]~| ERROR expected a `FnMut(& as Iterator>::Item)` closure, found + //[next]~^^ ERROR expected a `FnMut(& as Iterator>::Item)` closure, found } diff --git a/tests/ui/mismatched_types/expectation-from-return-type.rs b/tests/ui/mismatched_types/expectation-from-return-type.rs new file mode 100644 index 000000000000..ffe3d929984d --- /dev/null +++ b/tests/ui/mismatched_types/expectation-from-return-type.rs @@ -0,0 +1,21 @@ +use std::ptr; + +fn main() { //~ NOTE: this implicit `()` return type influences the call expression's return type + let a = 0; + ptr::read(&a) + //~^ ERROR: mismatched types + //~| NOTE: expected `*const ()`, found `&{integer}` + //~| NOTE: arguments to this function are incorrect + //~| NOTE: expected raw pointer + //~| NOTE: function defined here +} + +fn foo() { //~ NOTE: this implicit `()` return type influences the call expression's return type + let a = 0; + return ptr::read(&a); + //~^ ERROR: mismatched types + //~| NOTE: expected `*const ()`, found `&{integer}` + //~| NOTE: arguments to this function are incorrect + //~| NOTE: expected raw pointer + //~| NOTE: function defined here +} diff --git a/tests/ui/mismatched_types/expectation-from-return-type.stderr b/tests/ui/mismatched_types/expectation-from-return-type.stderr new file mode 100644 index 000000000000..3bd7a21f987f --- /dev/null +++ b/tests/ui/mismatched_types/expectation-from-return-type.stderr @@ -0,0 +1,35 @@ +error[E0308]: mismatched types + --> $DIR/expectation-from-return-type.rs:5:15 + | +LL | fn main() { + | - this implicit `()` return type influences the call expression's return type +LL | let a = 0; +LL | ptr::read(&a) + | --------- ^^ expected `*const ()`, found `&{integer}` + | | + | arguments to this function are incorrect + | + = note: expected raw pointer `*const ()` + found reference `&{integer}` +note: function defined here + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL + +error[E0308]: mismatched types + --> $DIR/expectation-from-return-type.rs:15:22 + | +LL | fn foo() { + | - this implicit `()` return type influences the call expression's return type +LL | let a = 0; +LL | return ptr::read(&a); + | --------- ^^ expected `*const ()`, found `&{integer}` + | | + | arguments to this function are incorrect + | + = note: expected raw pointer `*const ()` + found reference `&{integer}` +note: function defined here + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr index 9a18798db213..6243a9a729a2 100644 --- a/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr +++ b/tests/ui/mismatched_types/transforming-option-ref-issue-127545.stderr @@ -16,6 +16,8 @@ LL | arg.map(|v| &**v) error[E0308]: mismatched types --> $DIR/transforming-option-ref-issue-127545.rs:9:19 | +LL | pub fn bar(arg: Option<&Vec>) -> &[i32] { + | ------ this return type influences the call expression's return type LL | arg.unwrap_or(&[]) | --------- ^^^ expected `&Vec`, found `&[_; 0]` | | @@ -41,6 +43,8 @@ LL + arg.map_or(&[], |v| v) error[E0308]: mismatched types --> $DIR/transforming-option-ref-issue-127545.rs:13:19 | +LL | pub fn barzz<'a>(arg: Option<&'a Vec>, v: &'a [i32]) -> &'a [i32] { + | --------- this return type influences the call expression's return type LL | arg.unwrap_or(v) | --------- ^ expected `&Vec`, found `&[i32]` | | @@ -66,6 +70,8 @@ LL + arg.map_or(v, |v| v) error[E0308]: mismatched types --> $DIR/transforming-option-ref-issue-127545.rs:17:19 | +LL | pub fn convert_result(arg: Result<&Vec, ()>) -> &[i32] { + | ------ this return type influences the call expression's return type LL | arg.unwrap_or(&[]) | --------- ^^^ expected `&Vec`, found `&[_; 0]` | | diff --git a/tests/ui/missing/missing-items/missing-type-parameter2.rs b/tests/ui/missing/missing-items/missing-type-parameter2.rs index 772e60b1376c..c52f3157454a 100644 --- a/tests/ui/missing/missing-items/missing-type-parameter2.rs +++ b/tests/ui/missing/missing-items/missing-type-parameter2.rs @@ -1,10 +1,10 @@ struct X(); impl X {} -//~^ ERROR cannot find type `N` in this scope +//~^ ERROR cannot find const `N` in this scope //~| ERROR unresolved item provided when a constant was expected impl X {} -//~^ ERROR cannot find type `N` in this scope +//~^ ERROR cannot find const `N` in this scope //~| ERROR defaults for generic parameters are not allowed here //~| ERROR unresolved item provided when a constant was expected @@ -15,5 +15,4 @@ fn foo(_: T) where T: Send {} fn bar(_: A) {} //~^ ERROR cannot find type `A` in this scope -fn main() { -} +fn main() {} diff --git a/tests/ui/missing/missing-items/missing-type-parameter2.stderr b/tests/ui/missing/missing-items/missing-type-parameter2.stderr index c361ed79cc79..7c85d39ba78a 100644 --- a/tests/ui/missing/missing-items/missing-type-parameter2.stderr +++ b/tests/ui/missing/missing-items/missing-type-parameter2.stderr @@ -1,39 +1,30 @@ -error[E0425]: cannot find type `N` in this scope +error[E0425]: cannot find const `N` in this scope --> $DIR/missing-type-parameter2.rs:3:8 | LL | struct X(); - | ------------------------ similarly named struct `X` defined here + | ----------- corresponding const parameter on the type defined here LL | LL | impl X {} - | ^ + | ^ not found in this scope | -help: a struct with a similar name exists +help: you might have meant to introduce a const parameter `N` on the impl | -LL - impl X {} -LL + impl X {} - | -help: you might be missing a type parameter - | -LL | impl X {} - | +++ +LL | impl X {} + | +++++++++++++ -error[E0425]: cannot find type `N` in this scope +error[E0425]: cannot find const `N` in this scope --> $DIR/missing-type-parameter2.rs:6:28 | +LL | struct X(); + | ----------- corresponding const parameter on the type defined here +... LL | impl X {} - | - ^ - | | - | similarly named type parameter `T` defined here + | ^ not found in this scope | -help: a type parameter with a similar name exists +help: you might have meant to introduce a const parameter `N` on the impl | -LL - impl X {} -LL + impl X {} - | -help: you might be missing a type parameter - | -LL | impl X {} - | +++ +LL | impl X {} + | +++++++++++++ error[E0425]: cannot find type `T` in this scope --> $DIR/missing-type-parameter2.rs:11:20 diff --git a/tests/ui/on-unimplemented/self-types.rs b/tests/ui/on-unimplemented/self-types.rs new file mode 100644 index 000000000000..736d3baf60c7 --- /dev/null +++ b/tests/ui/on-unimplemented/self-types.rs @@ -0,0 +1,30 @@ +#![feature(rustc_attrs)] + +#[rustc_on_unimplemented( + on(Self = "{union}", message = "union self type"), + on(Self = "{enum}", message = "enum self type"), + on(Self = "{struct}", message = "struct self type"), + message = "fallback self type `{Self}`" +)] +trait Trait {} + +union Union { + value: u8, +} + +enum Enum { + Variant, +} + +struct Struct; + +fn needs_trait() {} + +fn main() { + needs_trait::(); + //~^ ERROR union self type + needs_trait::(); + //~^ ERROR enum self type + needs_trait::(); + //~^ ERROR struct self type +} diff --git a/tests/ui/on-unimplemented/self-types.stderr b/tests/ui/on-unimplemented/self-types.stderr new file mode 100644 index 000000000000..ea40ac566bb9 --- /dev/null +++ b/tests/ui/on-unimplemented/self-types.stderr @@ -0,0 +1,69 @@ +error[E0277]: union self type + --> $DIR/self-types.rs:24:19 + | +LL | needs_trait::(); + | ^^^^^ unsatisfied trait bound + | +help: the trait `Trait` is not implemented for `Union` + --> $DIR/self-types.rs:11:1 + | +LL | union Union { + | ^^^^^^^^^^^ +help: this trait has no implementations, consider adding one + --> $DIR/self-types.rs:9:1 + | +LL | trait Trait {} + | ^^^^^^^^^^^ +note: required by a bound in `needs_trait` + --> $DIR/self-types.rs:21:19 + | +LL | fn needs_trait() {} + | ^^^^^ required by this bound in `needs_trait` + +error[E0277]: enum self type + --> $DIR/self-types.rs:26:19 + | +LL | needs_trait::(); + | ^^^^ unsatisfied trait bound + | +help: the trait `Trait` is not implemented for `Enum` + --> $DIR/self-types.rs:15:1 + | +LL | enum Enum { + | ^^^^^^^^^ +help: this trait has no implementations, consider adding one + --> $DIR/self-types.rs:9:1 + | +LL | trait Trait {} + | ^^^^^^^^^^^ +note: required by a bound in `needs_trait` + --> $DIR/self-types.rs:21:19 + | +LL | fn needs_trait() {} + | ^^^^^ required by this bound in `needs_trait` + +error[E0277]: struct self type + --> $DIR/self-types.rs:28:19 + | +LL | needs_trait::(); + | ^^^^^^ unsatisfied trait bound + | +help: the trait `Trait` is not implemented for `Struct` + --> $DIR/self-types.rs:19:1 + | +LL | struct Struct; + | ^^^^^^^^^^^^^ +help: this trait has no implementations, consider adding one + --> $DIR/self-types.rs:9:1 + | +LL | trait Trait {} + | ^^^^^^^^^^^ +note: required by a bound in `needs_trait` + --> $DIR/self-types.rs:21:19 + | +LL | fn needs_trait() {} + | ^^^^^ required by this bound in `needs_trait` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs index 83ad8c76bb1c..21edb9ceeff4 100644 --- a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.rs @@ -6,6 +6,7 @@ #![expect(incomplete_features)] fn good_fn_item(((x if x) | x): bool) -> bool { x } +//~^ ERROR: used binding `x` is possibly-uninitialized [E0381] fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} //~^ ERROR cannot find value `x` in this scope diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr index 44e42f142707..a95ffdd42532 100644 --- a/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr +++ b/tests/ui/pattern/rfc-3637-guard-patterns/name-resolution.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `y` is not bound in all patterns - --> $DIR/name-resolution.rs:37:10 + --> $DIR/name-resolution.rs:38:10 | LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | ^^^^^^^^^^^^ - variable not in all patterns @@ -13,7 +13,7 @@ LL + ((Ok(x) if y) | (Err(x) if x),) => x && y, | error[E0408]: variable `x` is not bound in all patterns - --> $DIR/name-resolution.rs:37:25 + --> $DIR/name-resolution.rs:38:25 | LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | - ^^^^^^^^^^^^^ pattern doesn't bind `x` @@ -27,7 +27,7 @@ LL + ((Ok(y) if y) | (Err(y) if x),) => x && y, | error[E0408]: variable `x` is not bound in all patterns - --> $DIR/name-resolution.rs:63:28 + --> $DIR/name-resolution.rs:64:28 | LL | Some(x if x > 0) | None => {} | - ^^^^ pattern doesn't bind `x` @@ -35,7 +35,7 @@ LL | Some(x if x > 0) | None => {} | variable not in all patterns error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:10:34 + --> $DIR/name-resolution.rs:11:34 | LL | fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {} | ^ @@ -47,7 +47,7 @@ LL + fn bad_fn_item_1(x: bool, ((y if y) | y): bool) {} | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:12:25 + --> $DIR/name-resolution.rs:13:25 | LL | fn bad_fn_item_2(((x if y) | x): bool, y: bool) {} | ^ @@ -59,7 +59,7 @@ LL + fn bad_fn_item_2(((x if x) | x): bool, y: bool) {} | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:20:18 + --> $DIR/name-resolution.rs:21:18 | LL | (x, y if x) => x && y, | ^ @@ -71,7 +71,7 @@ LL + (x, y if y) => x && y, | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:22:15 + --> $DIR/name-resolution.rs:23:15 | LL | (x if y, y) => x && y, | ^ @@ -83,7 +83,7 @@ LL + (x if x, y) => x && y, | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:29:20 + --> $DIR/name-resolution.rs:30:20 | LL | (x @ (y if x),) => x && y, | ^ @@ -95,7 +95,7 @@ LL + (x @ (y if y),) => x && y, | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:37:20 + --> $DIR/name-resolution.rs:38:20 | LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | ^ @@ -107,7 +107,7 @@ LL + ((Ok(x) if x) | (Err(y) if x),) => x && y, | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:37:36 + --> $DIR/name-resolution.rs:38:36 | LL | ((Ok(x) if y) | (Err(y) if x),) => x && y, | ^ @@ -119,13 +119,13 @@ LL + ((Ok(x) if y) | (Err(y) if y),) => x && y, | error[E0425]: cannot find value `nonexistent` in this scope - --> $DIR/name-resolution.rs:44:15 + --> $DIR/name-resolution.rs:45:15 | LL | let (_ if nonexistent) = true; | ^^^^^^^^^^^ not found in this scope error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:46:22 + --> $DIR/name-resolution.rs:47:22 | LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } | ^ @@ -137,7 +137,7 @@ LL + if let ((x, y if y) | (x if y, y)) = (true, true) { x && y; } | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:46:33 + --> $DIR/name-resolution.rs:47:33 | LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } | ^ @@ -149,7 +149,7 @@ LL + if let ((x, y if x) | (x if x, y)) = (true, true) { x && y; } | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:49:25 + --> $DIR/name-resolution.rs:50:25 | LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } | ^ @@ -161,7 +161,7 @@ LL + while let ((x, y if y) | (x if y, y)) = (true, true) { x && y; } | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:49:36 + --> $DIR/name-resolution.rs:50:36 | LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; } | ^ @@ -173,7 +173,7 @@ LL + while let ((x, y if x) | (x if x, y)) = (true, true) { x && y; } | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:52:19 + --> $DIR/name-resolution.rs:53:19 | LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } | ^ @@ -185,7 +185,7 @@ LL + for ((x, y if y) | (x if y, y)) in [(true, true)] { x && y; } | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:52:30 + --> $DIR/name-resolution.rs:53:30 | LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; } | ^ @@ -197,7 +197,7 @@ LL + for ((x, y if x) | (x if x, y)) in [(true, true)] { x && y; } | error[E0425]: cannot find value `y` in this scope - --> $DIR/name-resolution.rs:57:13 + --> $DIR/name-resolution.rs:58:13 | LL | (|(x if y), (y if x)| x && y)(true, true); | ^ @@ -209,7 +209,7 @@ LL + (|(x if x), (y if x)| x && y)(true, true); | error[E0425]: cannot find value `x` in this scope - --> $DIR/name-resolution.rs:57:23 + --> $DIR/name-resolution.rs:58:23 | LL | (|(x if y), (y if x)| x && y)(true, true); | ^ @@ -221,7 +221,7 @@ LL + (|(x if y), (y if y)| x && y)(true, true); | error[E0308]: mismatched types - --> $DIR/name-resolution.rs:75:18 + --> $DIR/name-resolution.rs:76:18 | LL | local if local => 0, | ^^^^^ expected `bool`, found `({integer}, {integer})` @@ -229,7 +229,16 @@ LL | local if local => 0, = note: expected type `bool` found tuple `({integer}, {integer})` -error: aborting due to 20 previous errors +error[E0381]: used binding `x` is possibly-uninitialized + --> $DIR/name-resolution.rs:8:49 + | +LL | fn good_fn_item(((x if x) | x): bool) -> bool { x } + | - - ^ `x` used here but it is possibly-uninitialized + | | | + | | binding initialized here in some conditions + | binding declared here but left uninitialized -Some errors have detailed explanations: E0308, E0408, E0425. +error: aborting due to 21 previous errors + +Some errors have detailed explanations: E0308, E0381, E0408, E0425. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/pin-ergonomics/pin_v2-attr.stderr b/tests/ui/pin-ergonomics/pin_v2-attr.stderr index 8f8a9f3b3a19..5c8a455114c4 100644 --- a/tests/ui/pin-ergonomics/pin_v2-attr.stderr +++ b/tests/ui/pin-ergonomics/pin_v2-attr.stderr @@ -116,14 +116,6 @@ LL | #[pin_v2] | = help: `#[pin_v2]` can only be applied to data types -error: `#[pin_v2]` attribute cannot be used on delegations - --> $DIR/pin_v2-attr.rs:58:5 - | -LL | #[pin_v2] - | ^^^^^^^^^ - | - = help: `#[pin_v2]` can only be applied to data types - error: `#[pin_v2]` attribute cannot be used on inherent methods --> $DIR/pin_v2-attr.rs:61:5 | @@ -308,5 +300,13 @@ LL | #[pin_v2] | = help: `#[pin_v2]` can only be applied to data types +error: `#[pin_v2]` attribute cannot be used on delegations + --> $DIR/pin_v2-attr.rs:58:5 + | +LL | #[pin_v2] + | ^^^^^^^^^ + | + = help: `#[pin_v2]` can only be applied to data types + error: aborting due to 39 previous errors diff --git a/tests/ui/proc-macro/meta-macro-hygiene.stdout b/tests/ui/proc-macro/meta-macro-hygiene.stdout index b5db9922b31a..a03e567bd48a 100644 --- a/tests/ui/proc-macro/meta-macro-hygiene.stdout +++ b/tests/ui/proc-macro/meta-macro-hygiene.stdout @@ -1,7 +1,7 @@ Def site: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) Input: TokenStream [Ident { ident: "$crate", span: $DIR/meta-macro-hygiene.rs:26:37: 26:43 (#3) }, Punct { ch: ':', spacing: Joint, span: $DIR/meta-macro-hygiene.rs:26:43: 26:44 (#3) }, Punct { ch: ':', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:26:44: 26:45 (#3) }, Ident { ident: "dummy", span: $DIR/meta-macro-hygiene.rs:26:45: 26:50 (#3) }, Punct { ch: '!', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:26:50: 26:51 (#3) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/meta-macro-hygiene.rs:26:51: 26:53 (#3) }] Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Punct { ch: ':', spacing: Joint, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Punct { ch: ':', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Ident { ident: "dummy", span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Punct { ch: '!', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }] -#![feature /* 0#0 */(prelude_import)] +#![feature /* 0#0 */(prelude_import /* 0#0 */)] //@ aux-build:make-macro.rs //@ proc-macro: meta-macro.rs //@ edition:2018 @@ -30,7 +30,8 @@ macro_rules! produce_it */ { () => { - meta_macro::print_def_site!($crate::dummy!()); + meta_macro /* 0#0 */::print_def_site /* 0#0 + */!($crate /* 0#0 */::dummy /* 0#0 */!()); // `print_def_site!` will respan the `$crate` identifier // with `Span::def_site()`. This should cause it to resolve // relative to `meta_macro`, *not* `make_macro` (despite diff --git a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout index e45abab03b4c..61b55782e6e9 100644 --- a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout +++ b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout @@ -20,7 +20,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ span: $DIR/nonterminal-token-hygiene.rs:23:27: 23:32 (#4), }, ] -#![feature /* 0#0 */(prelude_import)] +#![feature /* 0#0 */(prelude_import /* 0#0 */)] #![no_std /* 0#0 */] // Make sure that marks from declarative macros are applied to tokens in nonterminal. @@ -34,7 +34,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ //@ proc-macro: test-macros.rs //@ edition: 2015 -#![feature /* 0#0 */(decl_macro)] +#![feature /* 0#0 */(decl_macro /* 0#0 */)] #![no_std /* 0#0 */] extern crate core /* 0#2 */; #[prelude_import /* 0#1 */] @@ -49,15 +49,22 @@ macro_rules! outer /* 0#0 */ { - ($item:item) => + ($item /* 0#0 */:item /* 0#0 */) => { - macro inner() { print_bang! { $item } } inner!(); + macro /* 0#0 */ inner /* 0#0 */() + { print_bang /* 0#0 */! { $item /* 0#0 */ } } inner /* 0#0 + */!(); }; } struct S /* 0#0 */; -macro inner /* 0#3 */ { () => { print_bang! { struct S; } } } +macro inner + /* + 0#3 + */ { + () => { print_bang /* 0#3 */! { struct /* 0#0 */ S /* 0#0 */; } } +} struct S /* 0#5 */; // OK, not a duplicate definition of `S` diff --git a/tests/ui/regions/regions-outlives-nominal-type-enum-region-rev.rs b/tests/ui/regions/regions-outlives-nominal-type-enum-region-rev.rs deleted file mode 100644 index 3d4a30507884..000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-enum-region-rev.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod rev_variant_struct_region { - struct Foo<'a> { - x: fn(&'a i32), - } - enum Bar<'a,'b> { - V(&'a Foo<'b>) - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-enum-region.rs b/tests/ui/regions/regions-outlives-nominal-type-enum-region.rs deleted file mode 100644 index 2e0d1b36ca59..000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-enum-region.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod variant_struct_region { - struct Foo<'a> { - x: &'a i32, - } - enum Bar<'a,'b> { - V(&'a Foo<'b>) - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-enum-type-rev.rs b/tests/ui/regions/regions-outlives-nominal-type-enum-type-rev.rs deleted file mode 100644 index baf7874bc1a6..000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-enum-type-rev.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod variant_struct_type { - struct Foo { - x: fn(T) - } - enum Bar<'a,'b> { - V(&'a Foo<&'b i32>) - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-enum-type.rs b/tests/ui/regions/regions-outlives-nominal-type-enum-type.rs deleted file mode 100644 index b8392c967b10..000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-enum-type.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod variant_struct_type { - struct Foo { - x: T - } - enum Bar<'a,'b> { - V(&'a Foo<&'b i32>) - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-struct-region-rev.rs b/tests/ui/regions/regions-outlives-nominal-type-struct-region-rev.rs deleted file mode 100644 index 6a50248cb70b..000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-struct-region-rev.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod rev_variant_struct_region { - struct Foo<'a> { - x: fn(&'a i32), - } - struct Bar<'a,'b> { - f: &'a Foo<'b> - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-struct-region.rs b/tests/ui/regions/regions-outlives-nominal-type-struct-region.rs deleted file mode 100644 index 17564bcbf269..000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-struct-region.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod variant_struct_region { - struct Foo<'a> { - x: &'a i32, - } - struct Bar<'a,'b> { - f: &'a Foo<'b> - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-struct-type-rev.rs b/tests/ui/regions/regions-outlives-nominal-type-struct-type-rev.rs deleted file mode 100644 index 33961de7d6a4..000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-struct-type-rev.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod rev_variant_struct_type { - struct Foo { - x: fn(T) - } - struct Bar<'a,'b> { - f: &'a Foo<&'b i32> - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-struct-type.rs b/tests/ui/regions/regions-outlives-nominal-type-struct-type.rs deleted file mode 100644 index c5238086fc05..000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-struct-type.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod variant_struct_type { - struct Foo { - x: T - } - struct Bar<'a,'b> { - f: &'a Foo<&'b i32> - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type.rs b/tests/ui/regions/regions-outlives-nominal-type.rs new file mode 100644 index 000000000000..177d064042dd --- /dev/null +++ b/tests/ui/regions/regions-outlives-nominal-type.rs @@ -0,0 +1,80 @@ +// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its +// arguments (like `'a`) outlive `'b`. +// +// Rule OutlivesNominalType from RFC 1214. + +//@ check-pass + +mod variant_enum_region { + struct Foo<'a> { + x: &'a i32, + } + enum Bar<'a, 'b> { + V(&'a Foo<'b>), + } +} + +mod rev_variant_enum_region { + struct Foo<'a> { + x: fn(&'a i32), + } + enum Bar<'a, 'b> { + V(&'a Foo<'b>), + } +} + +mod variant_enum_type { + struct Foo { + x: T, + } + enum Bar<'a, 'b> { + V(&'a Foo<&'b i32>), + } +} + +mod rev_variant_enum_type { + struct Foo { + x: fn(T), + } + enum Bar<'a, 'b> { + V(&'a Foo<&'b i32>), + } +} + +mod variant_struct_region { + struct Foo<'a> { + x: &'a i32, + } + struct Bar<'a, 'b> { + f: &'a Foo<'b>, + } +} + +mod rev_variant_struct_region { + struct Foo<'a> { + x: fn(&'a i32), + } + struct Bar<'a, 'b> { + f: &'a Foo<'b>, + } +} + +mod variant_struct_type { + struct Foo { + x: T, + } + struct Bar<'a, 'b> { + f: &'a Foo<&'b i32>, + } +} + +mod rev_variant_struct_type { + struct Foo { + x: fn(T), + } + struct Bar<'a, 'b> { + f: &'a Foo<&'b i32>, + } +} + +fn main() {} diff --git a/tests/ui/kindck/kindck-copy.rs b/tests/ui/traits/basic-copyable-types.rs similarity index 63% rename from tests/ui/kindck/kindck-copy.rs rename to tests/ui/traits/basic-copyable-types.rs index 36bf0d2b785f..c953b60260df 100644 --- a/tests/ui/kindck/kindck-copy.rs +++ b/tests/ui/traits/basic-copyable-types.rs @@ -2,9 +2,9 @@ use std::rc::Rc; -fn assert_copy() { } +fn assert_copy() {} -trait Dummy { } +trait Dummy {} #[derive(Copy, Clone)] struct MyStruct { @@ -16,8 +16,8 @@ struct MyNoncopyStruct { x: Box, } -fn test<'a,T,U:Copy>(_: &'a isize) { - // lifetime pointers are ok... +fn test<'a, T, U: Copy>(_: &'a isize) { + // references are ok... assert_copy::<&'static isize>(); assert_copy::<&'a isize>(); assert_copy::<&'a str>(); @@ -25,25 +25,25 @@ fn test<'a,T,U:Copy>(_: &'a isize) { // ...unless they are mutable assert_copy::<&'static mut isize>(); //~ ERROR : Copy` is not satisfied - assert_copy::<&'a mut isize>(); //~ ERROR : Copy` is not satisfied + assert_copy::<&'a mut isize>(); //~ ERROR : Copy` is not satisfied // boxes are not ok - assert_copy::>(); //~ ERROR : Copy` is not satisfied - assert_copy::(); //~ ERROR : Copy` is not satisfied - assert_copy:: >(); //~ ERROR : Copy` is not satisfied + assert_copy::>(); //~ ERROR : Copy` is not satisfied + assert_copy::(); //~ ERROR : Copy` is not satisfied + assert_copy::>(); //~ ERROR : Copy` is not satisfied assert_copy::>(); //~ ERROR : Copy` is not satisfied - // borrowed object types are generally ok + // borrowed trait objects are generally ok assert_copy::<&'a dyn Dummy>(); assert_copy::<&'a (dyn Dummy + Send)>(); assert_copy::<&'static (dyn Dummy + Send)>(); - // owned object types are not ok + // boxed trait objects are not ok assert_copy::>(); //~ ERROR : Copy` is not satisfied assert_copy::>(); //~ ERROR : Copy` is not satisfied - // mutable object types are not ok - assert_copy::<&'a mut (dyn Dummy + Send)>(); //~ ERROR : Copy` is not satisfied + // mutable references to trait objects are not ok + assert_copy::<&'a mut (dyn Dummy + Send)>(); //~ ERROR : Copy` is not satisfied // raw ptrs are ok assert_copy::<*const isize>(); @@ -55,7 +55,7 @@ fn test<'a,T,U:Copy>(_: &'a isize) { assert_copy::<()>(); // tuples are ok - assert_copy::<(isize,isize)>(); + assert_copy::<(isize, isize)>(); // structs of POD are ok assert_copy::(); @@ -64,8 +64,7 @@ fn test<'a,T,U:Copy>(_: &'a isize) { assert_copy::(); //~ ERROR : Copy` is not satisfied // ref counted types are not ok - assert_copy::>(); //~ ERROR : Copy` is not satisfied + assert_copy::>(); //~ ERROR : Copy` is not satisfied } -pub fn main() { -} +pub fn main() {} diff --git a/tests/ui/kindck/kindck-copy.stderr b/tests/ui/traits/basic-copyable-types.stderr similarity index 61% rename from tests/ui/kindck/kindck-copy.stderr rename to tests/ui/traits/basic-copyable-types.stderr index f5623ddd4f79..b4fa95753335 100644 --- a/tests/ui/kindck/kindck-copy.stderr +++ b/tests/ui/traits/basic-copyable-types.stderr @@ -1,14 +1,14 @@ error[E0277]: the trait bound `&'static mut isize: Copy` is not satisfied - --> $DIR/kindck-copy.rs:27:19 + --> $DIR/basic-copyable-types.rs:27:19 | LL | assert_copy::<&'static mut isize>(); | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'static mut isize` | note: required by a bound in `assert_copy` - --> $DIR/kindck-copy.rs:5:18 + --> $DIR/basic-copyable-types.rs:5:19 | -LL | fn assert_copy() { } - | ^^^^ required by this bound in `assert_copy` +LL | fn assert_copy() {} + | ^^^^ required by this bound in `assert_copy` help: consider removing the leading `&`-reference | LL - assert_copy::<&'static mut isize>(); @@ -16,16 +16,16 @@ LL + assert_copy::(); | error[E0277]: the trait bound `&'a mut isize: Copy` is not satisfied - --> $DIR/kindck-copy.rs:28:19 + --> $DIR/basic-copyable-types.rs:28:19 | LL | assert_copy::<&'a mut isize>(); | ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'a mut isize` | note: required by a bound in `assert_copy` - --> $DIR/kindck-copy.rs:5:18 + --> $DIR/basic-copyable-types.rs:5:19 | -LL | fn assert_copy() { } - | ^^^^ required by this bound in `assert_copy` +LL | fn assert_copy() {} + | ^^^^ required by this bound in `assert_copy` help: consider removing the leading `&`-reference | LL - assert_copy::<&'a mut isize>(); @@ -33,117 +33,117 @@ LL + assert_copy::(); | error[E0277]: the trait bound `Box: Copy` is not satisfied - --> $DIR/kindck-copy.rs:31:19 + --> $DIR/basic-copyable-types.rs:31:19 | LL | assert_copy::>(); | ^^^^^^^^^^ the trait `Copy` is not implemented for `Box` | note: required by a bound in `assert_copy` - --> $DIR/kindck-copy.rs:5:18 + --> $DIR/basic-copyable-types.rs:5:19 | -LL | fn assert_copy() { } - | ^^^^ required by this bound in `assert_copy` +LL | fn assert_copy() {} + | ^^^^ required by this bound in `assert_copy` error[E0277]: the trait bound `String: Copy` is not satisfied - --> $DIR/kindck-copy.rs:32:19 + --> $DIR/basic-copyable-types.rs:32:19 | LL | assert_copy::(); | ^^^^^^ the trait `Copy` is not implemented for `String` | note: required by a bound in `assert_copy` - --> $DIR/kindck-copy.rs:5:18 + --> $DIR/basic-copyable-types.rs:5:19 | -LL | fn assert_copy() { } - | ^^^^ required by this bound in `assert_copy` +LL | fn assert_copy() {} + | ^^^^ required by this bound in `assert_copy` error[E0277]: the trait bound `Vec: Copy` is not satisfied - --> $DIR/kindck-copy.rs:33:19 + --> $DIR/basic-copyable-types.rs:33:19 | -LL | assert_copy:: >(); +LL | assert_copy::>(); | ^^^^^^^^^^ the trait `Copy` is not implemented for `Vec` | note: required by a bound in `assert_copy` - --> $DIR/kindck-copy.rs:5:18 + --> $DIR/basic-copyable-types.rs:5:19 | -LL | fn assert_copy() { } - | ^^^^ required by this bound in `assert_copy` +LL | fn assert_copy() {} + | ^^^^ required by this bound in `assert_copy` error[E0277]: the trait bound `Box<&'a mut isize>: Copy` is not satisfied - --> $DIR/kindck-copy.rs:34:19 + --> $DIR/basic-copyable-types.rs:34:19 | LL | assert_copy::>(); | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Box<&'a mut isize>` | note: required by a bound in `assert_copy` - --> $DIR/kindck-copy.rs:5:18 + --> $DIR/basic-copyable-types.rs:5:19 | -LL | fn assert_copy() { } - | ^^^^ required by this bound in `assert_copy` +LL | fn assert_copy() {} + | ^^^^ required by this bound in `assert_copy` error[E0277]: the trait bound `Box: Copy` is not satisfied - --> $DIR/kindck-copy.rs:42:19 + --> $DIR/basic-copyable-types.rs:42:19 | LL | assert_copy::>(); | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Box` | note: required by a bound in `assert_copy` - --> $DIR/kindck-copy.rs:5:18 + --> $DIR/basic-copyable-types.rs:5:19 | -LL | fn assert_copy() { } - | ^^^^ required by this bound in `assert_copy` +LL | fn assert_copy() {} + | ^^^^ required by this bound in `assert_copy` error[E0277]: the trait bound `Box: Copy` is not satisfied - --> $DIR/kindck-copy.rs:43:19 + --> $DIR/basic-copyable-types.rs:43:19 | LL | assert_copy::>(); | ^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Box` | note: required by a bound in `assert_copy` - --> $DIR/kindck-copy.rs:5:18 + --> $DIR/basic-copyable-types.rs:5:19 | -LL | fn assert_copy() { } - | ^^^^ required by this bound in `assert_copy` +LL | fn assert_copy() {} + | ^^^^ required by this bound in `assert_copy` error[E0277]: the trait bound `&'a mut (dyn Dummy + Send + 'a): Copy` is not satisfied - --> $DIR/kindck-copy.rs:46:19 + --> $DIR/basic-copyable-types.rs:46:19 | LL | assert_copy::<&'a mut (dyn Dummy + Send)>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `&'a mut (dyn Dummy + Send + 'a)` | note: required by a bound in `assert_copy` - --> $DIR/kindck-copy.rs:5:18 + --> $DIR/basic-copyable-types.rs:5:19 | -LL | fn assert_copy() { } - | ^^^^ required by this bound in `assert_copy` +LL | fn assert_copy() {} + | ^^^^ required by this bound in `assert_copy` error[E0277]: the trait bound `MyNoncopyStruct: Copy` is not satisfied - --> $DIR/kindck-copy.rs:64:19 + --> $DIR/basic-copyable-types.rs:64:19 | LL | assert_copy::(); | ^^^^^^^^^^^^^^^ unsatisfied trait bound | help: the trait `Copy` is not implemented for `MyNoncopyStruct` - --> $DIR/kindck-copy.rs:15:1 + --> $DIR/basic-copyable-types.rs:15:1 | LL | struct MyNoncopyStruct { | ^^^^^^^^^^^^^^^^^^^^^^ note: required by a bound in `assert_copy` - --> $DIR/kindck-copy.rs:5:18 + --> $DIR/basic-copyable-types.rs:5:19 | -LL | fn assert_copy() { } - | ^^^^ required by this bound in `assert_copy` +LL | fn assert_copy() {} + | ^^^^ required by this bound in `assert_copy` error[E0277]: the trait bound `Rc: Copy` is not satisfied - --> $DIR/kindck-copy.rs:67:19 + --> $DIR/basic-copyable-types.rs:67:19 | LL | assert_copy::>(); | ^^^^^^^^^ the trait `Copy` is not implemented for `Rc` | note: required by a bound in `assert_copy` - --> $DIR/kindck-copy.rs:5:18 + --> $DIR/basic-copyable-types.rs:5:19 | -LL | fn assert_copy() { } - | ^^^^ required by this bound in `assert_copy` +LL | fn assert_copy() {} + | ^^^^ required by this bound in `assert_copy` error: aborting due to 11 previous errors diff --git a/tests/ui/kindck/kindck-nonsendable-1.rs b/tests/ui/traits/closure-rc-not-send.rs similarity index 70% rename from tests/ui/kindck/kindck-nonsendable-1.rs rename to tests/ui/traits/closure-rc-not-send.rs index b32fd78624b8..78ef834afabd 100644 --- a/tests/ui/kindck/kindck-nonsendable-1.rs +++ b/tests/ui/traits/closure-rc-not-send.rs @@ -2,10 +2,10 @@ fn foo(_x: Rc) {} -fn bar(_: F) { } +fn bar(_: F) {} fn main() { let x = Rc::new(3); - bar(move|| foo(x)); + bar(move || foo(x)); //~^ ERROR `Rc` cannot be sent between threads safely } diff --git a/tests/ui/traits/closure-rc-not-send.stderr b/tests/ui/traits/closure-rc-not-send.stderr new file mode 100644 index 000000000000..5e73f13648d5 --- /dev/null +++ b/tests/ui/traits/closure-rc-not-send.stderr @@ -0,0 +1,25 @@ +error[E0277]: `Rc` cannot be sent between threads safely + --> $DIR/closure-rc-not-send.rs:9:9 + | +LL | bar(move || foo(x)); + | --- -------^^^^^^^ + | | | + | | `Rc` cannot be sent between threads safely + | | within this `{closure@$DIR/closure-rc-not-send.rs:9:9: 9:16}` + | required by a bound introduced by this call + | + = help: within `{closure@$DIR/closure-rc-not-send.rs:9:9: 9:16}`, the trait `Send` is not implemented for `Rc` +note: required because it's used within this closure + --> $DIR/closure-rc-not-send.rs:9:9 + | +LL | bar(move || foo(x)); + | ^^^^^^^ +note: required by a bound in `bar` + --> $DIR/closure-rc-not-send.rs:5:22 + | +LL | fn bar(_: F) {} + | ^^^^ required by this bound in `bar` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/kindck/kindck-impl-type-params.rs b/tests/ui/traits/copy-bounds-impl-type-params.rs similarity index 94% rename from tests/ui/kindck/kindck-impl-type-params.rs rename to tests/ui/traits/copy-bounds-impl-type-params.rs index 707c5dbaec30..ba54bc313c08 100644 --- a/tests/ui/kindck/kindck-impl-type-params.rs +++ b/tests/ui/traits/copy-bounds-impl-type-params.rs @@ -6,7 +6,9 @@ struct S(marker::PhantomData); trait Gettable { - fn get(&self) -> T { panic!() } + fn get(&self) -> T { + panic!() + } } impl Gettable for S {} @@ -45,4 +47,4 @@ fn foo3<'a>() { //~^ ERROR : Copy` is not satisfied } -fn main() { } +fn main() {} diff --git a/tests/ui/kindck/kindck-impl-type-params.stderr b/tests/ui/traits/copy-bounds-impl-type-params.stderr similarity index 86% rename from tests/ui/kindck/kindck-impl-type-params.stderr rename to tests/ui/traits/copy-bounds-impl-type-params.stderr index 0c9ab13f4774..08fde8fb5df3 100644 --- a/tests/ui/kindck/kindck-impl-type-params.stderr +++ b/tests/ui/traits/copy-bounds-impl-type-params.stderr @@ -1,11 +1,11 @@ error[E0277]: `T` cannot be sent between threads safely - --> $DIR/kindck-impl-type-params.rs:16:13 + --> $DIR/copy-bounds-impl-type-params.rs:18:13 | LL | let a = &t as &dyn Gettable; | ^^ `T` cannot be sent between threads safely | note: required for `S` to implement `Gettable` - --> $DIR/kindck-impl-type-params.rs:12:32 + --> $DIR/copy-bounds-impl-type-params.rs:14:32 | LL | impl Gettable for S {} | ---- ^^^^^^^^^^^ ^^^^ @@ -18,13 +18,13 @@ LL | fn f(val: T) { | +++++++++++++++++++ error[E0277]: the trait bound `T: Copy` is not satisfied - --> $DIR/kindck-impl-type-params.rs:16:13 + --> $DIR/copy-bounds-impl-type-params.rs:18:13 | LL | let a = &t as &dyn Gettable; | ^^ the trait `Copy` is not implemented for `T` | note: required for `S` to implement `Gettable` - --> $DIR/kindck-impl-type-params.rs:12:32 + --> $DIR/copy-bounds-impl-type-params.rs:14:32 | LL | impl Gettable for S {} | ---- ^^^^^^^^^^^ ^^^^ @@ -37,13 +37,13 @@ LL | fn f(val: T) { | +++++++++++++++++++ error[E0277]: `T` cannot be sent between threads safely - --> $DIR/kindck-impl-type-params.rs:23:31 + --> $DIR/copy-bounds-impl-type-params.rs:25:31 | LL | let a: &dyn Gettable = &t; | ^^ `T` cannot be sent between threads safely | note: required for `S` to implement `Gettable` - --> $DIR/kindck-impl-type-params.rs:12:32 + --> $DIR/copy-bounds-impl-type-params.rs:14:32 | LL | impl Gettable for S {} | ---- ^^^^^^^^^^^ ^^^^ @@ -56,13 +56,13 @@ LL | fn g(val: T) { | +++++++++++++++++++ error[E0277]: the trait bound `T: Copy` is not satisfied - --> $DIR/kindck-impl-type-params.rs:23:31 + --> $DIR/copy-bounds-impl-type-params.rs:25:31 | LL | let a: &dyn Gettable = &t; | ^^ the trait `Copy` is not implemented for `T` | note: required for `S` to implement `Gettable` - --> $DIR/kindck-impl-type-params.rs:12:32 + --> $DIR/copy-bounds-impl-type-params.rs:14:32 | LL | impl Gettable for S {} | ---- ^^^^^^^^^^^ ^^^^ @@ -75,18 +75,18 @@ LL | fn g(val: T) { | +++++++++++++++++++ error[E0277]: the trait bound `String: Copy` is not satisfied - --> $DIR/kindck-impl-type-params.rs:36:13 + --> $DIR/copy-bounds-impl-type-params.rs:38:13 | LL | let a = t as Box>; | ^ the trait `Copy` is not implemented for `String` | help: the trait `Gettable` is implemented for `S` - --> $DIR/kindck-impl-type-params.rs:12:1 + --> $DIR/copy-bounds-impl-type-params.rs:14:1 | LL | impl Gettable for S {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: required for `S` to implement `Gettable` - --> $DIR/kindck-impl-type-params.rs:12:32 + --> $DIR/copy-bounds-impl-type-params.rs:14:32 | LL | impl Gettable for S {} | ---- ^^^^^^^^^^^ ^^^^ @@ -95,18 +95,18 @@ LL | impl Gettable for S {} = note: required for the cast from `Box>` to `Box>` error[E0277]: the trait bound `Foo: Copy` is not satisfied - --> $DIR/kindck-impl-type-params.rs:44:37 + --> $DIR/copy-bounds-impl-type-params.rs:46:37 | LL | let a: Box> = t; | ^ the trait `Copy` is not implemented for `Foo` | help: the trait `Gettable` is implemented for `S` - --> $DIR/kindck-impl-type-params.rs:12:1 + --> $DIR/copy-bounds-impl-type-params.rs:14:1 | LL | impl Gettable for S {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note: required for `S` to implement `Gettable` - --> $DIR/kindck-impl-type-params.rs:12:32 + --> $DIR/copy-bounds-impl-type-params.rs:14:32 | LL | impl Gettable for S {} | ---- ^^^^^^^^^^^ ^^^^ @@ -120,7 +120,7 @@ LL | struct Foo; // does not impl Copy | error: lifetime may not live long enough - --> $DIR/kindck-impl-type-params.rs:30:13 + --> $DIR/copy-bounds-impl-type-params.rs:32:13 | LL | fn foo<'a>() { | -- lifetime `'a` defined here diff --git a/tests/ui/kindck/kindck-inherited-copy-bound.rs b/tests/ui/traits/inherited-copy-bound.rs similarity index 75% rename from tests/ui/kindck/kindck-inherited-copy-bound.rs rename to tests/ui/traits/inherited-copy-bound.rs index 92c2b273c2c1..dd9fea3dcde3 100644 --- a/tests/ui/kindck/kindck-inherited-copy-bound.rs +++ b/tests/ui/traits/inherited-copy-bound.rs @@ -1,16 +1,14 @@ // Test that Copy bounds inherited by trait are checked. - use std::any::Any; -trait Foo : Copy { +trait Foo: Copy { fn foo(&self) {} } -impl Foo for T { -} +impl Foo for T {} -fn take_param(foo: &T) { } +fn take_param(foo: &T) {} fn a() { let x: Box<_> = Box::new(3); @@ -24,4 +22,4 @@ fn b() { //~^ ERROR E0038 } -fn main() { } +fn main() {} diff --git a/tests/ui/kindck/kindck-inherited-copy-bound.stderr b/tests/ui/traits/inherited-copy-bound.stderr similarity index 62% rename from tests/ui/kindck/kindck-inherited-copy-bound.stderr rename to tests/ui/traits/inherited-copy-bound.stderr index c15aabacddd1..c251d5922446 100644 --- a/tests/ui/kindck/kindck-inherited-copy-bound.stderr +++ b/tests/ui/traits/inherited-copy-bound.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `Box<{integer}>: Foo` is not satisfied - --> $DIR/kindck-inherited-copy-bound.rs:17:16 + --> $DIR/inherited-copy-bound.rs:15:16 | LL | take_param(&x); | ---------- ^^ the trait `Copy` is not implemented for `Box<{integer}>` @@ -7,30 +7,30 @@ LL | take_param(&x); | required by a bound introduced by this call | note: required for `Box<{integer}>` to implement `Foo` - --> $DIR/kindck-inherited-copy-bound.rs:10:14 + --> $DIR/inherited-copy-bound.rs:9:15 | -LL | impl Foo for T { - | ---- ^^^ ^ - | | - | unsatisfied trait bound introduced here +LL | impl Foo for T {} + | ---- ^^^ ^ + | | + | unsatisfied trait bound introduced here note: required by a bound in `take_param` - --> $DIR/kindck-inherited-copy-bound.rs:13:17 + --> $DIR/inherited-copy-bound.rs:11:18 | -LL | fn take_param(foo: &T) { } - | ^^^ required by this bound in `take_param` +LL | fn take_param(foo: &T) {} + | ^^^ required by this bound in `take_param` error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/kindck-inherited-copy-bound.rs:23:24 + --> $DIR/inherited-copy-bound.rs:21:24 | LL | let z = &x as &dyn Foo; | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/kindck-inherited-copy-bound.rs:6:13 + --> $DIR/inherited-copy-bound.rs:5:12 | -LL | trait Foo : Copy { - | --- ^^^^ ...because it requires `Self: Sized` +LL | trait Foo: Copy { + | --- ^^^^ ...because it requires `Self: Sized` | | | this trait is not dyn compatible... diff --git a/tests/ui/traits/next-solver/generalize/eagely-normalizing-aliases.rs b/tests/ui/traits/next-solver/generalize/eagely-normalizing-aliases.rs new file mode 100644 index 000000000000..463fe49e5531 --- /dev/null +++ b/tests/ui/traits/next-solver/generalize/eagely-normalizing-aliases.rs @@ -0,0 +1,26 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +// Regression test for trait-system-refactor-initiative#262 + +trait View {} +trait HasAssoc { + type Assoc; +} + +struct StableVec(T); +impl View for StableVec {} + +fn assert_view(f: F) -> F { f } + + +fn store(x: StableVec) +where + T: HasAssoc, + StableVec: View, +{ + let _: StableVec = assert_view(x); +} + +fn main() {} diff --git a/tests/ui/traits/send-trait-objects-basic.rs b/tests/ui/traits/send-trait-objects-basic.rs new file mode 100644 index 000000000000..c999d4d0f695 --- /dev/null +++ b/tests/ui/traits/send-trait-objects-basic.rs @@ -0,0 +1,55 @@ +// Test which trait objects and basic types are considered sendable, considering lifetimes. + +fn assert_send_static() {} +fn assert_send() {} + +trait Dummy {} + +fn test1<'a>() { + assert_send_static::<&'a dyn Dummy>(); + //~^ ERROR `&'a (dyn Dummy + 'a)` cannot be sent between threads safely [E0277] +} + +fn test2<'a>() { + assert_send_static::<&'a (dyn Dummy + Sync)>(); + //~^ ERROR: lifetime may not live long enough +} + +fn test3<'a>() { + assert_send_static::>(); + //~^ ERROR `(dyn Dummy + 'a)` cannot be sent between threads safely +} + +fn test4<'a>() { + assert_send::<*mut &'a isize>(); + //~^ ERROR `*mut &'a isize` cannot be sent between threads safely +} + +fn main() { + assert_send_static::<&'static (dyn Dummy + Sync)>(); + assert_send_static::>(); + + assert_send::<&'static dyn Dummy>(); + //~^ ERROR `&'static (dyn Dummy + 'static)` cannot be sent between threads safely [E0277] + assert_send::>(); + //~^ ERROR `dyn Dummy` cannot be sent between threads safely + assert_send::<&'static (dyn Dummy + Sync)>(); + assert_send::>(); + + // owned content is ok + assert_send::>(); + assert_send::(); + assert_send::>(); + + // but not if it owns a bad thing + assert_send::>(); + //~^ ERROR `*mut u8` cannot be sent between threads safely + + assert_send::<*mut isize>(); + //~^ ERROR `*mut isize` cannot be sent between threads safely +} + +fn object_ref_with_static_bound_not_ok() { + assert_send::<&'static (dyn Dummy + 'static)>(); + //~^ ERROR `&'static (dyn Dummy + 'static)` cannot be sent between threads safely [E0277] +} diff --git a/tests/ui/traits/send-trait-objects-basic.stderr b/tests/ui/traits/send-trait-objects-basic.stderr new file mode 100644 index 000000000000..0393f7bc19df --- /dev/null +++ b/tests/ui/traits/send-trait-objects-basic.stderr @@ -0,0 +1,127 @@ +error[E0277]: `&'a (dyn Dummy + 'a)` cannot be sent between threads safely + --> $DIR/send-trait-objects-basic.rs:9:26 + | +LL | assert_send_static::<&'a dyn Dummy>(); + | ^^^^^^^^^^^^^ `&'a (dyn Dummy + 'a)` cannot be sent between threads safely + | + = help: the trait `Sync` is not implemented for `(dyn Dummy + 'a)` + = note: required for `&'a (dyn Dummy + 'a)` to implement `Send` +note: required by a bound in `assert_send_static` + --> $DIR/send-trait-objects-basic.rs:3:26 + | +LL | fn assert_send_static() {} + | ^^^^ required by this bound in `assert_send_static` + +error[E0277]: `(dyn Dummy + 'a)` cannot be sent between threads safely + --> $DIR/send-trait-objects-basic.rs:19:26 + | +LL | assert_send_static::>(); + | ^^^^^^^^^^^^^^^^^^^ `(dyn Dummy + 'a)` cannot be sent between threads safely + | + = help: the trait `Send` is not implemented for `(dyn Dummy + 'a)` + = note: required for `std::ptr::Unique<(dyn Dummy + 'a)>` to implement `Send` +note: required because it appears within the type `Box<(dyn Dummy + 'a)>` + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL +note: required by a bound in `assert_send_static` + --> $DIR/send-trait-objects-basic.rs:3:26 + | +LL | fn assert_send_static() {} + | ^^^^ required by this bound in `assert_send_static` + +error[E0277]: `*mut &'a isize` cannot be sent between threads safely + --> $DIR/send-trait-objects-basic.rs:24:19 + | +LL | assert_send::<*mut &'a isize>(); + | ^^^^^^^^^^^^^^ `*mut &'a isize` cannot be sent between threads safely + | + = help: the trait `Send` is not implemented for `*mut &'a isize` +note: required by a bound in `assert_send` + --> $DIR/send-trait-objects-basic.rs:4:19 + | +LL | fn assert_send() {} + | ^^^^ required by this bound in `assert_send` + +error[E0277]: `&'static (dyn Dummy + 'static)` cannot be sent between threads safely + --> $DIR/send-trait-objects-basic.rs:32:19 + | +LL | assert_send::<&'static dyn Dummy>(); + | ^^^^^^^^^^^^^^^^^^ `&'static (dyn Dummy + 'static)` cannot be sent between threads safely + | + = help: the trait `Sync` is not implemented for `(dyn Dummy + 'static)` + = note: required for `&'static (dyn Dummy + 'static)` to implement `Send` +note: required by a bound in `assert_send` + --> $DIR/send-trait-objects-basic.rs:4:19 + | +LL | fn assert_send() {} + | ^^^^ required by this bound in `assert_send` + +error[E0277]: `dyn Dummy` cannot be sent between threads safely + --> $DIR/send-trait-objects-basic.rs:34:19 + | +LL | assert_send::>(); + | ^^^^^^^^^^^^^^ `dyn Dummy` cannot be sent between threads safely + | + = help: the trait `Send` is not implemented for `dyn Dummy` + = note: required for `std::ptr::Unique` to implement `Send` +note: required because it appears within the type `Box` + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL +note: required by a bound in `assert_send` + --> $DIR/send-trait-objects-basic.rs:4:19 + | +LL | fn assert_send() {} + | ^^^^ required by this bound in `assert_send` + +error[E0277]: `*mut u8` cannot be sent between threads safely + --> $DIR/send-trait-objects-basic.rs:45:19 + | +LL | assert_send::>(); + | ^^^^^^^^^^^^ `*mut u8` cannot be sent between threads safely + | + = help: the trait `Send` is not implemented for `*mut u8` + = note: required for `std::ptr::Unique<*mut u8>` to implement `Send` +note: required because it appears within the type `Box<*mut u8>` + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL +note: required by a bound in `assert_send` + --> $DIR/send-trait-objects-basic.rs:4:19 + | +LL | fn assert_send() {} + | ^^^^ required by this bound in `assert_send` + +error[E0277]: `*mut isize` cannot be sent between threads safely + --> $DIR/send-trait-objects-basic.rs:48:19 + | +LL | assert_send::<*mut isize>(); + | ^^^^^^^^^^ `*mut isize` cannot be sent between threads safely + | + = help: the trait `Send` is not implemented for `*mut isize` +note: required by a bound in `assert_send` + --> $DIR/send-trait-objects-basic.rs:4:19 + | +LL | fn assert_send() {} + | ^^^^ required by this bound in `assert_send` + +error[E0277]: `&'static (dyn Dummy + 'static)` cannot be sent between threads safely + --> $DIR/send-trait-objects-basic.rs:53:19 + | +LL | assert_send::<&'static (dyn Dummy + 'static)>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `&'static (dyn Dummy + 'static)` cannot be sent between threads safely + | + = help: the trait `Sync` is not implemented for `(dyn Dummy + 'static)` + = note: required for `&'static (dyn Dummy + 'static)` to implement `Send` +note: required by a bound in `assert_send` + --> $DIR/send-trait-objects-basic.rs:4:19 + | +LL | fn assert_send() {} + | ^^^^ required by this bound in `assert_send` + +error: lifetime may not live long enough + --> $DIR/send-trait-objects-basic.rs:14:5 + | +LL | fn test2<'a>() { + | -- lifetime `'a` defined here +LL | assert_send_static::<&'a (dyn Dummy + Sync)>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static` + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr b/tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr index 53920bc9e02e..fd07d5b09aeb 100644 --- a/tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr +++ b/tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr @@ -112,6 +112,9 @@ LL + let x: u16 = (S {}).method(0u16); error[E0308]: arguments to this function are incorrect --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:27:5 | +LL | fn main() { + | - this implicit `()` return type influences the call expression's return type +... LL | function(0u32, 8u8) | ^^^^^^^^ ---- --- expected `bool`, found `u8` | | diff --git a/tests/ui/union/union-no-derive-suggestion.rs b/tests/ui/union/union-no-derive-suggestion.rs new file mode 100644 index 000000000000..0ccbac3167f2 --- /dev/null +++ b/tests/ui/union/union-no-derive-suggestion.rs @@ -0,0 +1,37 @@ +//! Check that we do not suggest using `#[derive(...)]` for unions, +//! as some traits cannot be autoderived for them. +//@ dont-require-annotations: NOTE + +union U { //~ HELP consider annotating `U` with `#[derive(Clone)]` + //~| HELP consider annotating `U` with `#[derive(Copy)]` + //~| HELP the trait `Debug` is not implemented for `U` + //~| HELP the trait `Default` is not implemented for `U` + //~| HELP the trait `Hash` is not implemented for `U` + a: u8, +} + +fn x() {} +fn y() {} + +fn main() { + let u = U { a: 0 }; + // Debug + println!("{u:?}"); //~ ERROR `U` doesn't implement `Debug` + //~| NOTE manually `impl Debug for U` + // PartialEq + let _ = u == U { a: 0 }; //~ ERROR binary operation `==` cannot be applied to type `U` + //~| NOTE the trait `PartialEq` must be implemented + // PartialOrd + let _ = u < U { a: 1 }; //~ ERROR binary operation `<` cannot be applied to type `U` + //~| NOTE the trait `PartialOrd` must be implemented + // Default + let _: U = Default::default(); //~ ERROR the trait bound `U: Default` is not satisfied + // Hash + let mut h = std::collections::hash_map::DefaultHasher::new(); + std::hash::Hash::hash(&u, &mut h); //~ ERROR the trait bound `U: Hash` is not satisfied + + // Clone + x::(); //~ ERROR the trait bound `U: Clone` is not satisfied + // Copy + y::(); //~ ERROR the trait bound `U: Copy` is not satisfied +} diff --git a/tests/ui/union/union-no-derive-suggestion.stderr b/tests/ui/union/union-no-derive-suggestion.stderr new file mode 100644 index 000000000000..7a42b871b80d --- /dev/null +++ b/tests/ui/union/union-no-derive-suggestion.stderr @@ -0,0 +1,109 @@ +error[E0277]: `U` doesn't implement `Debug` + --> $DIR/union-no-derive-suggestion.rs:19:15 + | +LL | println!("{u:?}"); + | ^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | +help: the trait `Debug` is not implemented for `U` + --> $DIR/union-no-derive-suggestion.rs:5:1 + | +LL | union U { + | ^^^^^^^ + = note: manually `impl Debug for U` + +error[E0369]: binary operation `==` cannot be applied to type `U` + --> $DIR/union-no-derive-suggestion.rs:22:15 + | +LL | let _ = u == U { a: 0 }; + | - ^^ ---------- U + | | + | U + | +note: an implementation of `PartialEq` might be missing for `U` + --> $DIR/union-no-derive-suggestion.rs:5:1 + | +LL | union U { + | ^^^^^^^ must implement `PartialEq` +note: the trait `PartialEq` must be implemented + --> $SRC_DIR/core/src/cmp.rs:LL:COL + +error[E0369]: binary operation `<` cannot be applied to type `U` + --> $DIR/union-no-derive-suggestion.rs:25:15 + | +LL | let _ = u < U { a: 1 }; + | - ^ ---------- U + | | + | U + | +note: an implementation of `PartialOrd` might be missing for `U` + --> $DIR/union-no-derive-suggestion.rs:5:1 + | +LL | union U { + | ^^^^^^^ must implement `PartialOrd` +note: the trait `PartialOrd` must be implemented + --> $SRC_DIR/core/src/cmp.rs:LL:COL + +error[E0277]: the trait bound `U: Default` is not satisfied + --> $DIR/union-no-derive-suggestion.rs:28:16 + | +LL | let _: U = Default::default(); + | ^^^^^^^^^^^^^^^^^^ unsatisfied trait bound + | +help: the trait `Default` is not implemented for `U` + --> $DIR/union-no-derive-suggestion.rs:5:1 + | +LL | union U { + | ^^^^^^^ + +error[E0277]: the trait bound `U: Hash` is not satisfied + --> $DIR/union-no-derive-suggestion.rs:31:27 + | +LL | std::hash::Hash::hash(&u, &mut h); + | --------------------- ^^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | +help: the trait `Hash` is not implemented for `U` + --> $DIR/union-no-derive-suggestion.rs:5:1 + | +LL | union U { + | ^^^^^^^ + +error[E0277]: the trait bound `U: Clone` is not satisfied + --> $DIR/union-no-derive-suggestion.rs:34:9 + | +LL | x::(); + | ^ the trait `Clone` is not implemented for `U` + | +note: required by a bound in `x` + --> $DIR/union-no-derive-suggestion.rs:13:9 + | +LL | fn x() {} + | ^^^^^ required by this bound in `x` +help: consider annotating `U` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | union U { + | + +error[E0277]: the trait bound `U: Copy` is not satisfied + --> $DIR/union-no-derive-suggestion.rs:36:9 + | +LL | y::(); + | ^ the trait `Copy` is not implemented for `U` + | +note: required by a bound in `y` + --> $DIR/union-no-derive-suggestion.rs:14:9 + | +LL | fn y() {} + | ^^^^ required by this bound in `y` +help: consider annotating `U` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | union U { + | + +error: aborting due to 7 previous errors + +Some errors have detailed explanations: E0277, E0369. +For more information about an error, try `rustc --explain E0277`. diff --git a/typos.toml b/typos.toml index 3c95a45d572d..82e2b98f2e49 100644 --- a/typos.toml +++ b/typos.toml @@ -20,6 +20,7 @@ extend-exclude = [ # right now. Entries should look like `mipsel = "mipsel"`. # # tidy-alphabetical-start +anser = "anser" # an ANSI parsing package used by rust-analyzer arange = "arange" # short for A-range childs = "childs" clonable = "clonable" @@ -29,10 +30,12 @@ makro = "makro" # deliberate misspelling to avoid `macro` keyword misformed = "misformed" moreso = "moreso" numer = "numer" # short for numerator, not a typo for "number" +old-skool = "old-skool" # variant spelling of "old-school" optin = "optin" # short for opt-in publically = "publically" rplace = "rplace" # short for R-place splitted = "splitted" +sythetic = "sythetic" # typo in vendored LLVM sources taits = "taits" # lowercase for TAITs (type alias impl trait) targetting = "targetting" unparseable = "unparseable"