Merge pull request #2802 from rust-lang/rustc-pull

Rustc pull update
This commit is contained in:
Redddy
2026-03-23 14:06:29 +09:00
committed by GitHub
409 changed files with 8669 additions and 3422 deletions
+2 -2
View File
@@ -1140,9 +1140,9 @@ version = "0.1.96"
[[package]]
name = "derive-where"
version = "1.6.0"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f"
checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534"
dependencies = [
"proc-macro2",
"quote",
+4 -3
View File
@@ -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
+1 -1
View File
@@ -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,
+2 -2
View File
@@ -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,
+2 -2
View File
@@ -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`
+59 -208
View File
@@ -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);
}
@@ -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,
+5 -2
View File
@@ -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(..)
+4 -3
View File
@@ -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,
+34 -13
View File
@@ -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);
+198 -63
View File
@@ -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,
+1 -1
View File
@@ -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))
}
+1 -1
View File
@@ -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,
@@ -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();
}
}
}
@@ -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,
}
}
@@ -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);
@@ -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());
@@ -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();
@@ -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 });
@@ -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),
@@ -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
}
}
}
@@ -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 => {}
}
}
@@ -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,
@@ -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,
+4 -1
View File
@@ -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;
};
+79 -23
View File
@@ -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
@@ -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,
}
+15 -10
View File
@@ -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(
@@ -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: &note_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: &note_msg,
span,
});
}
} else {
err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note {
is_partial_move,
+7 -10
View File
@@ -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);
}
@@ -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())
}
}
+3 -3
View File
@@ -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(
@@ -4,13 +4,13 @@ Date: Sun, 15 Feb 2026 14:06:49 +0000
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 @@ index c61961f8584..d7b4fa20322 100644
f128: #[cfg(all(not(miri), target_has_reliable_f128_math))],
},
test {
--
--
2.50.1
+2 -10
View File
@@ -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);
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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);
@@ -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>)
+49 -1
View File
@@ -34,7 +34,9 @@
pub use self::freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard};
#[doc(no_inline)]
pub use self::lock::{Lock, LockGuard, Mode};
pub use self::mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode};
pub use self::mode::{
FromDyn, check_dyn_thread_safe, is_dyn_thread_safe, set_dyn_thread_safe_mode,
};
pub use self::parallel::{
broadcast, par_fns, par_for_each_in, par_join, par_map, parallel_guard, spawn,
try_par_for_each_in,
@@ -64,12 +66,20 @@ mod atomic {
mod mode {
use std::sync::atomic::{AtomicU8, Ordering};
use crate::sync::{DynSend, DynSync};
const UNINITIALIZED: u8 = 0;
const DYN_NOT_THREAD_SAFE: u8 = 1;
const DYN_THREAD_SAFE: u8 = 2;
static DYN_THREAD_SAFE_MODE: AtomicU8 = AtomicU8::new(UNINITIALIZED);
// Whether thread safety is enabled (due to running under multiple threads).
#[inline]
pub fn check_dyn_thread_safe() -> Option<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 {
@@ -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.
@@ -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 {
+4 -2
View File
@@ -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;
+2
View File
@@ -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;
@@ -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,
}
}
+2
View File
@@ -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)]`.
@@ -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),
@@ -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,
+1 -1
View File
@@ -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.
-13
View File
@@ -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,
+1 -8
View File
@@ -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 */
+1 -5
View File
@@ -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};
@@ -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| {
@@ -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))
+1 -1
View File
@@ -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()
});
@@ -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
{
@@ -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,
+9
View File
@@ -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 {
+43 -1
View File
@@ -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!(
@@ -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))
}
@@ -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)
}
}
@@ -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!()
}
}
+15 -3
View File
@@ -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)
}
}
@@ -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
+6 -2
View File
@@ -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);
-1
View File
@@ -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")));
+4 -5
View File
@@ -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.
+12 -52
View File
@@ -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)
}
}
}
}
@@ -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 {
+29 -108
View File
@@ -3019,46 +3019,6 @@ pub(crate) struct IllFormedAttributeInput {
pub docs: &'static str,
}
#[derive(Diagnostic)]
#[diag("unicode codepoint changing visible direction of text present in comment")]
#[note(
"these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen"
)]
pub(crate) struct UnicodeTextFlow {
#[label(
"{$num_codepoints ->
[1] this comment contains an invisible unicode text flow control codepoint
*[other] this comment contains invisible unicode text flow control codepoints
}"
)]
pub comment_span: Span,
#[subdiagnostic]
pub characters: Vec<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"
@@ -3192,52 +3152,6 @@ pub(crate) struct PatternsInFnsWithoutBodySub {
pub ident: Ident,
}
#[derive(Diagnostic)]
#[diag("prefix `{$prefix}` is unknown")]
pub(crate) struct ReservedPrefix {
#[label("unknown prefix")]
pub label: Span,
#[suggestion(
"insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
code = " ",
applicability = "machine-applicable"
)]
pub suggestion: Span,
pub prefix: String,
}
#[derive(Diagnostic)]
#[diag("prefix `'r` is reserved")]
pub(crate) struct RawPrefix {
#[label("reserved prefix")]
pub label: Span,
#[suggestion(
"insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
code = " ",
applicability = "machine-applicable"
)]
pub suggestion: Span,
}
#[derive(Diagnostic)]
#[diag(
"this labeled break expression is easy to confuse with an unlabeled break with a labeled value expression"
)]
pub(crate) struct BreakWithLabelAndLoop {
#[subdiagnostic]
pub sub: BreakWithLabelAndLoopSub,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion("wrap this expression in parentheses", applicability = "machine-applicable")]
pub(crate) struct BreakWithLabelAndLoopSub {
#[suggestion_part(code = "(")]
pub left: Span,
#[suggestion_part(code = ")")]
pub right: Span,
}
#[derive(Diagnostic)]
#[diag("where clause not allowed here")]
#[note("see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information")]
@@ -3423,28 +3337,6 @@ pub(crate) enum MutRefSugg {
#[diag("`use` of a local item without leading `self::`, `super::`, or `crate::`")]
pub(crate) struct UnqualifiedLocalImportsDiag;
#[derive(Diagnostic)]
#[diag("will be parsed as a guarded string in Rust 2024")]
pub(crate) struct ReservedString {
#[suggestion(
"insert whitespace here to avoid this being parsed as a guarded string in Rust 2024",
code = " ",
applicability = "machine-applicable"
)]
pub suggestion: Span,
}
#[derive(Diagnostic)]
#[diag("reserved token in Rust 2024")]
pub(crate) struct ReservedMultihash {
#[suggestion(
"insert whitespace here to avoid this being parsed as a forbidden token in Rust 2024",
code = " ",
applicability = "machine-applicable"
)]
pub suggestion: Span,
}
#[derive(Diagnostic)]
#[diag("direct cast of function item into an integer")]
pub(crate) struct FunctionCastsAsIntegerDiag<'tcx> {
@@ -3953,6 +3845,11 @@ pub(crate) struct IgnoredDiagnosticOption {
#[help("at least one of the `message`, `note` and `label` options are expected")]
pub(crate) struct MissingOptionsForOnConstAttr;
#[derive(Diagnostic)]
#[diag("missing options for `on_move` attribute")]
#[help("at least one of the `message`, `note` and `label` options are expected")]
pub(crate) struct MissingOptionsForOnMoveAttr;
#[derive(Diagnostic)]
#[diag("malformed `on_unimplemented` attribute")]
#[help("only `message`, `note` and `label` are allowed as options")]
@@ -3973,3 +3870,27 @@ pub(crate) struct MalformedOnConstAttrLint {
#[diag("`Eq::assert_receiver_is_total_eq` should never be implemented by hand")]
#[note("this method was used to add checks to the `Eq` derive macro")]
pub(crate) struct EqInternalMethodImplemented;
#[derive(Diagnostic)]
#[diag("unknown or malformed `on_move` attribute")]
#[help(
"only `message`, `note` and `label` are allowed as options. Their values must be string literals"
)]
pub(crate) struct MalformedOnMoveAttrLint {
#[label("invalid option found here")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("unknown parameter `{$name}`")]
#[help("expect `Self` as format argument")]
pub(crate) struct OnMoveMalformedFormatLiterals {
pub name: Symbol,
}
#[derive(Diagnostic)]
#[diag("expected a literal or missing delimiter")]
#[help(
"only literals are allowed as values for the `message`, `note` and `label` options. These options must be separated by a comma"
)]
pub(crate) struct OnMoveMalformedAttrExpectedLiteralOrDelimiter;
+8 -9
View File
@@ -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)]
+14 -17
View File
@@ -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;
@@ -476,11 +478,6 @@ fn #name(#key_ty) #return_ty
});
if let Some(feedable) = &modifiers.feedable {
assert!(
modifiers.anon.is_none(),
feedable.span(),
"Query {name} cannot be both `feedable` and `anon`."
);
assert!(
modifiers.eval_always.is_none(),
feedable.span(),
@@ -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`].
+2 -3
View File
@@ -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,
+19 -1
View File
@@ -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
+73 -7
View File
@@ -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)
};
}
+32 -7
View File
@@ -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")
}
+19 -13
View File
@@ -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> {
+27 -16
View File
@@ -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(()),
}
}
}
}
+10 -5
View File
@@ -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
+137 -131
View File
@@ -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) -> ! {
+7
View File
@@ -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,
+10
View File
@@ -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);
}
}
}
+4 -1
View File
@@ -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,
}
+6 -1
View File
@@ -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> {
@@ -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);
-29
View File
@@ -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)]
@@ -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));
}
@@ -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),
};
@@ -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);
}
}
}
}
@@ -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);
+12 -10
View File
@@ -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))
}
@@ -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,
+1 -1
View File
@@ -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);
+7 -6
View File
@@ -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) }
})
}
@@ -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 }
}
@@ -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()
@@ -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);
}
@@ -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,
@@ -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"),
);
@@ -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>(
+108
View File
@@ -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,
}
+51 -8
View File
@@ -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 `#`

Some files were not shown because too many files have changed in this diff Show More