Merge pull request #2802 from rust-lang/rustc-pull
Rustc pull update
diff --git a/Cargo.lock b/Cargo.lock
index 98567f85..539f66f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1140,9 +1140,9 @@
[[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 662bcc5..f3b2a64 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 a7bf1c9..51994c2 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 cf3f331..9483948 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 6cffd8e..fd05ed0 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 050e725..0044913 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<hir::Attribute> {
- let defs_orig_attrs = ids
- .path
- .iter()
- .map(|def_id| (*def_id, self.parse_local_original_attrs(*def_id)))
- .collect::<Vec<_>>();
-
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::<Vec<_>>()
}
- fn parse_local_original_attrs(&self, def_id: DefId) -> Option<Vec<hir::Attribute>> {
- 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<DelegationIds, ErrorGuaranteed> {
+ fn get_sig_id(&self, mut node_id: NodeId, span: Span) -> Result<DefId, ErrorGuaranteed> {
let mut visited: FxHashSet<NodeId> = 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<DefId> {
// 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 6bfe73f..d6d8819 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<T> {
/// User-specified args are present: `reuse foo::<String>;`.
@@ -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<Item = hir::GenericParam<'hir>> {
// 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<Item = hir::WherePredicate<'hir>> {
// 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<Generics> {
- 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<Generics> {
// 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 5032d7d..920ca8a 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<Expr>]) -> &'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 602635a..1f1f86f 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 {
/// <core::fmt::Argument>::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 e2ac38c..fa10309 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<LocalDefId, hir::MaybeOwner<'hir>>),
+ Map(&'a mut FxIndexMap<LocalDefId, hir::MaybeOwner<'hir>>),
+}
+
+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<LocalDefId, AstOwner<'a>>,
- pub(super) owners: &'a mut IndexVec<LocalDefId, hir::MaybeOwner<'hir>>,
+ 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<Item>],
@@ -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 e8092b5..a2be51c 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<LocalDefId, AstOwner<'a>>,
-
- 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<LocalDefId, AstOwner<'a>>,
- 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<Vec<usize>> {
+struct ResolverDelayedAstLowering<'a, 'tcx> {
+ node_id_to_def_id: NodeMap<LocalDefId>,
+ partial_res_map: NodeMap<PartialRes>,
+ 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<Vec<usize>> {
+ self.base.legacy_const_generic_args(expr, tcx)
+ }
+
+ fn get_partial_res(&self, id: NodeId) -> Option<PartialRes> {
+ self.partial_res_map.get(&id).copied().or_else(|| self.base.get_partial_res(id))
+ }
+
+ fn get_import_res(&self, id: NodeId) -> PerNS<Option<Res<NodeId>>> {
+ self.base.get_import_res(id)
+ }
+
+ fn get_label_res(&self, id: NodeId) -> Option<NodeId> {
+ self.base.get_label_res(id)
+ }
+
+ fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes> {
+ 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<LocalDefId> {
+ 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<Vec<usize>> {
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<Vec<
return None;
}
- let def_id = self.partial_res_map.get(&expr.id)?.full_res()?.opt_def_id()?;
+ let def_id = self.get_partial_res(expr.id)?.full_res()?.opt_def_id()?;
// We only support cross-crate argument rewriting. Uses
// within the same crate should be updated to use the new
@@ -304,9 +367,42 @@ fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes> {
///
/// 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<LocalDefId> {
+ 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<LocalDefId>,
+fn index_crate<'a, 'b>(
+ resolver: &'b impl ResolverAstLoweringExt<'a>,
krate: &'a Crate,
) -> IndexVec<LocalDefId, AstOwner<'a>> {
- 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<LocalDefId>,
+ struct Indexer<'a, 'b, R> {
+ resolver: &'b R,
index: IndexVec<LocalDefId, AstOwner<'a>>,
}
- 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<LocalDefId> = 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<LocalDefId> {
- 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 c1c1397..aece455 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 fe85302..139140a 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 4ba5dc5..a72ed84 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 9b4ff2b..0c59287 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 3407feb..e997fdf 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 d2f743f..badf696 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 7377159..f3612af 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 a8db114..652a783 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<Attrib
return None;
};
let Some(classname) = nv.value_as_str() else {
- // `#[rustc_objc_class = ...]` is expected to be used as an implementatioin detail
+ // `#[rustc_objc_class = ...]` is expected to be used as an implementation detail
// inside a standard library macro, but `cx.expected_string_literal` exposes too much.
// Use a custom error message instead.
cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });
@@ -201,7 +201,7 @@ fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<Attrib
return None;
};
let Some(methname) = nv.value_as_str() else {
- // `#[rustc_objc_selector = ...]` is expected to be used as an implementatioin detail
+ // `#[rustc_objc_selector = ...]` is expected to be used as an implementation detail
// inside a standard library macro, but `cx.expected_string_literal` exposes too much.
// Use a custom error message instead.
cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });
diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs
index 8f114b3..8abcaeb 100644
--- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs
@@ -22,6 +22,7 @@
pub(crate) mod do_not_recommend;
pub(crate) mod on_const;
+pub(crate) mod on_move;
pub(crate) mod on_unimplemented;
#[derive(Copy, Clone)]
@@ -32,6 +33,8 @@ pub(crate) enum Mode {
DiagnosticOnUnimplemented,
/// `#[diagnostic::on_const]`
DiagnosticOnConst,
+ /// `#[diagnostic::on_move]`
+ DiagnosticOnMove,
}
fn merge_directives<S: Stage>(
@@ -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 0000000..006b3b6
--- /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<Span>,
+ 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<S: Stage> AttributeParser<S> for OnMoveParser {
+ const ATTRIBUTES: AcceptMapping<Self, S> = &[(
+ &[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<AttributeKind> {
+ 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 ac50fe3..8eb902b 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<Attrib
let dialect = parse_dialect(cx, dialect, &mut failed);
let phase = parse_phase(cx, phase, &mut failed);
+ check_custom_mir(cx, dialect, phase, &mut failed);
if failed {
return None;
@@ -138,3 +140,51 @@ fn parse_phase<S: Stage>(
Some((phase, span))
}
+
+fn check_custom_mir<S: Stage>(
+ 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 6ddd9f2..25b295c 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<S: Stage>(
}
}
-// 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<S: Stage>(
cx: &AcceptContext<'_, '_, S>,
@@ -451,6 +452,16 @@ pub(crate) fn parse_unstability<S: Stage>(
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 190568b..259a73d 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 f75f63a..fa66dec 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<T>(
emit_errors: ShouldEmit,
parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser) -> Option<T>,
template: &AttributeTemplate,
+ allow_expr_metavar: AllowExprMetavar,
) -> Option<T> {
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<T>(
&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 07f2463..6e8a500 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<Self> {
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::<core::convert::Infallible>().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<Self, Diag<'sess>> {
- 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 751b8f8..bab8300 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 0d3c554..4fcb5f3 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<String>,
+ primary_message: Option<String>,
) -> 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 6411215..7bbce73 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 9aa8f7a..47f14dc 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 e2d684e..152a767 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 3ebde94..c4a4580 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<CfgEntry,
let meta = MetaItemOrLitParser::parse_single(
&mut parser,
ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
+ AllowExprMetavar::Yes,
)
.map_err(|diag| diag.emit())?;
let cfg = AttributeParser::parse_single_args(
diff --git a/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch b/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch
index 340f6cc..2a6bfe8 100644
--- a/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch
+++ b/compiler/rustc_codegen_cranelift/patches/0029-sysroot_tests-disable-f16-math.patch
@@ -4,13 +4,13 @@
Subject: [PATCH] Disable f16 math tests for cranelift
---
- coretests/tests/floats/mod.rs | 26 +++++++++++++-------------
+ coretests/tests/num/floats.rs | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/coretests/tests/floats/mod.rs b/coretests/tests/floats/mod.rs
index c61961f8584..d7b4fa20322 100644
---- a/coretests/tests/floats/mod.rs
-+++ b/coretests/tests/floats/mod.rs
+--- a/coretests/tests/num/floats.rs
++++ b/coretests/tests/num/floats.rs
@@ -1534,7 +1534,7 @@ fn s_nan() -> Float {
name: powf,
attrs: {
@@ -128,6 +128,5 @@
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 efd4e55..ffa5827 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 2423880..45e16a7 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 <https://github.com/rust-lang/rust/issues/125109> et al. (full
// list at <https://github.com/rust-lang/rust/issues/116909>)
(Arch::PowerPC | Arch::PowerPC64, _) => false,
- // ABI unsupported <https://github.com/llvm/llvm-project/issues/41838>
- (Arch::Sparc, _) => false,
+ // ABI unsupported <https://github.com/llvm/llvm-project/issues/41838> (fixed in llvm22)
+ (Arch::Sparc, _) if major < 22 => false,
// MinGW ABI bugs <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=115054>
(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 1a0f66d..5c14d6f 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 e2193a9..997077a 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<T: ?Sized + PointeeSized + DynSend>() {}
pub fn assert_dyn_send_val<T: ?Sized + PointeeSized + DynSend>(_t: &T) {}
pub fn assert_dyn_send_sync_val<T: ?Sized + PointeeSized + DynSync + DynSend>(_t: &T) {}
-#[derive(Copy, Clone)]
-pub struct FromDyn<T>(T);
-
-impl<T> FromDyn<T> {
- #[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<O>(&self, val: O) -> FromDyn<O> {
- // 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<T: DynSend> Send for FromDyn<T> {}
-
-// `FromDyn` is `Sync` if `T` is `DynSync`, since it ensures that sync::is_dyn_thread_safe() is true.
-unsafe impl<T: DynSync> Sync for FromDyn<T> {}
-
-impl<T> std::ops::Deref for FromDyn<T> {
- type Target = T;
-
- #[inline(always)]
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl<T> std::ops::DerefMut for FromDyn<T> {
- #[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<dyn Send / Sync>)
diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index 327c28f..3d5bc85 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,6 +66,8 @@ 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;
@@ -72,6 +76,12 @@ mod mode {
// Whether thread safety is enabled (due to running under multiple threads).
#[inline]
+ pub fn check_dyn_thread_safe() -> Option<FromDyn<()>> {
+ 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 {
match DYN_THREAD_SAFE_MODE.load(Ordering::Relaxed) {
DYN_NOT_THREAD_SAFE => false,
@@ -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>(T);
+
+ impl<T> FromDyn<T> {
+ #[inline(always)]
+ pub fn derive<O>(&self, val: O) -> FromDyn<O> {
+ // 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<T: DynSend> Send for FromDyn<T> {}
+
+ // `FromDyn` is `Sync` if `T` is `DynSync`, since it ensures that sync::is_dyn_thread_safe() is true.
+ unsafe impl<T: DynSync> Sync for FromDyn<T> {}
+
+ impl<T> std::ops::Deref for FromDyn<T> {
+ type Target = T;
+
+ #[inline(always)]
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+
+ impl<T> std::ops::DerefMut for FromDyn<T> {
+ #[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 2ab4a7f..b112ecc 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<A, B, RA, RB>(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<A, B, RA: DynSend, RB: DynSend>(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<I: DynSend>(
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<I: DynSend, T: IntoIterator<Item = I>>(
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<T: IntoIterator, E: DynSend>(
<T as IntoIterator>::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<I: DynSend, T: IntoIterator<Item = I>, 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<I>, Option<R>)> =
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<I: DynSend, T: IntoIterator<Item = I>, R: DynSend, C: FromIterato
}
pub fn broadcast<R: DynSend>(op: impl Fn(usize) -> R + DynSync) -> Vec<R> {
- 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 8e4039b..ec5951e 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 76a9a6f9..640d074 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 3a2f548..d74450e 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 c508344..e8ca20d 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 b5b9da1..a18ddff 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<Box<Directive>>,
},
+ /// Represents `#[diagnostic::on_move]`
+ OnMove {
+ span: Span,
+ directive: Option<Box<Directive>>,
+ },
+
/// Represents `#[rustc_on_unimplemented]` and `#[diagnostic::on_unimplemented]`.
OnUnimplemented {
span: Span,
/// None if the directive was malformed in some way.
directive: Option<Box<Directive>>,
},
-
/// 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 27128f6..c19fc69 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 3959ee7..21abb55 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 7cce0ed..9526143 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<LocalDefId, MaybeOwner<'hir>>,
- // Only present when incr. comp. is enabled.
- pub opt_hir_hash: Option<Fingerprint>,
-}
-
#[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 a3f4415..58649a6 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<HirCtx: crate::HashStableContext> HashStable<HirCtx> 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<HirCtx: crate::HashStableContext> HashStable<HirCtx> 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 dd685c4..07c33eb 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 5656c45..f050fe2 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 2bba1b7..18c9bac 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 17c7c4b..6b77169 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 9faa75e..0471fd9 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 9d496c6..893cd02 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 70e3d7d..3487286 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -140,6 +140,9 @@ pub fn sup<T>(
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<T>(
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<T>(
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 b573065..e7082f9 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<T>(&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<T>(&self, value: T) -> T
+ where
+ T: TypeFoldable<TyCtxt<'tcx>>,
+ {
+ 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<T>(&self, value: T) -> T
where
T: TypeFoldable<TyCtxt<'tcx>>,
@@ -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 69c090b..b2d5913 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<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
+ pub fn instantiate_ty_var<R: PredicateEmittingRelation<Self>>(
&self,
relation: &mut R,
target_is_expected: bool,
@@ -61,29 +61,56 @@ pub fn instantiate_ty_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
) -> 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<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
// 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 `<?0 as Trait>::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<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
}
}
} 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<R: PredicateEmittingRelation<InferCtxt<'tcx>
// 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<R: PredicateEmittingRelation<InferCtxt<'tcx>
fn generalize<T: Into<Term<'tcx>> + Relate<TyCtxt<'tcx>>>(
&self,
span: Span,
- structurally_relate_aliases: StructurallyRelateAliases,
+ initial_state: GeneralizerState,
target_vid: impl Into<TermVid>,
ambient_variance: ty::Variance,
source_term: T,
+ normalize: &mut dyn FnMut(ty::AliasTy<'tcx>) -> Ty<'tcx>,
) -> RelateResult<'tcx, Generalization<T>> {
assert!(!source_term.has_escaping_bound_vars());
let (for_universe, root_vid) = match target_vid.into() {
@@ -264,13 +302,13 @@ fn generalize<T: Into<Term<'tcx>> + Relate<TyCtxt<'tcx>>>(
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 `<T as Bar<<?0 as Foo>::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
- /// `<T as Bar<<?0 as Foo>::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<Ty<'tcx>, 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<T: Relate<TyCtxt<'tcx>>>(
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 a05e2d4..7e480df 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 96a0375..67f9dc6 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<<InferCtxt<'tcx> as rustc_type_ir::InferCtxtLike>::Interner>,
+ ) -> <<InferCtxt<'tcx> 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 13df23a..917d7b6 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>, 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 6709c82..56cae2c 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<T, E, F>(&self, f: F) -> Result<T, E>
// 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 cf62d42..28f5109 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 6c2924d..f9d583b 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 c5344ee..0cd0275 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 458553f..d5cd26d 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 fe95a68..a7c5943 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 d8b62e8..d5b5a50 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -3020,46 +3020,6 @@ pub(crate) struct IllFormedAttributeInput {
}
#[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<UnicodeCharNoteSub>,
- #[subdiagnostic]
- pub suggestions: Option<UnicodeTextFlowSuggestion>,
-
- 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<Span>,
-}
-
-#[derive(Diagnostic)]
#[diag(
"absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition"
)]
@@ -3193,52 +3153,6 @@ pub(crate) struct PatternsInFnsWithoutBodySub {
}
#[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 <https://github.com/rust-lang/rust/issues/89122> for more information")]
pub(crate) struct DeprecatedWhereClauseLocation {
@@ -3424,28 +3338,6 @@ pub(crate) enum MutRefSugg {
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> {
#[subdiagnostic]
@@ -3954,6 +3846,11 @@ pub(crate) struct IgnoredDiagnosticOption {
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")]
pub(crate) struct MalformedOnUnimplementedAttrLint {
@@ -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 1492df5..e2e4ffc 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 a803d86..b75236b 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<Ident>,
arena_cache: Option<Ident>,
cache_on_disk_if: Option<CacheOnDiskIf>,
depth_limit: Option<Ident>,
desc: Desc,
eval_always: Option<Ident>,
feedable: Option<Ident>,
+ no_force: Option<Ident>,
no_hash: Option<Ident>,
separate_provide_extern: Option<Ident>,
// tidy-alphabetical-end
@@ -156,8 +156,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
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;
@@ -477,11 +479,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(),
"Query {name} cannot be both `feedable` and `eval_always`."
diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs
index 49ad6bb..81f3041 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 d410d9a..d0d7d58 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 fc2a7fb..499c6dae 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 82f8eb4..ad56e46 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<LocalDefId, MaybeOwner<'hir>>,
+ // Ids of delayed AST owners which are lowered through `lower_delayed_owner` query.
+ pub delayed_ids: FxIndexSet<LocalDefId>,
+ // 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<ast::Crate>)>,
+ // Only present when incr. comp. is enabled.
+ pub opt_hir_hash: Option<Fingerprint>,
+}
+
+impl<'hir> Crate<'hir> {
+ pub fn new(
+ owners: IndexVec<LocalDefId, MaybeOwner<'hir>>,
+ delayed_ids: FxIndexSet<LocalDefId>,
+ delayed_resolver: Steal<(ResolverAstLowering<'hir>, Arc<ast::Crate>)>,
+ opt_hir_hash: Option<Fingerprint>,
+ ) -> 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<HirCtx: HashStableContext> HashStable<HirCtx> 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 60327be..1f339ea 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 4dd68db..c5dab80 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<String> {
diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs
index c2525d2..402c448 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<Value = Erased<Result<T, ErrorGuaranteed>>>,
Result<T, ErrorGuaranteed>: Erasable,
{
+ let convert = |value: Erased<Result<T, ErrorGuaranteed>>| -> 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 ad9b9de..50c9136 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 d4e7bb5..dcf7a95 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<DefId> };
(LocalDefId) => { impl $crate::query::IntoQueryKey<LocalDefId> };
($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> =
+ <Key<'tcx> as $crate::query::QueryKey>::Cache<Erased<Value<'tcx>>>;
+
/// 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> =
- <Key<'tcx> as $crate::query::QueryKey>::Cache<Erased<Value<'tcx>>>;
-
// 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<DefKind> {
// 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<DefKind> {
- 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<DefKind>
+ {
+ 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<D
/// Holds a `QueryVTable` for each query.
pub struct QueryVTables<'tcx> {
$(
- 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 8811657..51f33691 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<Pat<'tcx>>,
+ #[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 c17e155..aa1b6b1 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 be6dfb2..7be225a 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<Ty<'tcx>>,
/// 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 5642878..58a2edc 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 b454689..5bb11c9 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 d35b755..f072b26 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<LintBuffer>,
- /// Information about functions signatures for delegation items expansion
- pub delegation_fn_sigs: LocalDefIdMap<DelegationFnSig>,
// Information about delegations which is used when handling recursive delegations
pub delegation_infos: LocalDefIdMap<DelegationInfo>,
}
-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 ad6c1f7..c67d99a 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 7698af4..cbe31fd 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 d7edf82..d4f5fe8 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 ee35539..aed0c6e 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 b4eedb1..019af24 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<u8>`.
- (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 b108dff..ea27252 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 c646b0f..b79bda2 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::<ExprKind<'tcx>>;
+ let mut expr = None::<ExprKind<'_>>;
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 60cb509..7f4b70b 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<Thir<'_>>, ExprId), ErrorGuaranteed> {
+) -> Result<(&'tcx Steal<Thir<'tcx>>, 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<Pat<'tcx>> {
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<Pat<'tcx>> = 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 ff9d456..ff4778d 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 67cde0e..7d4e25c 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<PatMigration<'tcx>>,
}
-#[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<Pat<'tcx>> {
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<Pat<'tcx>> {
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<Pat<'tc
hir::PatKind::Or(pats) => 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 27dec99..ea34e5f 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 ce2be24..e469451 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 cd74e87..456fe52 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 6841fe1..fedb639 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<I, I::Predicate>,
@@ -1018,7 +1018,8 @@ pub(super) fn relate<T: Relate<I>>(
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<T: Relate<I>>(
/// 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<T: Relate<I>>(
- &self,
+ pub(super) fn relate_and_get_goals<T: Relate<I>>(
+ &mut self,
param_env: I::ParamEnv,
lhs: T,
+ variance: ty::Variance,
rhs: T,
) -> Result<Vec<Goal<I, I::Predicate>>, 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<I>| {
+ 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<T: TypeFoldable<I> + Copy>(
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index f4f7182..0e852d2 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<Self> {
}
}
}
+
+#[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<UnicodeCharNoteSub>,
+ #[subdiagnostic]
+ pub suggestions: Option<UnicodeTextFlowSuggestion>,
+
+ 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<Span>,
+}
+
+#[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 cd90655..2223e6a 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 adfc68f..7d1a066 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<Expr>> {
_ => 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 bec6ab7..a8ca801 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 228f21c..5e84122 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};
@@ -873,17 +872,6 @@ pub(crate) struct CannotStabilizeDeprecated {
}
#[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> {
#[primary_span]
@@ -1275,28 +1263,6 @@ pub(crate) struct ReprAlignShouldBeAlignStatic {
}
#[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 {
#[primary_span]
@@ -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 dc38f2d..0226c54 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 b0ea1e0..30fbc5c 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<VtblEntry> {
+ 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<VtblEntry> {
+ 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 8205d29..7ec8b68 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<VtblEntry> {
+ 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<VtblEntry> {
+ 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 4bf98c4..31cc6bd 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 4418d68..359769d 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<AssocItem> {
};
assoc_items
}
+
+ /// Get all vtable entries of a trait.
+ pub fn vtable_entries(&self, trait_ref: TraitRef<'tcx>) -> Vec<VtblEntry<'tcx>> {
+ 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<VtblEntry<'tcx>> {
+ 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 597c8b2..44b92dc 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 {
- <Q::Cache as QueryCache>::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 = <Q::Cache as QueryCache>::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::<Q>),
+ force_from_dep_node_fn: (can_recover && !is_no_force)
+ .then_some(force_from_dep_node_inner::<Q>),
promote_from_disk_fn: (can_recover && is_cache_on_disk)
.then_some(promote_from_disk_inner::<Q>),
}
@@ -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 aea0bb4..bbd55e9 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::<C, true>(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 29877c8..213fc79 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<Ab
result
}
-/// Look for query cycles by doing a depth first search starting at `query`.
+/// Looks for a query cycle by doing a depth first search starting at `query`.
/// `span` is the reason for the `query` to execute. This is initially DUMMY_SP.
/// If a cycle is detected, this initial value is replaced with the span causing
-/// the cycle.
-fn cycle_check<'tcx>(
+/// 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 caf4c7d..56b7534 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 80538ae..d9d42cf 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 5aa0965..24c923d 100644
--- a/compiler/rustc_resolve/src/errors.rs
+++ b/compiler/rustc_resolve/src/errors.rs
@@ -561,6 +561,10 @@ pub(crate) struct ExpectedModuleFound {
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 {
#[primary_span]
@@ -884,6 +888,21 @@ pub(crate) struct UnexpectedResChangeTyToConstParamSugg {
}
#[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",
applicability = "has-placeholders",
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 453fe9d..a484a51 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<AssocItem>]) {
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<Res>| {
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<String> {
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 80e7a95..a8ca4fa 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<ast::NodeId>;
@@ -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<const A: u8, const X: u8, const P: u32>;
+ ///
+ /// impl Foo for C<A, X, P> {}
+ /// // ^ the struct `C` in `C<A, X, P>` is used as the self type
+ /// // ^ ^ ^ but A, X and P are not declared on the impl
+ ///
+ /// Suggested fix:
+ ///
+ /// impl<const A: u8, const X: u8, const P: u32> Foo for C<A, X, P> {}
+ ///
+ /// Current behavior (suggestions are emitted one-by-one):
+ ///
+ /// impl<const A: u8> Foo for C<A, X, P> {}
+ /// impl<const X: u8> Foo for C<A, X, P> {}
+ /// impl<const P: u32> Foo for C<A, X, P> {}
+ ///
+ /// Ideally the suggestion should aggregate them into a single line:
+ ///
+ /// impl<const A: u8, const X: u8, const P: u32> Foo for C<A, X, P> {}
+ /// ```
+ ///
+ 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<Diag<'tcx>> {
+ 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<A, X, 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<A, X, C> -> its position
+ // e.g NodeId(A) -> 0, NodeId(X) -> 1, NodeId(C) -> 2
+ let usage_to_pos: FxHashMap<NodeId, usize> = 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<A, X, C>
+ // e.g for missing `B` in `C<A, B, 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::<Vec<_>>()
+ .join("::");
+
+ // Find the first impl param whose position in C<A, X, 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<A, C> -> impl<A, const B: u8, C>
+ (
+ 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<A, B> -> impl<A, B, const C: u8>
+ (
+ last.span().shrink_to_hi(),
+ format!(", const {}: {}", target_ident, full_type),
+ )
+ }
+ None => {
+ // No generics at all on impl
+ // e.g impl Foo for C<A> -> impl<const A: u8> Foo for C<A>
+ (
+ impl_.generics.span.shrink_to_hi(),
+ format!("<const {}: {}>", 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<Diag<'tcx>>) {
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 7c94219..27d4182 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<DefId>, Vec<Visibility<DefId>>)> = Default::default(),
+ /// for all the struct
+ /// it's not used during normal resolution, only for better error reporting.
+ struct_generics: LocalDefIdMap<Generics> = 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 d6c708f..6ae9d3a 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 cb3f736..d1899de 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -518,17 +518,6 @@ pub(crate) struct FailedToCreateProfiler {
}
#[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 <https://github.com/rust-lang/rust/issues/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}`")]
#[note("manually setting a built-in cfg can and does create incoherent behaviors")]
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 98731a2..1741dde 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 5db8ed8..fb1b3c8 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<O> = fn(&mut O, v: Option<&str>) -> bool;
type OptionDescrs<O> = &'static [OptionDesc<O>];
+/// Indicates whether a removed option should warn or error.
+enum RemovedOption {
+ Warn,
+ Err,
+}
+
pub struct OptionDesc<O> {
name: &'static str,
setter: OptionSetter<O>,
@@ -712,7 +718,7 @@ pub struct OptionDesc<O> {
type_desc: &'static str,
// description for option from options table
desc: &'static str,
- is_deprecated_and_do_nothing: bool,
+ removed: Option<RemovedOption>,
tmod: Option<OptionsTargetModifiers>,
}
@@ -743,18 +749,18 @@ fn build_options<O: Default>(
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<O: Default>(
#[allow(non_upper_case_globals)]
mod desc {
+ pub(crate) const parse_ignore: &str = "<ignored>"; // 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<Align>, 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<CodeModel> = (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<Align>, v: Option<&str>) -> bool {
inline_threshold: Option<u32> = (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<Align>, 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<Align>, 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<SplitDebuginfo> = (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<Align>, v: Option<&str>) -> bool {
(default: no)"),
box_noalias: bool = (true, parse_bool, [TRACKED],
"emit noalias metadata for box (default: yes)"),
- branch_protection: Option<BranchProtection> = (None, parse_branch_protection, [TRACKED],
+ branch_protection: Option<BranchProtection> = (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 2b15202..a8210f2 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<MultiSpan>,
+ 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 8290bdf9..fece46d 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 286fea7..738c9b9 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 5cb8922..7fcaea3 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
@@ -468,6 +476,25 @@ pub fn emit_inference_failure_err(
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<Ty<'tcx>>,
+ ) -> Diag<'a> {
let term = self.resolve_vars_if_possible(term);
let arg_data = self
.extract_inference_diagnostics_data(term, ty::print::RegionHighlightMode::default());
@@ -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<Ty<'tcx>>,
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<Ty<'tcx>>,
) -> 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 7de8891..7f5ed9e 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 d5383fd..6bf0817 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 d08e0fa..4ae6584 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 ee2f8d9..4a6d5eb 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 5d200c4..c7699f3 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<TyCtxt<'tcx>>,
@@ -27,6 +35,46 @@ fn evaluate_root_goal_for_proof_tree_raw<'tcx>(
)
}
+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::<TypeErasedInfcx<'a, 'tcx>, &'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 = Providers { evaluate_root_goal_for_proof_tree_raw, ..*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 1d44407..49e0bdd 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 cef964a..9fb16af 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 78f3a19..7a84a87 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<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
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 10c53c8..b00f9ee 100644
--- a/compiler/rustc_type_ir/Cargo.toml
+++ b/compiler/rustc_type_ir/Cargo.toml
@@ -7,7 +7,7 @@
# 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 fc8b39f..0b0f0fd 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<T>` 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<I: Interner, T> {
@@ -365,12 +361,7 @@ fn visit_region(&mut self, r: I::Region) -> Self::Result {
/// `instantiate`.
///
/// See <https://rustc-dev-guide.rust-lang.org/ty_module/early_binder.html> 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 64b87fa..72d54c2 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>) -> I::Ty;
}
pub fn super_combine_tys<Infcx, I, R>(
diff --git a/compiler/rustc_type_ir/src/relate/solver_relating.rs b/compiler/rustc_type_ir/src/relate/solver_relating.rs
index 82ee4f7..541b253 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<T: Relate<Self::Interner>>(
variance: ty::Variance,
rhs: T,
span: <Self::Interner as Interner>::Span,
+ normalize: &mut dyn FnMut(ty::AliasTy<Self::Interner>) -> <Self::Interner as Interner>::Ty,
) -> Result<
Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
TypeError<Self::Interner>,
@@ -32,40 +33,46 @@ fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
>;
}
-impl<Infcx: InferCtxtLike> RelateExt for Infcx {
- fn relate<T: Relate<Self::Interner>>(
+impl<I: Interner, Infcx: InferCtxtLike<Interner = I>> RelateExt for Infcx {
+ fn relate<T: Relate<I>>(
&self,
- param_env: <Self::Interner as Interner>::ParamEnv,
+ param_env: I::ParamEnv,
lhs: T,
variance: ty::Variance,
rhs: T,
- span: <Self::Interner as Interner>::Span,
- ) -> Result<
- Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
- TypeError<Self::Interner>,
- > {
- let mut relate =
- SolverRelating::new(self, StructurallyRelateAliases::No, variance, param_env, span);
+ span: I::Span,
+ normalize: &mut dyn FnMut(ty::AliasTy<I>) -> I::Ty,
+ ) -> Result<Vec<Goal<I, I::Predicate>>, TypeError<I>> {
+ 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<T: Relate<Self::Interner>>(
+ fn eq_structurally_relating_aliases<T: Relate<I>>(
&self,
- param_env: <Self::Interner as Interner>::ParamEnv,
+ param_env: I::ParamEnv,
lhs: T,
rhs: T,
- span: <Self::Interner as Interner>::Span,
- ) -> Result<
- Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
- TypeError<Self::Interner>,
- > {
+ span: I::Span,
+ ) -> Result<Vec<Goal<I, I::Predicate>>, TypeError<I>> {
+ // 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<T: Relate<Self::Interner>>(
/// 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>) -> I::Ty,
goals: Vec<Goal<I, I::Predicate>>,
/// 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>) -> 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>) -> I::Ty {
+ (self.normalize)(alias)
+ }
}
diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs
index aa19013..bc668f7 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 3eb8ca4..99ebcb4 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<I: Iterator<Item = T>>(&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 b3dc435..49d7487 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 05d8e0d..ecd1e34 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 d3d6495..dfa27e3 100644
--- a/library/core/src/fmt/num.rs
+++ b/library/core/src/fmt/num.rs
@@ -823,7 +823,7 @@ fn enc_16lsd<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>], 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<const OFFSET: usize>(buf: &mut [MaybeUninit<u8>], 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 164c274..35f93d8f 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 becac51..c79e8fc 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 27a53d4..c9b0cc5 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<F: RawFloat>(&self) -> bool {
+ fn can_use_fast_path<F: Lemire>(&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<F: RawFloat>(&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<F: RawFloat>(&self) -> Option<F> {
+ pub fn try_fast_path<F: Lemire>(&self) -> Option<F> {
// Here we need to work around <https://github.com/rust-lang/rust/issues/114479>.
// 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 c3f2723..f89d16c 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:
/// <https://arxiv.org/abs/2101.11408.pdf>.
-pub fn compute_float<F: RawFloat>(q: i64, mut w: u64) -> BiasedFp {
+pub fn compute_float<F: 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 3f5724a..76b8d41 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<F: RawFloat>(x: BiasedFp) -> F {
+fn biased_fp_to_float<F: FloatExt>(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<F: RawFloat>(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<F: RawFloat>(s: &str) -> Result<F, ParseFloatError> {
+pub fn dec2flt<F: Lemire>(s: &str) -> Result<F, ParseFloatError> {
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 e4049bc..ee55ead 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<Decimal> {
}
/// Try to parse a special, non-finite float.
-pub(crate) fn parse_inf_nan<F: RawFloat>(s: &[u8], negative: bool) -> Option<F> {
+pub(crate) fn parse_inf_nan<F: Float>(s: &[u8], negative: bool) -> Option<F> {
// 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 089b12f..f1b2525 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: <https://arxiv.org/pdf/2101.11408.pdf#section.11>.
-pub(crate) fn parse_long_mantissa<F: RawFloat>(s: &[u8]) -> BiasedFp {
+pub(crate) fn parse_long_mantissa<F: Float>(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 3d6ad66..5ccc91f 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 d35409f..6fccfd1 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 21aabdc..7b84f7a 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<T: Copy>: Copy {
@@ -12,11 +16,11 @@ pub trait CastInto<T: Copy>: 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<u32, Output = Self>
+ ops::Shl<u32, Output = Self>
+ ops::BitAnd<Output = Self>
@@ -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<Output = Self>
- + Neg<Output = Self>
- + Mul<Output = Self>
- + Add<Output = Self>
- + LowerExp
+ + ops::Div<Output = Self>
+ + ops::Neg<Output = Self>
+ + ops::Mul<Output = Self>
+ + ops::Add<Output = Self>
+ + fmt::Debug
+ PartialEq
+ PartialOrd
+ Default
+ Clone
+ Copy
- + Debug
{
/// The unsigned integer with the same size as the float
- type Int: Integer + Into<u64>;
+ type Int: Int + Into<u64>;
/* 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 6c6479c..aec5242 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 0cd61b0..e347a20 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 72112f8..b0ba070 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 25e1243..0713c5c 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!(<f16 as RawFloat>::INFINITY, f16::INFINITY);
- assert_eq!(<f16 as RawFloat>::NEG_INFINITY, -f16::INFINITY);
- assert_eq!(<f16 as RawFloat>::NAN.to_bits(), f16::NAN.to_bits());
- assert_eq!(<f16 as RawFloat>::NEG_NAN.to_bits(), (-f16::NAN).to_bits());
- assert_eq!(<f16 as RawFloat>::SIG_BITS, 10);
- assert_eq!(<f16 as RawFloat>::MIN_EXPONENT_ROUND_TO_EVEN, -22);
- assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_ROUND_TO_EVEN, 5);
- assert_eq!(<f16 as RawFloat>::MIN_EXPONENT_FAST_PATH, -4);
- assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_FAST_PATH, 4);
- assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_DISGUISED_FAST_PATH, 7);
- assert_eq!(<f16 as RawFloat>::EXP_MIN, -14);
- assert_eq!(<f16 as RawFloat>::EXP_SAT, 0x1f);
- assert_eq!(<f16 as RawFloat>::SMALLEST_POWER_OF_TEN, -27);
- assert_eq!(<f16 as RawFloat>::LARGEST_POWER_OF_TEN, 4);
- assert_eq!(<f16 as RawFloat>::MAX_MANTISSA_FAST_PATH, 2048);
+ assert_eq!(<f16 as Float>::INFINITY, f16::INFINITY);
+ assert_eq!(<f16 as Float>::NEG_INFINITY, -f16::INFINITY);
+ assert_eq!(<f16 as Float>::NAN.to_bits(), f16::NAN.to_bits());
+ assert_eq!(<f16 as Float>::NEG_NAN.to_bits(), (-f16::NAN).to_bits());
+ assert_eq!(<f16 as Float>::SIG_BITS, 10);
+ assert_eq!(<f16 as Float>::MIN_EXPONENT_ROUND_TO_EVEN, -22);
+ assert_eq!(<f16 as Float>::MAX_EXPONENT_ROUND_TO_EVEN, 5);
+ assert_eq!(<f16 as Float>::EXP_MIN, -14);
+ assert_eq!(<f16 as Float>::EXP_SAT, 0x1f);
+ assert_eq!(<f16 as Float>::SMALLEST_POWER_OF_TEN, -27);
+ assert_eq!(<f16 as Float>::LARGEST_POWER_OF_TEN, 4);
+
+ assert_eq!(<f16 as Lemire>::MIN_EXPONENT_FAST_PATH, -4);
+ assert_eq!(<f16 as Lemire>::MAX_EXPONENT_FAST_PATH, 4);
+ assert_eq!(<f16 as Lemire>::MAX_EXPONENT_DISGUISED_FAST_PATH, 7);
+ assert_eq!(<f16 as Lemire>::MAX_MANTISSA_FAST_PATH, 2048);
}
#[test]
fn test_f32_consts() {
- assert_eq!(<f32 as RawFloat>::INFINITY, f32::INFINITY);
- assert_eq!(<f32 as RawFloat>::NEG_INFINITY, -f32::INFINITY);
- assert_eq!(<f32 as RawFloat>::NAN.to_bits(), f32::NAN.to_bits());
- assert_eq!(<f32 as RawFloat>::NEG_NAN.to_bits(), (-f32::NAN).to_bits());
- assert_eq!(<f32 as RawFloat>::SIG_BITS, 23);
- assert_eq!(<f32 as RawFloat>::MIN_EXPONENT_ROUND_TO_EVEN, -17);
- assert_eq!(<f32 as RawFloat>::MAX_EXPONENT_ROUND_TO_EVEN, 10);
- assert_eq!(<f32 as RawFloat>::MIN_EXPONENT_FAST_PATH, -10);
- assert_eq!(<f32 as RawFloat>::MAX_EXPONENT_FAST_PATH, 10);
- assert_eq!(<f32 as RawFloat>::MAX_EXPONENT_DISGUISED_FAST_PATH, 17);
- assert_eq!(<f32 as RawFloat>::EXP_MIN, -126);
- assert_eq!(<f32 as RawFloat>::EXP_SAT, 0xff);
- assert_eq!(<f32 as RawFloat>::SMALLEST_POWER_OF_TEN, -65);
- assert_eq!(<f32 as RawFloat>::LARGEST_POWER_OF_TEN, 38);
- assert_eq!(<f32 as RawFloat>::MAX_MANTISSA_FAST_PATH, 16777216);
+ assert_eq!(<f32 as Float>::INFINITY, f32::INFINITY);
+ assert_eq!(<f32 as Float>::NEG_INFINITY, -f32::INFINITY);
+ assert_eq!(<f32 as Float>::NAN.to_bits(), f32::NAN.to_bits());
+ assert_eq!(<f32 as Float>::NEG_NAN.to_bits(), (-f32::NAN).to_bits());
+ assert_eq!(<f32 as Float>::SIG_BITS, 23);
+ assert_eq!(<f32 as Float>::MIN_EXPONENT_ROUND_TO_EVEN, -17);
+ assert_eq!(<f32 as Float>::MAX_EXPONENT_ROUND_TO_EVEN, 10);
+ assert_eq!(<f32 as Float>::EXP_MIN, -126);
+ assert_eq!(<f32 as Float>::EXP_SAT, 0xff);
+ assert_eq!(<f32 as Float>::SMALLEST_POWER_OF_TEN, -65);
+ assert_eq!(<f32 as Float>::LARGEST_POWER_OF_TEN, 38);
+
+ assert_eq!(<f32 as Lemire>::MIN_EXPONENT_FAST_PATH, -10);
+ assert_eq!(<f32 as Lemire>::MAX_EXPONENT_FAST_PATH, 10);
+ assert_eq!(<f32 as Lemire>::MAX_EXPONENT_DISGUISED_FAST_PATH, 17);
+ assert_eq!(<f32 as Lemire>::MAX_MANTISSA_FAST_PATH, 16777216);
}
#[test]
fn test_f64_consts() {
- assert_eq!(<f64 as RawFloat>::INFINITY, f64::INFINITY);
- assert_eq!(<f64 as RawFloat>::NEG_INFINITY, -f64::INFINITY);
- assert_eq!(<f64 as RawFloat>::NAN.to_bits(), f64::NAN.to_bits());
- assert_eq!(<f64 as RawFloat>::NEG_NAN.to_bits(), (-f64::NAN).to_bits());
- assert_eq!(<f64 as RawFloat>::SIG_BITS, 52);
- assert_eq!(<f64 as RawFloat>::MIN_EXPONENT_ROUND_TO_EVEN, -4);
- assert_eq!(<f64 as RawFloat>::MAX_EXPONENT_ROUND_TO_EVEN, 23);
- assert_eq!(<f64 as RawFloat>::MIN_EXPONENT_FAST_PATH, -22);
- assert_eq!(<f64 as RawFloat>::MAX_EXPONENT_FAST_PATH, 22);
- assert_eq!(<f64 as RawFloat>::MAX_EXPONENT_DISGUISED_FAST_PATH, 37);
- assert_eq!(<f64 as RawFloat>::EXP_MIN, -1022);
- assert_eq!(<f64 as RawFloat>::EXP_SAT, 0x7ff);
- assert_eq!(<f64 as RawFloat>::SMALLEST_POWER_OF_TEN, -342);
- assert_eq!(<f64 as RawFloat>::LARGEST_POWER_OF_TEN, 308);
- assert_eq!(<f64 as RawFloat>::MAX_MANTISSA_FAST_PATH, 9007199254740992);
+ assert_eq!(<f64 as Float>::INFINITY, f64::INFINITY);
+ assert_eq!(<f64 as Float>::NEG_INFINITY, -f64::INFINITY);
+ assert_eq!(<f64 as Float>::NAN.to_bits(), f64::NAN.to_bits());
+ assert_eq!(<f64 as Float>::NEG_NAN.to_bits(), (-f64::NAN).to_bits());
+ assert_eq!(<f64 as Float>::SIG_BITS, 52);
+ assert_eq!(<f64 as Float>::MIN_EXPONENT_ROUND_TO_EVEN, -4);
+ assert_eq!(<f64 as Float>::MAX_EXPONENT_ROUND_TO_EVEN, 23);
+ assert_eq!(<f64 as Float>::EXP_MIN, -1022);
+ assert_eq!(<f64 as Float>::EXP_SAT, 0x7ff);
+ assert_eq!(<f64 as Float>::SMALLEST_POWER_OF_TEN, -342);
+ assert_eq!(<f64 as Float>::LARGEST_POWER_OF_TEN, 308);
+
+ assert_eq!(<f64 as Lemire>::MIN_EXPONENT_FAST_PATH, -22);
+ assert_eq!(<f64 as Lemire>::MAX_EXPONENT_FAST_PATH, 22);
+ assert_eq!(<f64 as Lemire>::MAX_EXPONENT_DISGUISED_FAST_PATH, 37);
+ assert_eq!(<f64 as Lemire>::MAX_MANTISSA_FAST_PATH, 9007199254740992);
}
diff --git a/library/coretests/tests/num/dec2flt/lemire.rs b/library/coretests/tests/num/dec2flt/lemire.rs
index e4ce533..e5a7ae3 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 0000000..2a82ae3
--- /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<T>(x: T) -> T
+where
+ T: FromStr<Err: fmt::Debug> + fmt::Display,
+{
+ x.to_string().parse::<T>().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::<Float>().unwrap().is_nan());
+ assert!("-nan".parse::<Float>().unwrap().is_nan());
+ assert!("+nan".parse::<Float>().unwrap().is_nan());
+ assert!("+NAN".parse::<Float>().unwrap().is_nan());
+ assert!("-NaN".parse::<Float>().unwrap().is_nan());
+ }
+}
diff --git a/library/coretests/tests/floats/mod.rs b/library/coretests/tests/num/floats.rs
similarity index 98%
rename from library/coretests/tests/floats/mod.rs
rename to library/coretests/tests/num/floats.rs
index c9bbff2..d87f850 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<T: TestableFloat + Copy>(_x: T) -> T {
+pub(crate) const fn lim_for_ty<T: TestableFloat + Copy>(_x: T) -> T {
T::APPROX
}
@@ -232,7 +232,9 @@ const fn lim_for_ty<T: TestableFloat + Copy>(_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: <Float as TestableFloat>::Int = l.to_bits(),
- rb: <Float as TestableFloat>::Int = r.to_bits(),
+ lb: <Float as $crate::num::floats::TestableFloat>::Int = l.to_bits(),
+ rb: <Float as $crate::num::floats::TestableFloat>::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 b0f6a75..0000000
--- 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::<f32>().unwrap().is_nan());
- assert!("-nan".parse::<f32>().unwrap().is_nan());
- assert!("+nan".parse::<f32>().unwrap().is_nan());
- assert!("+NAN".parse::<f32>().unwrap().is_nan());
- assert!("-NaN".parse::<f32>().unwrap().is_nan());
-}
diff --git a/library/coretests/tests/num/mod.rs b/library/coretests/tests/num/mod.rs
index 73b0e23..e0214c6 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 c0a6e30..5f2e441 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 25b1ece..7bdba3a 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 6fcb28e..c8c8a6c 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 b88ed19..a21748c 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 8df81a5..16ae8a8 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<T>) -> 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::<i32>();
+ /// 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<T>) -> 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::<i32>();
+ /// 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 f1ecf80..c743462 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 0ae23f6..a1c49bb 100644
--- a/library/std/src/sync/mpsc.rs
+++ b/library/std/src/sync/mpsc.rs
@@ -607,6 +607,29 @@ impl<T> Sender<T> {
pub fn send(&self, t: T) -> Result<(), SendError<T>> {
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::<i32>();
+ /// 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::<i32>();
+ /// 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 7db4745..2a85718 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<SystemTime> {
}
#[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> {
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> {
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<SystemTime> {
}
#[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> {
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> {
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 3f06c9b..e1d9d80 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 1318d8d..5de0018 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 f4e6a20..85c23ac 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<BTreeMap<EditorKind, Vec<&'static str>>> = 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<Option<EditorKind>> {
/// 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 0000000..067c8ff
--- /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 e8f83ff..57b5492 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 47f6d6f..8457607 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 b264c96..f9b0d2d 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 0000000..a22e8de
--- /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 a70e4ac..7c0f1d3 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -406,6 +406,31 @@
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 e95d777..a51e46f 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 9882846..014d1a0 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 @@
## 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 @@
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 @@
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 091c757..0cf3822 100644
--- a/src/doc/rustc/src/platform-support/vxworks.md
+++ b/src/doc/rustc/src/platform-support/vxworks.md
@@ -28,6 +28,12 @@
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 40dfdb8..a1578ab 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 cbb9bb8..e84cb63 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 7c798a3..45bfb65 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -6,7 +6,103 @@
## 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 @@
[`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 4aa0a42..6f9db8d 100644
--- a/src/tools/clippy/clippy.toml
+++ b/src/tools/clippy/clippy.toml
@@ -5,11 +5,11 @@
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 359bf17..99709ed 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 b98a20a..8b45473 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 75761de..151f9c9 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 a76027c..3850c55 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<Span> {
- 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> {
+ 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 e16b194..441b907 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 6578cf2..f476aba 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 4d67c15..719484b 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 96de118..2c89afc 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 5d09f75..ffc6f71 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 e388651..400cfcd 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 c34e0d3..c771331 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 7c397bd..2c368f1 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 0000000..6662a34
--- /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<i32> = 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<i32> = 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<ManualPopIfPattern<'tcx>> {
+ 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<ManualPopIfPattern<'tcx>> {
+ 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<ManualPopIfPattern<'tcx>> {
+ 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<ManualPopIfPattern<'tcx>> {
+ 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.<pop_method>().unwrap()` or `collection.<pop_method>().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 c95a72d..de5f83b 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 1fc8bb9..abbf600 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 421c606..e3a112d 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 3a16c2e..7b3ad25 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 5b0de80..a8312a0 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<HirId> = 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 dc8c06f..717c47b 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 23a0046..2296704 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 7b10c37..d2e593f 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 aaface3..3aa6661 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<FilterType> {
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 e4ae14b..0973395 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 0ed166f..b647dbd 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 042558d..36dd4e9 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 98b7834..cbb7da3 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 0000000..7c1c31d
--- /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 265619e..0000000
--- 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 1f6bb60..0000000
--- 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 387b6f8..a0ad1fe 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 567443b..b4519d6 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 e79ce91..dfd7834a 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 b200fd1f..ebf649c 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 7c68762..e806123 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 8524497..c99f2e2 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 44e35b8..ac2d374 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 60775d1..2bf1d8b 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 28c339c..7b61222 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 1291f24..fa2ad4b 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 b22cf9d..63e41e8a 100644
--- a/src/tools/clippy/clippy_test_deps/Cargo.lock
+++ b/src/tools/clippy/clippy_test_deps/Cargo.lock
@@ -55,9 +55,9 @@
[[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 d7e4013..b5e2e0f 100644
--- a/src/tools/clippy/clippy_utils/README.md
+++ b/src/tools/clippy/clippy_utils/README.md
@@ -8,7 +8,7 @@
<!-- begin autogenerated nightly -->
```
-nightly-2026-03-05
+nightly-2026-03-21
```
<!-- end autogenerated nightly -->
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index 1a75bdff..51db6b6 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<ConstValue> {
&& 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 88dd3d9..f3c4ae3 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 4e9c64d..a4d8fd2 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<Box<SpanlessEqCallback<'a>>>,
+ expr_fallback: Option<Box<SpanlessEqCallback<'a, 'tcx>>>,
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 1e73e43..b37558c 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<Item = (TokenKind, &str, Inn
/// Checks whether a given span has any comment token
/// This checks for all types of comment: line "//", block "/**", doc "///" "//!"
-pub fn span_contains_comment(sm: &SourceMap, span: Span) -> 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<String> {
- 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::<Vec<_>>()
+pub fn span_extract_comments(cx: &impl source::HasSession, span: Span) -> Vec<String> {
+ span.with_source_text(cx, |snippet| {
+ tokenize_with_text(snippet)
+ .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }))
+ .map(|(_, s, _)| s.to_string())
+ .collect::<Vec<_>>()
+ })
+ .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 5aa457b..ecadc33 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 d6ba678..bb67e43 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 af51477..1ac417a 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 c9bb94c..1f8bcde 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 cd69a12..efb57e6 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<MultiSpan>, msg: impl Into<DiagMessage>) {
- 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<MultiSpan>, msg: impl Into<DiagMessage>) {
- 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 e9d53c6..8631ddb 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 @@
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 0000000..b703924
--- /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 41e5bbe..36300aa 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 de0d3fb..63b4418 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 c19947c..b570389 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 @@
= 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<Vec<u8>> = 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 @@
= 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<char> = 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::<i16>());
| ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `str::parse::<i16>`
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: `<dyn TestTrait>::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 0000000..6e30786
--- /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<T>(PhantomData<T>);
+
+impl<T> FakeVec<T> {
+ fn last(&self) -> Option<&T> {
+ None
+ }
+
+ fn pop(&mut self) -> Option<T> {
+ None
+ }
+}
+
+fn is_some_and_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
+ 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<i32>, mut deque: VecDeque<i32>) {
+ // 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<i32> = 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<i32>, mut deque: VecDeque<i32>) {
+ 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<i32>) {
+ // 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<i32>, mut deque: VecDeque<i32>) {
+ 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<i32>) {
+ // 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<i32>, mut deque: VecDeque<i32>) {
+ 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<i32>) {
+ // 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<i32> = 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<i32>>) {
+ vec.pop_if(|e| *e == vec![1]);
+}
+
+#[clippy::msrv = "1.85.0"]
+fn msrv_too_low_vec(mut vec: Vec<i32>) {
+ 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<i32>) {
+ 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<i32>) {
+ vec.pop_if(|x| *x > 2);
+}
+
+#[clippy::msrv = "1.93.0"]
+fn msrv_high_enough_vecdeque(mut deque: VecDeque<i32>) {
+ 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 0000000..a2f679d
--- /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<T>(PhantomData<T>);
+
+impl<T> FakeVec<T> {
+ fn last(&self) -> Option<&T> {
+ None
+ }
+
+ fn pop(&mut self) -> Option<T> {
+ None
+ }
+}
+
+fn is_some_and_pattern_positive(mut vec: Vec<i32>, mut deque: VecDeque<i32>) {
+ 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<i32>, mut deque: VecDeque<i32>) {
+ // 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<i32> = 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<i32>, mut deque: VecDeque<i32>) {
+ 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<i32>) {
+ // 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<i32>, mut deque: VecDeque<i32>) {
+ 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<i32>) {
+ // 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<i32>, mut deque: VecDeque<i32>) {
+ 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<i32>) {
+ // 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<i32> = 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<i32>>) {
+ 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<i32>) {
+ 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<i32>) {
+ 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<i32>) {
+ 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<i32>) {
+ 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 0000000..d57e20d
--- /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 31684a5..b0ab833 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 @@
_ => 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 39bee01..9c78991 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 102517d..e209da5 100644
--- a/src/tools/clippy/tests/ui/question_mark.fixed
+++ b/src/tools/clippy/tests/ui/question_mark.fixed
@@ -524,3 +524,16 @@
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 cfea127..579b514 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<i32> {
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 c243f12..1d7f665 100644
--- a/src/tools/clippy/tests/ui/question_mark.stderr
+++ b/src/tools/clippy/tests/ui/question_mark.stderr
@@ -362,5 +362,24 @@
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 468f0a5..c717488 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 @@
#[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<i32>` -> `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 101374a..25be9be 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<i32>` -> `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 2046dd1..e165903 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 @@
|
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 @@
|
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 @@
|
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 9974ee2..8d25efb 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<T>(x: T) -> T {
- x
-}
-
const fn double_it(x: i32) -> i32 {
x * 2
}
@@ -18,31 +15,42 @@
// 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 9b53f3fc..42d4ad6 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<T>(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 d90875e..3b579eb 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
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-LL | let _: &str = option.map_or_else(|| &string, |x| x);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)`
+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:34:5
+ --> tests/ui/unnecessary_option_map_or_else.rs:32:5
|
LL | / option.map_or_else(
LL | |
@@ -29,19 +34,122 @@
... |
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 | let _: &str = option.map_or_else(|| &string, identity);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| &string)`
+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(|| ((), ()));
+ |
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 _: String = option.map_or_else(|| string, do_nothing);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `option.unwrap_or_else(|| string)`
+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);
+ |
-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 5d7e3fa..5554251 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 @@
// 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<usize, usize> = Ok(0);
x.map_or_else(|err| err, |n| n + 1);
@@ -61,4 +70,16 @@
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 d2bab0f..21a4826 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<usize, usize> = 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 e6afa50..f769991 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
+ --> 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)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= 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);
+ |
error: unused "map closure" when calling `Result::map_or_else` value
- --> tests/ui/unnecessary_result_map_or_else.rs:11: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`: `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 | let y: &str = x.map_or_else(|err| err, |n| n);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `x.unwrap_or_else(|err| err)`
+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:22:5
+ --> tests/ui/unnecessary_result_map_or_else.rs:27:5
|
LL | / x.map_or_else(
LL | |
@@ -29,7 +34,111 @@
... |
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 d82a7b9..75e8315 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 91a5c12..26ca901 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 @@
<h2 class="lint-title"> {# #}
<div class="panel-title-name" id="lint-{{lint.id}}"> {# #}
{{lint.id ~}}
- <a href="#{{lint.id}}" class="anchor label label-default">¶</a> {#+ #}
- <a href="" class="copy-to-clipboard anchor label label-default"> {# #}
+ <a href="#{{lint.id}}" class="anchor label">¶</a> {#+ #}
+ <a href="" class="copy-to-clipboard anchor label"> {# #}
📋 {# #}
</a> {# #}
</div> {# #}
- <span class="label label-default lint-group group-{{lint.group}}">{{lint.group}}</span> {#+ #}
+ <span class="label lint-group group-{{lint.group}}">{{lint.group}}</span> {#+ #}
<span class="label lint-level level-{{lint.level}}">{{lint.level}}</span> {#+ #}
@@ -194,24 +194,20 @@
{# Applicability #}
<div> {# #}
Applicability: {#+ #}
- <span class="label label-default applicability">{{ lint.applicability_str() }}</span> {# #}
+ <span class="label applicability">{{ lint.applicability_str() }}</span> {# #}
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a> {# #}
</div>
{# Clippy version #}
<div> {# #}
{% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: {#+ #}
- <span class="label label-default label-version">{{lint.version}}</span> {# #}
+ <span class="label label-version">{{lint.version}}</span> {# #}
</div>
{# Open related issues #}
- <div> {# #}
- <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a> {# #}
- </div>
+ <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a>
{# Jump to source #}
{% if let Some(id_location) = lint.id_location %}
- <div> {# #}
- <a href="https://github.com/rust-lang/rust-clippy/blob/master/{{id_location}}">View Source</a> {# #}
- </div>
+ <a href="https://github.com/rust-lang/rust-clippy/blob/master/{{id_location}}">View Source</a>
{% endif %}
</div> {# #}
</div> {# #}
diff --git a/src/tools/clippy/util/gh-pages/style.css b/src/tools/clippy/util/gh-pages/style.css
index ce478a3..4f1935b 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 @@
.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 @@
text-decoration: none;
}
-.label-default {
- background-color: #777;
-}
-
.lint-level {
min-width: 4em;
}
@@ -420,6 +420,7 @@
.lint-group {
min-width: 8em;
+ background-color: var(--label-background);
}
.group-deprecated {
opacity: 0.5;
@@ -459,7 +460,7 @@
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 @@
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 b674c6e..b584d14 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 112a051..f382502 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -1475,7 +1475,6 @@
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 860ecc3..430d4a5 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 7271c54..0000000
--- 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 2525d30..242e716 100644
--- a/tests/pretty/delegation-inherit-attributes.pp
+++ b/tests/pretty/delegation-inherit-attributes.pp
@@ -117,7 +117,7 @@
fn foo<Self>(self: _, arg1: _) -> _ { <X as T>::foo(self + 1, arg1) }
#[attr = MustUse {reason: "some reason"}]
#[attr = Inline(Hint)]
- fn bar(self: _, arg1: _) -> _ { foo(self + 1, arg1) }
+ fn bar<Self>(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 0000000..63934bb
--- /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 0000000..ee5a0f7
--- /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 0000000..54c6deb
--- /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 0000000..8db2568
--- /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 2ac5fde..1d06fe1 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 1ddcb79..1ac68c9 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 8e5c31d..731d19b 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 22533b9..6382c59 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 @@
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 0000000..9b72eff
--- /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() == "<Concrete as Child>")
+ .expect("Could not find <Concrete as Child>");
+
+ 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 <Concrete as Child>. Found: {:#?}",
+ entries
+ ),
+ }
+ let vacant_impl = local_impls
+ .iter()
+ .find(|i| i.trimmed_name() == "<Concrete as WithVacant>")
+ .expect("Could not find <Concrete as WithVacant>");
+ 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 <Concrete as WithVacant>. 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<T>(&self, meow: T) where Self: Sized;
+ }}
+
+ impl WithVacant for Concrete {{
+ fn valid(&self) {{}}
+ fn excluded<T>(&self, meow: T) {{}}
+ }}
+
+ fn main() {{}}
+ "#
+ )?;
+ Ok(())
+}
diff --git a/tests/ui/README.md b/tests/ui/README.md
index 4572882..c14cbd9 100644
--- a/tests/ui/README.md
+++ b/tests/ui/README.md
@@ -133,7 +133,7 @@
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: <https://github.com/rust-lang/rust/pull/32062>
**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 @@
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 @@
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 @@
## `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 @@
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? <https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/lifetime_parameters.html#liveness-and-universal-regions>
## `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 0000000..155b1d7
--- /dev/null
+++ b/tests/ui/async-await/drop-option-future.rs
@@ -0,0 +1,16 @@
+//! Regression test for <https://github.com/rust-lang/rust/issues/98077>.
+
+//@ 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 22ed8bd..0000000
--- 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/borrowck/non-promotable-static-ref.rs b/tests/ui/borrowck/non-promotable-static-ref.rs
new file mode 100644
index 0000000..4473349
--- /dev/null
+++ b/tests/ui/borrowck/non-promotable-static-ref.rs
@@ -0,0 +1,10 @@
+//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 { t }
+
+fn main() {
+ foo(&unpromotable(5u32));
+}
+//~^^ ERROR temporary value dropped while borrowed
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 1d8e136..979b349 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 0000000..91dc8e3
--- /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 <https://github.com/rust-lang/rust/issues/153816>
+
+struct Inv<T, U>(*mut (T, U));
+
+fn pass_through<F>(_: F) -> Inv<F, F> {
+ todo!()
+}
+
+fn map(_: Inv<impl FnOnce(), impl Fn()>) {}
+
+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 0000000..241a177
--- /dev/null
+++ b/tests/ui/coercion/fudge-inference/input-ty-higher-ranked-fn-trait.rs
@@ -0,0 +1,21 @@
+//@ check-pass
+
+// Regression test for <https://github.com/rust-lang/rust/issues/153849>
+
+#[expect(dead_code)]
+// Must be invariant
+pub struct Server<T>(*mut T);
+impl<T> Server<T> {
+ 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 c8d2683..03c94d9 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::<baz()>(); //~ 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::<bar(bar(1, 1), bar(1, 1))>(); //~ ERROR expected type, found `1`
foo::<bar(1, 1)>(); //~ ERROR expected type, found `1`
foo::<bar(FOO, 2)>(); //~ 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 68e6b2a..82cc948 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<const N: u8>;
trait Foo {}
impl Foo for A<N> {}
-//~^ 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<const N: u8>;
impl<N> Foo for B<N> {}
@@ -12,5 +12,16 @@ impl<N> Foo for B<N> {}
struct C<const C: u8, const N: u8>;
impl<const N: u8> Foo for C<N, T> {}
-//~^ 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<const E: u8, const X: u8, const P: u32>;
+impl Foo for D<E, X, P> {}
+//~^ 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<const O: u8, const G: u8, const F: u32>;
+impl<const F: u8, const H: u32> Foo for D<F, Q, 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 a0a6d8c..4d8ab68 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<const N: u8>;
- | ---------------------- similarly named struct `A` defined here
+ | ----------- corresponding const parameter on the type defined here
LL | trait Foo {}
LL | impl Foo for A<N> {}
- | ^
+ | ^ 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<N> {}
-LL + impl Foo for A<A> {}
- |
-help: you might be missing a type parameter
- |
-LL | impl<N> Foo for A<N> {}
- | +++
+LL | impl<const N: u8> Foo for A<N> {}
+ | +++++++++++++
-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<const N: u8>;
- | ---------------------- similarly named struct `A` defined here
-...
+LL | struct C<const C: u8, const N: u8>;
+ | ----------- corresponding const parameter on the type defined here
LL | impl<const N: u8> Foo for C<N, T> {}
- | ^
+ | ^ 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<const N: u8> Foo for C<N, T> {}
-LL + impl<const N: u8> Foo for C<N, A> {}
+LL | impl<const N: u8, const T: u8> Foo for C<N, T> {}
+ | +++++++++++++
+
+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<const E: u8, const X: u8, const P: u32>;
+ | ----------- corresponding const parameter on the type defined here
+LL | impl Foo for D<E, X, P> {}
+ | ^ not found in this scope
|
-LL | impl<const N: u8, T> Foo for C<N, T> {}
- | +++
+help: you might have meant to introduce a const parameter `E` on the impl
+ |
+LL | impl<const E: u8> Foo for D<E, X, P> {}
+ | +++++++++++++
+
+error[E0425]: cannot find const `X` in this scope
+ --> $DIR/invalid-const-arguments.rs:19:19
+ |
+LL | struct D<const E: u8, const X: u8, const P: u32>;
+ | ----------- corresponding const parameter on the type defined here
+LL | impl Foo for D<E, X, P> {}
+ | ^ not found in this scope
+ |
+help: you might have meant to introduce a const parameter `X` on the impl
+ |
+LL | impl<const X: u8> Foo for D<E, X, P> {}
+ | +++++++++++++
+
+error[E0425]: cannot find const `P` in this scope
+ --> $DIR/invalid-const-arguments.rs:19:22
+ |
+LL | struct D<const E: u8, const X: u8, const P: u32>;
+ | ------------ corresponding const parameter on the type defined here
+LL | impl Foo for D<E, X, P> {}
+ | ^ not found in this scope
+ |
+help: you might have meant to introduce a const parameter `P` on the impl
+ |
+LL | impl<const P: u32> Foo for D<E, X, P> {}
+ | ++++++++++++++
+
+error[E0425]: cannot find const `Q` in this scope
+ --> $DIR/invalid-const-arguments.rs:25:46
+ |
+LL | struct D<const E: u8, const X: u8, const P: u32>;
+ | ----------- corresponding const parameter on the type defined here
+...
+LL | impl<const F: u8, const H: u32> Foo for D<F, Q, D> {}
+ | ^ not found in this scope
+ |
+help: you might have meant to introduce a const parameter `Q` on the impl
+ |
+LL | impl<const F: u8, const H: u32, const Q: u8> Foo for D<F, Q, D> {}
+ | +++++++++++++
error[E0747]: unresolved item provided when a constant was expected
--> $DIR/invalid-const-arguments.rs:5:16
@@ -70,7 +112,29 @@
LL | impl<const N: u8> Foo for C<N, { T }> {}
| + +
-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<E, X, P> {}
+ | ^
+ |
+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<const F: u8, const H: u32> Foo for D<F, Q, D> {}
+ | ^
+ |
+help: if this generic argument was intended as a const parameter, surround it with braces
+ |
+LL | impl<const F: u8, const H: u32> Foo for D<F, { Q }, 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 56fac3e..09d06e0 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<const CF: CompileFlag>() {}
pub fn test_2<T, const CF: CompileFlag>(x: T) {}
-pub struct Example<const CF: CompileFlag, T=u32>{
+pub struct Example<const CF: CompileFlag, T = u32> {
x: T,
}
impl<const CF: CompileFlag, T> Example<CF, T> {
- const ASSOC_FLAG: CompileFlag = CompileFlag::A;
+ const ASSOC_FLAG: CompileFlag = CompileFlag::A;
}
pub fn main() {
- test_1::<CompileFlag::A>();
- //~^ ERROR: expected type, found variant
- //~| ERROR: unresolved item provided when a constant was expected
+ test_1::<CompileFlag::A>();
+ //~^ 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<CompileFlag::A, _> = Example { x: 0 };
- //~^ ERROR: expected type, found variant
- //~| ERROR: unresolved item provided when a constant was expected
+ let _: Example<CompileFlag::A, _> = Example { x: 0 };
+ //~^ ERROR: expected type, found variant
+ //~| ERROR: unresolved item provided when a constant was expected
- let _: Example<Example::ASSOC_FLAG, _> = Example { x: 0 };
- //~^ ERROR: type provided when a constant was expected
+ let _: Example<Example::ASSOC_FLAG, _> = 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 9110523..a557927 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::<CompileFlag::A>();
- | ^^^^^^^^^^^^^^
- | |
- | not a type
- | help: try using the variant's enum: `CompileFlag`
+LL | test_1::<CompileFlag::A>();
+ | ^^^^^^^^^^^^^^
+ | |
+ | 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<CompileFlag::A, _> = Example { x: 0 };
- | ^^^^^^^^^^^^^^
- | |
- | not a type
- | help: try using the variant's enum: `CompileFlag`
+LL | let _: Example<CompileFlag::A, _> = 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::<CompileFlag::A>();
- | ^^^^^^^^^^^^^^
+LL | test_1::<CompileFlag::A>();
+ | ^^^^^^^^^^^^^^
|
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<CompileFlag::A, _> = Example { x: 0 };
- | ^^^^^^^^^^^^^^
+LL | let _: Example<CompileFlag::A, _> = 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::ASSOC_FLAG, _> = Example { x: 0 };
- | ^^^^^^^^^^^^^^^^^^^
+LL | let _: Example<Example::ASSOC_FLAG, _> = 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 2668348..0f00f70 100644
--- a/tests/ui/const-generics/issues/issue-83249.stderr
+++ b/tests/ui/const-generics/issues/issue-83249.stderr
@@ -17,10 +17,10 @@
|
LL | fn foo<T: 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 0000000..9b007d3
--- /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<T> {}
+
+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 0000000..d8ffee4
--- /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 0000000..eeb7668
--- /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<T, const N: T> {
+//~^ ERROR: `T` can't be used as a const parameter type [E0741]
+ a: T,
+}
+
+fn foo<const N: usize, const A: [u8; N]>() {}
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 0000000..a4b13f4
--- /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<T, const N: T> {
+ | ^
+
+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 0000000..f1c0e0b
--- /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<T: ConstParamTy_, const N: T>() {}
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 0000000..017c63d
--- /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<T>; //~ 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 0000000..406206c
--- /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<T>;
+ | ^ 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 285059e..67f7a12 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: Clone>(_: 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 6b6acfc..db88b8d 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 | 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 ead3c30..a20b28c 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 @@
| ^^^
| |
| 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 78aefbc..6c57c3b 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 @@
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 @@
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 @@
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 00dfede..9234771 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 @@
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<X, Y, Z>() {}
- | --- help: remove the unnecessary generic argument
-...
LL | reuse <X as Trait1::<(), ()>>::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 0000000..6cff8f4
--- /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 0000000..69c61e6
--- /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 0000000..8cfe8d6
--- /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 0000000..e6112e7
--- /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 0000000..483ff5a
--- /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 0000000..f2025c2
--- /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: X,
+}
+
+fn takes_foo(_: Foo) {}
+
+fn takes_mytype<X>(_: MyType<X>) {}
+
+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 0000000..6868f15
--- /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<X>(_: MyType<X>) {}
+ | ------------ ^^^^^^^^^ this parameter takes ownership of the value
+ | |
+ | in this function
+note: if `MyType<i32>` implemented `Clone`, you could clone the value
+ --> $DIR/on_move_with_format.rs:14:1
+ |
+LL | struct MyType<X> {
+ | ^^^^^^^^^^^^^^^^ 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 0000000..1af3daf
--- /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 0000000..f02f970
--- /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 0000000..f403ad1
--- /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 0000000..efb43c3
--- /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 0000000..2050403
--- /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 0000000..39992b0
--- /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 0000000..b5bb103d
--- /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 0000000..00d0657
--- /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 0000000..c676b87
--- /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 0000000..6dc230b
--- /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 0000000..e5603fd
--- /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 0000000..f4e6d69
--- /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 0000000..33e78ea
--- /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 0000000..29c987f
--- /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 0000000..651f618
--- /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 0000000..a09b8a9
--- /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 e64e60f..09c2a05 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 862f805..58d2bdb 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
= 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 @@
= 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 @@
= 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 @@
= 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 @@
= 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 @@
= 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 @@
LL | trait Foo<T> {}
| ^^^^^^^^^^^^
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<i32>) {}
| ^^^^^^^^ 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 @@
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 @@
|
= 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 0000000..a1f3b1f
--- /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 0000000..9ba6f27
--- /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 28a5c70..6894533 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 0000000..41bf75c
--- /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 0000000..89658bc
--- /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 0000000..2aa68c8
--- /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 0000000..36076b6
--- /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 f35bd7a..ac6051e 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 0000000..b36f2cf
--- /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 0000000..5408032
--- /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 bea5ddb..0000000
--- 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 2cbf0d2..f77f219 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<impl Trait> {
if true {
let x = hello();
- //[next]~^ ERROR: the size for values of type `dyn Trait` cannot be known at compilation time
let y: Box<dyn Trait> = 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 a480a69..db75876 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 @@
|
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<impl Trait + ?Sized> {
+ | ------------------------
+ | | |
+ | | the expected opaque type
+ | expected `Box<impl Trait + ?Sized>` because of return type
+...
+LL | Box::new(1u32)
+ | ^^^^^^^^^^^^^^ types differ
+ |
+ = note: expected struct `Box<impl Trait + ?Sized>`
+ found struct `Box<u32>`
-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<impl Trait + ?Sized> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 52a72b8..3bb9f9c 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<dyn Send> = 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 ebfbb29..c1dd535 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<impl Trait + ?Sized> {
+ //[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<impl Trait + ?Sized> {
//[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 0000000..9a49439
--- /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::<T>` 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 0000000..013a2f9
--- /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::<T>` 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 0000000..594b6f0
--- /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::<T>` needs `T` to be specified
+ | --- type must be known at this point
+ |
+ = note: the type must implement `Sum<i32>`
+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/issues/issue-52049.rs b/tests/ui/issues/issue-52049.rs
deleted file mode 100644
index efdcc44..0000000
--- a/tests/ui/issues/issue-52049.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-fn foo(_: &'static u32) {}
-
-fn unpromotable<T>(t: T) -> T { t }
-
-fn main() {
- foo(&unpromotable(5u32));
-}
-//~^^ ERROR temporary value dropped while borrowed
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 8b07719..0000000
--- a/tests/ui/kindck/kindck-impl-type-params-2.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-trait Foo {
-}
-
-
-
-impl<T:Copy> Foo for T {
-}
-
-fn take_param<T:Foo>(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 38dc94f..0000000
--- 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<T:Copy> 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<T:Foo>(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 95048c4..0000000
--- 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<T:Copy> 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<T:Foo>(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 <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
- --> $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 <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
- --> $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 8bb784d..0000000
--- a/tests/ui/kindck/kindck-nonsendable-1.stderr
+++ /dev/null
@@ -1,25 +0,0 @@
-error[E0277]: `Rc<usize>` cannot be sent between threads safely
- --> $DIR/kindck-nonsendable-1.rs:9:9
- |
-LL | bar(move|| foo(x));
- | --- ------^^^^^^^
- | | |
- | | `Rc<usize>` 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<usize>`
-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:FnOnce() + Send>(_: 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 f5d4424..0000000
--- 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<T: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::<Box<dyn Dummy>>();
- //~^ ERROR `dyn Dummy` cannot be sent between threads safely
-}
-
-fn object_with_send_bound_ok() {
- assert_send::<&'static (dyn Dummy + Sync)>();
- assert_send::<Box<dyn Dummy + 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 b71d402..0000000
--- 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<T: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::<Box<dyn Dummy>>();
- | ^^^^^^^^^^^^^^ `dyn Dummy` cannot be sent between threads safely
- |
- = help: the trait `Send` is not implemented for `dyn Dummy`
- = note: required for `std::ptr::Unique<dyn Dummy>` to implement `Send`
-note: required because it appears within the type `Box<dyn Dummy>`
- --> $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<T: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 76a9fc6..0000000
--- 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<T:Send+'static>() { }
-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::<Box<dyn Dummy + Send>>();
-}
-
-// closure and object types can have lifetime bounds which make
-// them not ok
-fn test_71<'a>() {
- assert_send::<Box<dyn Dummy + 'a>>();
- //~^ 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 2184ae7..0000000
--- 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<T:Send+'static>() { }
- | ^^^^ 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::<Box<dyn Dummy + 'a>>();
- | ^^^^^^^^^^^^^^^^^^^ `(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<T:Send+'static>() { }
- | ^^^^ 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 d37074e..0000000
--- a/tests/ui/kindck/kindck-send-object2.rs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Continue kindck-send-object1.rs.
-
-fn assert_send<T: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::<Box<dyn Dummy>>();
- //~^ 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::<Box<dyn Dummy + 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 52a7055..0000000
--- 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<T: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::<Box<dyn Dummy>>();
- | ^^^^^^^^^^^^^^ `dyn Dummy` cannot be sent between threads safely
- |
- = help: the trait `Send` is not implemented for `dyn Dummy`
- = note: required for `std::ptr::Unique<dyn Dummy>` to implement `Send`
-note: required because it appears within the type `Box<dyn Dummy>`
- --> $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<T: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 65efb69..0000000
--- 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<T:Send>() { }
-
-// owned content are ok
-fn test30() { assert_send::<Box<isize>>(); }
-fn test31() { assert_send::<String>(); }
-fn test32() { assert_send::<Vec<isize> >(); }
-
-// but not if they own a bad thing
-fn test40() {
- assert_send::<Box<*mut u8>>();
- //~^ 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 c433d80..0000000
--- 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::<Box<*mut u8>>();
- | ^^^^^^^^^^^^ `*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<T: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 eb1f2a5..0000000
--- a/tests/ui/kindck/kindck-send-unsafe.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-extern crate core;
-
-fn assert_send<T: 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 f1a5054..0000000
--- 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<T: 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<T: 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 0000000..68eff33
--- /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 0000000..ca3ac3b
--- /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. <https://github.com/rust-lang/rust/issues/148114>
+
+#![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 0000000..0fe8873
--- /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. <https://github.com/rust-lang/rust-clippy/issues/16133>
+
+#![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 0000000..36848ff
--- /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 99a5481..d1c48c3 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 <https://github.com/rust-lang/rust/issues/89562>
+
//@ 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 0000000..a2bee87
--- /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 0000000..eaad41c
--- /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 0000000..e9ca6e3
--- /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 0000000..1dab2ca
--- /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 0000000..a46ea10
--- /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 <https://doc.rust-lang.org/reference/conditional-compilation.html#the-cfg_attr-attribute>
+ = 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 0000000..90e8945
--- /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 afacb3a..4652bf5 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 @@
|
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 ba46a44..1c45a2c 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 @@
//[next]~^^ ERROR expected a `FnMut(&<std::ops::RangeInclusive<{integer}> 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(&<std::ops::RangeInclusive<{integer}> as Iterator>::Item)` closure, found
+ //[next]~^^ ERROR expected a `FnMut(&<std::ops::RangeInclusive<{integer}> 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 7912ed4..36e49c2 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 @@
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(&<std::ops::RangeInclusive<{integer}> 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 @@
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 0fd5670..20d6fed 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(&<std::ops::RangeInclusive<{integer}> 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(&<std::ops::RangeInclusive<{integer}> as Iterator>::Item)` closure, found
+ //[next]~^^ ERROR expected a `FnMut(&<std::ops::RangeInclusive<{integer}> 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 0000000..ffe3d92
--- /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 0000000..3bd7a21
--- /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 9a18798..6243a9a 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 @@
error[E0308]: mismatched types
--> $DIR/transforming-option-ref-issue-127545.rs:9:19
|
+LL | pub fn bar(arg: Option<&Vec<i32>>) -> &[i32] {
+ | ------ this return type influences the call expression's return type
LL | arg.unwrap_or(&[])
| --------- ^^^ expected `&Vec<i32>`, found `&[_; 0]`
| |
@@ -41,6 +43,8 @@
error[E0308]: mismatched types
--> $DIR/transforming-option-ref-issue-127545.rs:13:19
|
+LL | pub fn barzz<'a>(arg: Option<&'a Vec<i32>>, v: &'a [i32]) -> &'a [i32] {
+ | --------- this return type influences the call expression's return type
LL | arg.unwrap_or(v)
| --------- ^ expected `&Vec<i32>`, found `&[i32]`
| |
@@ -66,6 +70,8 @@
error[E0308]: mismatched types
--> $DIR/transforming-option-ref-issue-127545.rs:17:19
|
+LL | pub fn convert_result(arg: Result<&Vec<i32>, ()>) -> &[i32] {
+ | ------ this return type influences the call expression's return type
LL | arg.unwrap_or(&[])
| --------- ^^^ expected `&Vec<i32>`, 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 772e60b..c52f3157 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<const N: u8>();
impl X<N> {}
-//~^ 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<T, const A: u8 = 2> X<N> {}
-//~^ 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<const N: u8>(_: 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 c361ed7..7c85d39 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<const N: u8>();
- | ------------------------ similarly named struct `X` defined here
+ | ----------- corresponding const parameter on the type defined here
LL |
LL | impl X<N> {}
- | ^
+ | ^ 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<N> {}
-LL + impl X<X> {}
- |
-help: you might be missing a type parameter
- |
-LL | impl<N> X<N> {}
- | +++
+LL | impl<const N: u8> X<N> {}
+ | +++++++++++++
-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<const N: u8>();
+ | ----------- corresponding const parameter on the type defined here
+...
LL | impl<T, const A: u8 = 2> X<N> {}
- | - ^
- | |
- | 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<T, const A: u8 = 2> X<N> {}
-LL + impl<T, const A: u8 = 2> X<T> {}
- |
-help: you might be missing a type parameter
- |
-LL | impl<T, const A: u8 = 2, N> X<N> {}
- | +++
+LL | impl<T, const A: u8 = 2, const N: u8> X<N> {}
+ | +++++++++++++
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 0000000..736d3ba
--- /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<T: Trait>() {}
+
+fn main() {
+ needs_trait::<Union>();
+ //~^ ERROR union self type
+ needs_trait::<Enum>();
+ //~^ ERROR enum self type
+ needs_trait::<Struct>();
+ //~^ 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 0000000..ea40ac5
--- /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::<Union>();
+ | ^^^^^ 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<T: Trait>() {}
+ | ^^^^^ required by this bound in `needs_trait`
+
+error[E0277]: enum self type
+ --> $DIR/self-types.rs:26:19
+ |
+LL | needs_trait::<Enum>();
+ | ^^^^ 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<T: Trait>() {}
+ | ^^^^^ required by this bound in `needs_trait`
+
+error[E0277]: struct self type
+ --> $DIR/self-types.rs:28:19
+ |
+LL | needs_trait::<Struct>();
+ | ^^^^^^ 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<T: 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 83ad8c7..21edb9c 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 44e42f1..a95ffdd 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 @@
|
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 @@
|
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 @@
| 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 @@
|
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 @@
|
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 @@
|
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 @@
|
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 @@
|
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 @@
|
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 @@
|
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 @@
|
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 @@
|
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 @@
|
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 @@
|
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 @@
|
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 @@
|
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 @@
|
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 @@
|
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 @@
= 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 8f8a9f3..5c8a455 100644
--- a/tests/ui/pin-ergonomics/pin_v2-attr.stderr
+++ b/tests/ui/pin-ergonomics/pin_v2-attr.stderr
@@ -116,14 +116,6 @@
|
= 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 @@
|
= 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 b5db992..a03e567 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 @@
*/ {
() =>
{
- 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 e45abab..61b5578 100644
--- a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout
+++ b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout
@@ -20,7 +20,7 @@
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 @@
//@ 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 @@
/*
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 3d4a305..0000000
--- 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 2e0d1b3..0000000
--- 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 baf7874..0000000
--- 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<T> {
- 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 b8392c9..0000000
--- 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<T> {
- 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 6a50248..0000000
--- 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 17564bc..0000000
--- 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 33961de..0000000
--- 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<T> {
- 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 c523808..0000000
--- 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<T> {
- 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 0000000..177d064
--- /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<T> {
+ x: T,
+ }
+ enum Bar<'a, 'b> {
+ V(&'a Foo<&'b i32>),
+ }
+}
+
+mod rev_variant_enum_type {
+ struct Foo<T> {
+ 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<T> {
+ x: T,
+ }
+ struct Bar<'a, 'b> {
+ f: &'a Foo<&'b i32>,
+ }
+}
+
+mod rev_variant_struct_type {
+ struct Foo<T> {
+ 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 36bf0d2..c953b60 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<T:Copy>() { }
+fn assert_copy<T: Copy>() {}
-trait Dummy { }
+trait Dummy {}
#[derive(Copy, Clone)]
struct MyStruct {
@@ -16,8 +16,8 @@ struct MyNoncopyStruct {
x: Box<char>,
}
-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::<Box<isize>>(); //~ ERROR : Copy` is not satisfied
- assert_copy::<String>(); //~ ERROR : Copy` is not satisfied
- assert_copy::<Vec<isize> >(); //~ ERROR : Copy` is not satisfied
+ assert_copy::<Box<isize>>(); //~ ERROR : Copy` is not satisfied
+ assert_copy::<String>(); //~ ERROR : Copy` is not satisfied
+ assert_copy::<Vec<isize>>(); //~ ERROR : Copy` is not satisfied
assert_copy::<Box<&'a mut isize>>(); //~ 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::<Box<dyn Dummy>>(); //~ ERROR : Copy` is not satisfied
assert_copy::<Box<dyn Dummy + Send>>(); //~ 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::<MyStruct>();
@@ -64,8 +64,7 @@ fn test<'a,T,U:Copy>(_: &'a isize) {
assert_copy::<MyNoncopyStruct>(); //~ ERROR : Copy` is not satisfied
// ref counted types are not ok
- assert_copy::<Rc<isize>>(); //~ ERROR : Copy` is not satisfied
+ assert_copy::<Rc<isize>>(); //~ 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 f5623dd..b4fa957 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<T:Copy>() { }
- | ^^^^ required by this bound in `assert_copy`
+LL | fn assert_copy<T: Copy>() {}
+ | ^^^^ required by this bound in `assert_copy`
help: consider removing the leading `&`-reference
|
LL - assert_copy::<&'static mut isize>();
@@ -16,16 +16,16 @@
|
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<T:Copy>() { }
- | ^^^^ required by this bound in `assert_copy`
+LL | fn assert_copy<T: Copy>() {}
+ | ^^^^ required by this bound in `assert_copy`
help: consider removing the leading `&`-reference
|
LL - assert_copy::<&'a mut isize>();
@@ -33,117 +33,117 @@
|
error[E0277]: the trait bound `Box<isize>: Copy` is not satisfied
- --> $DIR/kindck-copy.rs:31:19
+ --> $DIR/basic-copyable-types.rs:31:19
|
LL | assert_copy::<Box<isize>>();
| ^^^^^^^^^^ the trait `Copy` is not implemented for `Box<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<T:Copy>() { }
- | ^^^^ required by this bound in `assert_copy`
+LL | fn assert_copy<T: 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::<String>();
| ^^^^^^ 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<T:Copy>() { }
- | ^^^^ required by this bound in `assert_copy`
+LL | fn assert_copy<T: Copy>() {}
+ | ^^^^ required by this bound in `assert_copy`
error[E0277]: the trait bound `Vec<isize>: Copy` is not satisfied
- --> $DIR/kindck-copy.rs:33:19
+ --> $DIR/basic-copyable-types.rs:33:19
|
-LL | assert_copy::<Vec<isize> >();
+LL | assert_copy::<Vec<isize>>();
| ^^^^^^^^^^ the trait `Copy` is not implemented for `Vec<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<T:Copy>() { }
- | ^^^^ required by this bound in `assert_copy`
+LL | fn assert_copy<T: 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::<Box<&'a mut isize>>();
| ^^^^^^^^^^^^^^^^^^ 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<T:Copy>() { }
- | ^^^^ required by this bound in `assert_copy`
+LL | fn assert_copy<T: Copy>() {}
+ | ^^^^ required by this bound in `assert_copy`
error[E0277]: the trait bound `Box<dyn Dummy>: Copy` is not satisfied
- --> $DIR/kindck-copy.rs:42:19
+ --> $DIR/basic-copyable-types.rs:42:19
|
LL | assert_copy::<Box<dyn Dummy>>();
| ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Box<dyn Dummy>`
|
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<T:Copy>() { }
- | ^^^^ required by this bound in `assert_copy`
+LL | fn assert_copy<T: Copy>() {}
+ | ^^^^ required by this bound in `assert_copy`
error[E0277]: the trait bound `Box<dyn Dummy + Send>: Copy` is not satisfied
- --> $DIR/kindck-copy.rs:43:19
+ --> $DIR/basic-copyable-types.rs:43:19
|
LL | assert_copy::<Box<dyn Dummy + Send>>();
| ^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Box<dyn Dummy + Send>`
|
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<T:Copy>() { }
- | ^^^^ required by this bound in `assert_copy`
+LL | fn assert_copy<T: 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<T:Copy>() { }
- | ^^^^ required by this bound in `assert_copy`
+LL | fn assert_copy<T: 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::<MyNoncopyStruct>();
| ^^^^^^^^^^^^^^^ 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<T:Copy>() { }
- | ^^^^ required by this bound in `assert_copy`
+LL | fn assert_copy<T: Copy>() {}
+ | ^^^^ required by this bound in `assert_copy`
error[E0277]: the trait bound `Rc<isize>: Copy` is not satisfied
- --> $DIR/kindck-copy.rs:67:19
+ --> $DIR/basic-copyable-types.rs:67:19
|
LL | assert_copy::<Rc<isize>>();
| ^^^^^^^^^ the trait `Copy` is not implemented for `Rc<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<T:Copy>() { }
- | ^^^^ required by this bound in `assert_copy`
+LL | fn assert_copy<T: 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 b32fd78..78ef834 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<usize>) {}
-fn bar<F:FnOnce() + Send>(_: F) { }
+fn bar<F: FnOnce() + Send>(_: F) {}
fn main() {
let x = Rc::new(3);
- bar(move|| foo(x));
+ bar(move || foo(x));
//~^ ERROR `Rc<usize>` 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 0000000..5e73f13
--- /dev/null
+++ b/tests/ui/traits/closure-rc-not-send.stderr
@@ -0,0 +1,25 @@
+error[E0277]: `Rc<usize>` cannot be sent between threads safely
+ --> $DIR/closure-rc-not-send.rs:9:9
+ |
+LL | bar(move || foo(x));
+ | --- -------^^^^^^^
+ | | |
+ | | `Rc<usize>` 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<usize>`
+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: FnOnce() + Send>(_: 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 707c5db..ba54bc3 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<T>(marker::PhantomData<T>);
trait Gettable<T> {
- fn get(&self) -> T { panic!() }
+ fn get(&self) -> T {
+ panic!()
+ }
}
impl<T: Send + Copy + 'static> Gettable<T> for S<T> {}
@@ -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 85%
rename from tests/ui/kindck/kindck-impl-type-params.stderr
rename to tests/ui/traits/copy-bounds-impl-type-params.stderr
index 0c9ab13..08fde8f 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>;
| ^^ `T` cannot be sent between threads safely
|
note: required for `S<T>` to implement `Gettable<T>`
- --> $DIR/kindck-impl-type-params.rs:12:32
+ --> $DIR/copy-bounds-impl-type-params.rs:14:32
|
LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {}
| ---- ^^^^^^^^^^^ ^^^^
@@ -18,13 +18,13 @@
| +++++++++++++++++++
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<T>;
| ^^ the trait `Copy` is not implemented for `T`
|
note: required for `S<T>` to implement `Gettable<T>`
- --> $DIR/kindck-impl-type-params.rs:12:32
+ --> $DIR/copy-bounds-impl-type-params.rs:14:32
|
LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {}
| ---- ^^^^^^^^^^^ ^^^^
@@ -37,13 +37,13 @@
| +++++++++++++++++++
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;
| ^^ `T` cannot be sent between threads safely
|
note: required for `S<T>` to implement `Gettable<T>`
- --> $DIR/kindck-impl-type-params.rs:12:32
+ --> $DIR/copy-bounds-impl-type-params.rs:14:32
|
LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {}
| ---- ^^^^^^^^^^^ ^^^^
@@ -56,13 +56,13 @@
| +++++++++++++++++++
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> = &t;
| ^^ the trait `Copy` is not implemented for `T`
|
note: required for `S<T>` to implement `Gettable<T>`
- --> $DIR/kindck-impl-type-params.rs:12:32
+ --> $DIR/copy-bounds-impl-type-params.rs:14:32
|
LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {}
| ---- ^^^^^^^^^^^ ^^^^
@@ -75,18 +75,18 @@
| +++++++++++++++++++
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<dyn Gettable<String>>;
| ^ the trait `Copy` is not implemented for `String`
|
help: the trait `Gettable<T>` is implemented for `S<T>`
- --> $DIR/kindck-impl-type-params.rs:12:1
+ --> $DIR/copy-bounds-impl-type-params.rs:14:1
|
LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: required for `S<String>` to implement `Gettable<String>`
- --> $DIR/kindck-impl-type-params.rs:12:32
+ --> $DIR/copy-bounds-impl-type-params.rs:14:32
|
LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {}
| ---- ^^^^^^^^^^^ ^^^^
@@ -95,18 +95,18 @@
= note: required for the cast from `Box<S<String>>` to `Box<dyn Gettable<String>>`
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<dyn Gettable<Foo>> = t;
| ^ the trait `Copy` is not implemented for `Foo`
|
help: the trait `Gettable<T>` is implemented for `S<T>`
- --> $DIR/kindck-impl-type-params.rs:12:1
+ --> $DIR/copy-bounds-impl-type-params.rs:14:1
|
LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: required for `S<Foo>` to implement `Gettable<Foo>`
- --> $DIR/kindck-impl-type-params.rs:12:32
+ --> $DIR/copy-bounds-impl-type-params.rs:14:32
|
LL | impl<T: Send + Copy + 'static> Gettable<T> for S<T> {}
| ---- ^^^^^^^^^^^ ^^^^
@@ -120,7 +120,7 @@
|
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 92c2b27..dd9fea3 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<T:Copy> Foo for T {
-}
+impl<T: Copy> Foo for T {}
-fn take_param<T:Foo>(foo: &T) { }
+fn take_param<T: Foo>(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 c15aaba..c251d59 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 @@
| 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<T:Copy> Foo for T {
- | ---- ^^^ ^
- | |
- | unsatisfied trait bound introduced here
+LL | impl<T: Copy> 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<T:Foo>(foo: &T) { }
- | ^^^ required by this bound in `take_param`
+LL | fn take_param<T: Foo>(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 <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
- --> $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 0000000..463fe49
--- /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>(T);
+impl<T> View for StableVec<T> {}
+
+fn assert_view<F: View>(f: F) -> F { f }
+
+
+fn store<T>(x: StableVec<T::Assoc>)
+where
+ T: HasAssoc,
+ StableVec<T>: View,
+{
+ let _: StableVec<T::Assoc> = 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 0000000..c999d4d
--- /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<T: Send + 'static>() {}
+fn assert_send<T: 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::<Box<dyn Dummy + 'a>>();
+ //~^ 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::<Box<dyn Dummy + Send>>();
+
+ assert_send::<&'static dyn Dummy>();
+ //~^ ERROR `&'static (dyn Dummy + 'static)` cannot be sent between threads safely [E0277]
+ assert_send::<Box<dyn Dummy>>();
+ //~^ ERROR `dyn Dummy` cannot be sent between threads safely
+ assert_send::<&'static (dyn Dummy + Sync)>();
+ assert_send::<Box<dyn Dummy + Send>>();
+
+ // owned content is ok
+ assert_send::<Box<isize>>();
+ assert_send::<String>();
+ assert_send::<Vec<isize>>();
+
+ // but not if it owns a bad thing
+ assert_send::<Box<*mut u8>>();
+ //~^ 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 0000000..0393f7b
--- /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<T: 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::<Box<dyn Dummy + 'a>>();
+ | ^^^^^^^^^^^^^^^^^^^ `(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<T: 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<T: 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<T: 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::<Box<dyn Dummy>>();
+ | ^^^^^^^^^^^^^^ `dyn Dummy` cannot be sent between threads safely
+ |
+ = help: the trait `Send` is not implemented for `dyn Dummy`
+ = note: required for `std::ptr::Unique<dyn Dummy>` to implement `Send`
+note: required because it appears within the type `Box<dyn Dummy>`
+ --> $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<T: 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::<Box<*mut u8>>();
+ | ^^^^^^^^^^^^ `*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<T: 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<T: 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<T: 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 53920bc..fd07d5b 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 @@
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 0000000..0ccbac3
--- /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<T: Clone>() {}
+fn y<T: Copy>() {}
+
+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::<U>(); //~ ERROR the trait bound `U: Clone` is not satisfied
+ // Copy
+ y::<U>(); //~ 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 0000000..7a42b87
--- /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::<U>();
+ | ^ 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<T: Clone>() {}
+ | ^^^^^ 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::<U>();
+ | ^ 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<T: Copy>() {}
+ | ^^^^ 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 3c95a45..82e2b98 100644
--- a/typos.toml
+++ b/typos.toml
@@ -20,6 +20,7 @@
# 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 @@
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"