Merge pull request #4837 from rust-lang/rustup-2026-01-30

Automatic Rustup
This commit is contained in:
Oli Scherer 2026-01-30 06:55:44 +00:00 committed by GitHub
commit ca86fb5aa9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
338 changed files with 12824 additions and 7869 deletions

2
.gitmodules vendored
View file

@ -25,7 +25,7 @@
[submodule "src/llvm-project"]
path = src/llvm-project
url = https://github.com/rust-lang/llvm-project.git
branch = rustc/21.1-2025-08-01
branch = rustc/22.1-2026-01-27
shallow = true
[submodule "src/doc/embedded-book"]
path = src/doc/embedded-book

View file

@ -184,9 +184,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "askama"
version = "0.15.1"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7125972258312e79827b60c9eb93938334100245081cf701a2dee981b17427"
checksum = "08e1676b346cadfec169374f949d7490fd80a24193d37d2afce0c047cf695e57"
dependencies = [
"askama_macros",
"itoa",
@ -197,9 +197,9 @@ dependencies = [
[[package]]
name = "askama_derive"
version = "0.15.1"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ba5e7259a1580c61571e3116ebaaa01e3c001b2132b17c4cc5c70780ca3e994"
checksum = "7661ff56517787343f376f75db037426facd7c8d3049cef8911f1e75016f3a37"
dependencies = [
"askama_parser",
"basic-toml",
@ -214,18 +214,18 @@ dependencies = [
[[package]]
name = "askama_macros"
version = "0.15.1"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "236ce20b77cb13506eaf5024899f4af6e12e8825f390bd943c4c37fd8f322e46"
checksum = "713ee4dbfd1eb719c2dab859465b01fa1d21cb566684614a713a6b7a99a4e47b"
dependencies = [
"askama_derive",
]
[[package]]
name = "askama_parser"
version = "0.15.1"
version = "0.15.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c63392767bb2df6aa65a6e1e3b80fd89bb7af6d58359b924c0695620f1512e"
checksum = "1d62d674238a526418b30c0def480d5beadb9d8964e7f38d635b03bf639c704c"
dependencies = [
"rustc-hash 2.1.1",
"serde",

View file

@ -57,7 +57,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
type Item = Symbol;
const CONVERT: ConvertFn<Self::Item> =
|items, first_span| AttributeKind::AllowConstFnUnstable(items, first_span);
|items, first_span| AttributeKind::RustcAllowConstFnUnstable(items, first_span);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),

View file

@ -181,7 +181,7 @@ impl<S: Stage> SingleAttributeParser<S> for ObjcClassParser {
cx.emit_err(NullOnObjcClass { span: nv.value_span });
return None;
}
Some(AttributeKind::ObjcClass { classname, span: cx.attr_span })
Some(AttributeKind::RustcObjcClass { classname, span: cx.attr_span })
}
}
@ -213,7 +213,7 @@ impl<S: Stage> SingleAttributeParser<S> for ObjcSelectorParser {
cx.emit_err(NullOnObjcSelector { span: nv.value_span });
return None;
}
Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span })
Some(AttributeKind::RustcObjcSelector { methname, span: cx.attr_span })
}
}

View file

@ -43,7 +43,7 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
return None;
}
Some(AttributeKind::Confusables {
Some(AttributeKind::RustcConfusables {
symbols: self.confusables,
first_span: self.first_span.unwrap(),
})

View file

@ -16,6 +16,6 @@ impl<S: Stage> SingleAttributeParser<S> for DummyParser {
const TEMPLATE: AttributeTemplate = template!(Word); // Anything, really
fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser) -> Option<AttributeKind> {
Some(AttributeKind::Dummy)
Some(AttributeKind::RustcDummy)
}
}

View file

@ -529,7 +529,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for StdInternalSymbolParser {
Allow(Target::Static),
Allow(Target::ForeignStatic),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcStdInternalSymbol;
}
pub(crate) struct LinkOrdinalParser;

View file

@ -11,7 +11,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for AsPtrParser {
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Method(MethodKind::TraitImpl)),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::AsPtr;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcAsPtr;
}
pub(crate) struct PubTransparentParser;
@ -23,7 +23,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for PubTransparentParser {
Allow(Target::Enum),
Allow(Target::Union),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::PubTransparent;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPubTransparent;
}
pub(crate) struct PassByValueParser;
@ -35,7 +35,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for PassByValueParser {
Allow(Target::Enum),
Allow(Target::TyAlias),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::PassByValue;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassByValue;
}
pub(crate) struct RustcShouldNotBeCalledOnConstItems;

View file

@ -173,7 +173,7 @@ impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
let (stability, span) = self.stability?;
Some(AttributeKind::BodyStability { stability, span })
Some(AttributeKind::RustcBodyStability { stability, span })
}
}
@ -185,7 +185,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for ConstStabilityIndirectParser {
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ConstStabilityIndirect;
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcConstStabilityIndirect;
}
#[derive(Default)]
@ -258,7 +258,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
let (stability, span) = self.stability?;
Some(AttributeKind::ConstStability { stability, span })
Some(AttributeKind::RustcConstStability { stability, span })
}
}

View file

@ -50,7 +50,11 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
cx.duplicate_key(arg.span(), key);
}
}
Some(AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span: cx.attr_span })
Some(AttributeKind::RustcSkipDuringMethodDispatch {
array,
boxed_slice,
span: cx.attr_span,
})
}
}
@ -59,7 +63,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for ParenSugarParser {
const PATH: &[Symbol] = &[sym::rustc_paren_sugar];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ParenSugar;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcParenSugar;
}
pub(crate) struct TypeConstParser;
@ -91,7 +95,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for DenyExplicitImplParser {
const PATH: &[Symbol] = &[sym::rustc_deny_explicit_impl];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DenyExplicitImpl;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcDenyExplicitImpl;
}
pub(crate) struct DynIncompatibleTraitParser;
@ -99,7 +103,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for DynIncompatibleTraitParser {
const PATH: &[Symbol] = &[sym::rustc_dyn_incompatible_trait];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DynIncompatibleTrait;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcDynIncompatibleTrait;
}
// Specialization
@ -109,7 +113,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for SpecializationTraitParser {
const PATH: &[Symbol] = &[sym::rustc_specialization_trait];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::SpecializationTrait;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcSpecializationTrait;
}
pub(crate) struct UnsafeSpecializationMarkerParser;
@ -117,7 +121,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for UnsafeSpecializationMarkerParser {
const PATH: &[Symbol] = &[sym::rustc_unsafe_specialization_marker];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::UnsafeSpecializationMarker;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcUnsafeSpecializationMarker;
}
// Coherence
@ -127,7 +131,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for CoinductiveParser {
const PATH: &[Symbol] = &[sym::rustc_coinductive];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Coinductive;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoinductive;
}
pub(crate) struct AllowIncoherentImplParser;
@ -136,7 +140,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for AllowIncoherentImplParser {
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Method(MethodKind::Inherent))]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::AllowIncoherentImpl;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcAllowIncoherentImpl;
}
pub(crate) struct FundamentalParser;

View file

@ -32,6 +32,6 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
}
None => None,
}
.map(AttributeKind::MacroTransparency)
.map(AttributeKind::RustcMacroTransparency)
}
}

View file

@ -205,7 +205,7 @@ pub(crate) fn allowed_targets_applied(
];
const IMPL_LIKE: &[Target] =
&[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum];
const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum, Target::Union];
let mut added_fake_targets = Vec::new();
filter_targets(

View file

@ -169,7 +169,7 @@ pub(crate) trait TypeOpInfo<'tcx> {
let placeholder_region = ty::Region::new_placeholder(
tcx,
ty::Placeholder::new(adjusted_universe.into(), placeholder.bound),
ty::PlaceholderRegion::new(adjusted_universe.into(), placeholder.bound),
);
let error_region =
@ -179,7 +179,7 @@ pub(crate) trait TypeOpInfo<'tcx> {
adjusted_universe.map(|adjusted| {
ty::Region::new_placeholder(
tcx,
ty::Placeholder::new(adjusted.into(), error_placeholder.bound),
ty::PlaceholderRegion::new(adjusted.into(), error_placeholder.bound),
)
})
} else {

View file

@ -1,3 +1,4 @@
use rustc_abi::FieldIdx;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diag};
use rustc_hir::intravisit::Visitor;
@ -7,7 +8,7 @@ use rustc_middle::mir::*;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
use rustc_span::def_id::DefId;
use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span};
use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
use rustc_trait_selection::infer::InferCtxtExt;
use tracing::debug;
@ -472,49 +473,30 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
if def_id.as_local() == Some(self.mir_def_id())
&& let Some(upvar_field) = upvar_field =>
{
let closure_kind_ty = closure_args.as_closure().kind_ty();
let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind,
Some(ty::ClosureKind::FnOnce) => {
bug!("closure kind does not match first argument type")
}
None => bug!("closure kind not inferred by borrowck"),
};
let capture_description =
format!("captured variable in an `{closure_kind}` closure");
let upvar = &self.upvars[upvar_field.index()];
let upvar_hir_id = upvar.get_root_variable();
let upvar_name = upvar.to_string(tcx);
let upvar_span = tcx.hir_span(upvar_hir_id);
let place_name = self.describe_any_place(move_place.as_ref());
let place_description =
if self.is_upvar_field_projection(move_place.as_ref()).is_some() {
format!("{place_name}, a {capture_description}")
} else {
format!("{place_name}, as `{upvar_name}` is a {capture_description}")
};
debug!(
"report: closure_kind_ty={:?} closure_kind={:?} place_description={:?}",
closure_kind_ty, closure_kind, place_description,
);
let closure_span = tcx.def_span(def_id);
self.cannot_move_out_of(span, &place_description)
.with_span_label(upvar_span, "captured outer variable")
.with_span_label(
closure_span,
format!("captured by this `{closure_kind}` closure"),
)
.with_span_help(
self.get_closure_bound_clause_span(*def_id),
"`Fn` and `FnMut` closures require captured values to be able to be \
consumed multiple times, but `FnOnce` closures may consume them only once",
)
self.report_closure_move_error(
span,
move_place,
*def_id,
closure_args.as_closure().kind_ty(),
upvar_field,
ty::Asyncness::No,
)
}
ty::CoroutineClosure(def_id, closure_args)
if def_id.as_local() == Some(self.mir_def_id())
&& let Some(upvar_field) = upvar_field
&& self
.get_closure_bound_clause_span(*def_id, ty::Asyncness::Yes)
.is_some() =>
{
self.report_closure_move_error(
span,
move_place,
*def_id,
closure_args.as_coroutine_closure().kind_ty(),
upvar_field,
ty::Asyncness::Yes,
)
}
_ => {
let source = self.borrowed_content_source(deref_base);
@ -563,45 +545,134 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
err
}
fn get_closure_bound_clause_span(&self, def_id: DefId) -> Span {
fn report_closure_move_error(
&self,
span: Span,
move_place: Place<'tcx>,
def_id: DefId,
closure_kind_ty: Ty<'tcx>,
upvar_field: FieldIdx,
asyncness: ty::Asyncness,
) -> Diag<'infcx> {
let tcx = self.infcx.tcx;
let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind,
Some(ty::ClosureKind::FnOnce) => {
bug!("closure kind does not match first argument type")
}
None => bug!("closure kind not inferred by borrowck"),
};
let async_prefix = if asyncness.is_async() { "Async" } else { "" };
let capture_description =
format!("captured variable in an `{async_prefix}{closure_kind}` closure");
let upvar = &self.upvars[upvar_field.index()];
let upvar_hir_id = upvar.get_root_variable();
let upvar_name = upvar.to_string(tcx);
let upvar_span = tcx.hir_span(upvar_hir_id);
let place_name = self.describe_any_place(move_place.as_ref());
let place_description = if self.is_upvar_field_projection(move_place.as_ref()).is_some() {
format!("{place_name}, a {capture_description}")
} else {
format!("{place_name}, as `{upvar_name}` is a {capture_description}")
};
debug!(?closure_kind_ty, ?closure_kind, ?place_description);
let closure_span = tcx.def_span(def_id);
let help_msg = format!(
"`{async_prefix}Fn` and `{async_prefix}FnMut` closures require captured values to \
be able to be consumed multiple times, but `{async_prefix}FnOnce` closures may \
consume them only once"
);
let mut err = self
.cannot_move_out_of(span, &place_description)
.with_span_label(upvar_span, "captured outer variable")
.with_span_label(
closure_span,
format!("captured by this `{async_prefix}{closure_kind}` closure"),
);
if let Some(bound_span) = self.get_closure_bound_clause_span(def_id, asyncness) {
err.span_help(bound_span, help_msg);
} else if !asyncness.is_async() {
// For sync closures, always emit the help message even without a span.
// For async closures, we only enter this branch if we found a valid span
// (due to the match guard), so no fallback is needed.
err.help(help_msg);
}
err
}
fn get_closure_bound_clause_span(
&self,
def_id: DefId,
asyncness: ty::Asyncness,
) -> Option<Span> {
let tcx = self.infcx.tcx;
let typeck_result = tcx.typeck(self.mir_def_id());
// Check whether the closure is an argument to a call, if so,
// get the instantiated where-bounds of that call.
let closure_hir_id = tcx.local_def_id_to_hir_id(def_id.expect_local());
let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return DUMMY_SP };
let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return None };
let predicates = match parent.kind {
hir::ExprKind::Call(callee, _) => {
let Some(ty) = typeck_result.node_type_opt(callee.hir_id) else { return DUMMY_SP };
let ty::FnDef(fn_def_id, args) = ty.kind() else { return DUMMY_SP };
let ty = typeck_result.node_type_opt(callee.hir_id)?;
let ty::FnDef(fn_def_id, args) = ty.kind() else { return None };
tcx.predicates_of(fn_def_id).instantiate(tcx, args)
}
hir::ExprKind::MethodCall(..) => {
let Some((_, method)) = typeck_result.type_dependent_def(parent.hir_id) else {
return DUMMY_SP;
};
let (_, method) = typeck_result.type_dependent_def(parent.hir_id)?;
let args = typeck_result.node_args(parent.hir_id);
tcx.predicates_of(method).instantiate(tcx, args)
}
_ => return DUMMY_SP,
_ => return None,
};
// Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`.
// Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`
// or `AsyncFn[Mut]`.
for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) {
if let Some(clause) = pred.as_trait_clause()
&& let ty::Closure(clause_closure_def_id, _) = clause.self_ty().skip_binder().kind()
&& *clause_closure_def_id == def_id
&& (tcx.lang_items().fn_mut_trait() == Some(clause.def_id())
|| tcx.lang_items().fn_trait() == Some(clause.def_id()))
{
// Found `<TyOfCapturingClosure as FnMut>`
// We point at the `Fn()` or `FnMut()` bound that coerced the closure, which
// could be changed to `FnOnce()` to avoid the move error.
return *span;
let dominated_by_fn_trait = self
.closure_clause_kind(*pred, def_id, asyncness)
.is_some_and(|kind| matches!(kind, ty::ClosureKind::Fn | ty::ClosureKind::FnMut));
if dominated_by_fn_trait {
// Found `<TyOfCapturingClosure as FnMut>` or
// `<TyOfCapturingClosure as AsyncFnMut>`.
// We point at the bound that coerced the closure, which could be changed
// to `FnOnce()` or `AsyncFnOnce()` to avoid the move error.
return Some(*span);
}
}
DUMMY_SP
None
}
/// If `pred` is a trait clause binding the closure `def_id` to `Fn`/`FnMut`/`FnOnce`
/// (or their async equivalents based on `asyncness`), returns the corresponding
/// `ClosureKind`. Otherwise returns `None`.
fn closure_clause_kind(
&self,
pred: ty::Clause<'tcx>,
def_id: DefId,
asyncness: ty::Asyncness,
) -> Option<ty::ClosureKind> {
let tcx = self.infcx.tcx;
let clause = pred.as_trait_clause()?;
let kind = match asyncness {
ty::Asyncness::Yes => tcx.async_fn_trait_kind_from_def_id(clause.def_id()),
ty::Asyncness::No => tcx.fn_trait_kind_from_def_id(clause.def_id()),
}?;
match clause.self_ty().skip_binder().kind() {
ty::Closure(id, _) | ty::CoroutineClosure(id, _) if *id == def_id => Some(kind),
_ => None,
}
}
fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) {

View file

@ -62,57 +62,23 @@ impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
}
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
enum PlaceholderReachability {
/// This SCC reaches no placeholders.
NoPlaceholders,
/// This SCC reaches at least one placeholder.
Placeholders {
/// The largest-universed placeholder we can reach
max_universe: (UniverseIndex, RegionVid),
struct PlaceholderReachability {
/// The largest-universed placeholder we can reach
max_universe: (UniverseIndex, RegionVid),
/// The placeholder with the smallest ID
min_placeholder: RegionVid,
/// The placeholder with the smallest ID
min_placeholder: RegionVid,
/// The placeholder with the largest ID
max_placeholder: RegionVid,
},
/// The placeholder with the largest ID
max_placeholder: RegionVid,
}
impl PlaceholderReachability {
/// Merge the reachable placeholders of two graph components.
fn merge(self, other: PlaceholderReachability) -> PlaceholderReachability {
use PlaceholderReachability::*;
match (self, other) {
(NoPlaceholders, NoPlaceholders) => NoPlaceholders,
(NoPlaceholders, p @ Placeholders { .. })
| (p @ Placeholders { .. }, NoPlaceholders) => p,
(
Placeholders {
min_placeholder: min_pl,
max_placeholder: max_pl,
max_universe: max_u,
},
Placeholders { min_placeholder, max_placeholder, max_universe },
) => Placeholders {
min_placeholder: min_pl.min(min_placeholder),
max_placeholder: max_pl.max(max_placeholder),
max_universe: max_u.max(max_universe),
},
}
}
fn max_universe(&self) -> Option<(UniverseIndex, RegionVid)> {
match self {
Self::NoPlaceholders => None,
Self::Placeholders { max_universe, .. } => Some(*max_universe),
}
}
/// If we have reached placeholders, determine if they can
/// be named from this universe.
fn can_be_named_by(&self, from: UniverseIndex) -> bool {
self.max_universe()
.is_none_or(|(max_placeholder_universe, _)| from.can_name(max_placeholder_universe))
fn merge(&mut self, other: &Self) {
self.max_universe = self.max_universe.max(other.max_universe);
self.min_placeholder = self.min_placeholder.min(other.min_placeholder);
self.max_placeholder = self.max_placeholder.max(other.max_placeholder);
}
}
@ -120,7 +86,7 @@ impl PlaceholderReachability {
/// the values of its elements. This annotates a single SCC.
#[derive(Copy, Debug, Clone)]
pub(crate) struct RegionTracker {
reachable_placeholders: PlaceholderReachability,
reachable_placeholders: Option<PlaceholderReachability>,
/// The largest universe nameable from this SCC.
/// It is the smallest nameable universes of all
@ -135,13 +101,13 @@ impl RegionTracker {
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
let reachable_placeholders =
if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) {
PlaceholderReachability::Placeholders {
Some(PlaceholderReachability {
max_universe: (definition.universe, rvid),
min_placeholder: rvid,
max_placeholder: rvid,
}
})
} else {
PlaceholderReachability::NoPlaceholders
None
};
Self {
@ -159,43 +125,46 @@ impl RegionTracker {
}
pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex {
if let Some((universe, _)) = self.reachable_placeholders.max_universe() {
universe
} else {
UniverseIndex::ROOT
}
self.reachable_placeholders.map(|pls| pls.max_universe.0).unwrap_or(UniverseIndex::ROOT)
}
/// Can all reachable placeholders be named from `from`?
/// True vacuously in case no placeholders were reached.
fn placeholders_can_be_named_by(&self, from: UniverseIndex) -> bool {
self.reachable_placeholders.is_none_or(|pls| from.can_name(pls.max_universe.0))
}
/// Determine if we can name all the placeholders in `other`.
pub(crate) fn can_name_all_placeholders(&self, other: Self) -> bool {
other.reachable_placeholders.can_be_named_by(self.max_nameable_universe.0)
// HACK: We first check whether we can name the highest existential universe
// of `other`. This only exists to avoid errors in case that scc already
// depends on a placeholder it cannot name itself.
self.max_nameable_universe().can_name(other.max_nameable_universe())
|| other.placeholders_can_be_named_by(self.max_nameable_universe.0)
}
/// If this SCC reaches a placeholder it can't name, return it.
fn unnameable_placeholder(&self) -> Option<(UniverseIndex, RegionVid)> {
self.reachable_placeholders.max_universe().filter(|&(placeholder_universe, _)| {
!self.max_nameable_universe().can_name(placeholder_universe)
})
self.reachable_placeholders
.filter(|pls| !self.max_nameable_universe().can_name(pls.max_universe.0))
.map(|pls| pls.max_universe)
}
}
impl scc::Annotation for RegionTracker {
fn merge_scc(self, other: Self) -> Self {
fn update_scc(&mut self, other: &Self) {
trace!("{:?} << {:?}", self.representative, other.representative);
Self {
representative: self.representative.min(other.representative),
max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe),
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
}
self.representative = self.representative.min(other.representative);
self.update_reachable(other);
}
fn merge_reached(self, other: Self) -> Self {
Self {
max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe),
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
representative: self.representative,
}
fn update_reachable(&mut self, other: &Self) {
self.max_nameable_universe = self.max_nameable_universe.min(other.max_nameable_universe);
match (self.reachable_placeholders.as_mut(), other.reachable_placeholders.as_ref()) {
(None, None) | (Some(_), None) => (),
(None, Some(theirs)) => self.reachable_placeholders = Some(*theirs),
(Some(ours), Some(theirs)) => ours.merge(theirs),
};
}
}

View file

@ -52,7 +52,7 @@ fn render_region_vid<'tcx>(
format!(" (for<{}>)", tcx.item_name(def_id))
}
ty::BoundRegionKind::ClosureEnv | ty::BoundRegionKind::Anon => " (for<'_>)".to_string(),
ty::BoundRegionKind::NamedAnon(_) => {
ty::BoundRegionKind::NamedForPrinting(_) => {
bug!("only used for pretty printing")
}
},

View file

@ -10,8 +10,8 @@ use rustc_middle::ty::{self, RegionVid};
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
use tracing::debug;
use crate::BorrowIndex;
use crate::polonius::LiveLoans;
use crate::{BorrowIndex, TyCtxt};
rustc_index::newtype_index! {
/// A single integer representing a `ty::Placeholder`.
@ -420,18 +420,18 @@ impl ToElementIndex<'_> for RegionVid {
impl<'tcx> ToElementIndex<'tcx> for ty::PlaceholderRegion<'tcx> {
fn add_to_row<N: Idx>(self, values: &mut RegionValues<'tcx, N>, row: N) -> bool
where
Self: Into<ty::Placeholder<TyCtxt<'tcx>, ty::BoundRegion>>,
Self: Into<ty::PlaceholderRegion<'tcx>>,
{
let placeholder: ty::Placeholder<TyCtxt<'tcx>, ty::BoundRegion> = self.into();
let placeholder: ty::PlaceholderRegion<'tcx> = self.into();
let index = values.placeholder_indices.lookup_index(placeholder);
values.placeholders.insert(row, index)
}
fn contained_in_row<N: Idx>(self, values: &RegionValues<'tcx, N>, row: N) -> bool
where
Self: Into<ty::Placeholder<TyCtxt<'tcx>, ty::BoundRegion>>,
Self: Into<ty::PlaceholderRegion<'tcx>>,
{
let placeholder: ty::Placeholder<TyCtxt<'tcx>, ty::BoundRegion> = self.into();
let placeholder: ty::PlaceholderRegion<'tcx> = self.into();
let index = values.placeholder_indices.lookup_index(placeholder);
values.placeholders.contains(row, index)
}

View file

@ -775,7 +775,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
ty::BoundRegionKind::Anon => sym::anon,
ty::BoundRegionKind::Named(def_id) => tcx.item_name(def_id),
ty::BoundRegionKind::ClosureEnv => sym::env,
ty::BoundRegionKind::NamedAnon(_) => {
ty::BoundRegionKind::NamedForPrinting(_) => {
bug!("only used for pretty printing")
}
};

View file

@ -174,7 +174,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
let infcx = self.type_checker.infcx;
let mut lazy_universe = None;
let delegate = FnMutDelegate {
regions: &mut |br: ty::BoundRegion| {
regions: &mut |br: ty::BoundRegion<'tcx>| {
// The first time this closure is called, create a
// new universe for the placeholders we will make
// from here out.
@ -191,10 +191,10 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
placeholder_reg
},
types: &mut |_bound_ty: ty::BoundTy| {
types: &mut |_bound_ty: ty::BoundTy<'tcx>| {
unreachable!("we only replace regions in nll_relate, not types")
},
consts: &mut |_bound_const: ty::BoundConst| {
consts: &mut |_bound_const: ty::BoundConst<'tcx>| {
unreachable!("we only replace regions in nll_relate, not consts")
},
};
@ -218,7 +218,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
let infcx = self.type_checker.infcx;
let mut reg_map = FxHashMap::default();
let delegate = FnMutDelegate {
regions: &mut |br: ty::BoundRegion| {
regions: &mut |br: ty::BoundRegion<'tcx>| {
if let Some(ex_reg_var) = reg_map.get(&br) {
*ex_reg_var
} else {
@ -230,10 +230,10 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
ex_reg_var
}
},
types: &mut |_bound_ty: ty::BoundTy| {
types: &mut |_bound_ty: ty::BoundTy<'tcx>| {
unreachable!("we only replace regions in nll_relate, not types")
},
consts: &mut |_bound_const: ty::BoundConst| {
consts: &mut |_bound_const: ty::BoundConst<'tcx>| {
unreachable!("we only replace regions in nll_relate, not consts")
},
};
@ -268,7 +268,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
ty::BoundRegionKind::Anon => sym::anon,
ty::BoundRegionKind::Named(def_id) => self.type_checker.tcx().item_name(def_id),
ty::BoundRegionKind::ClosureEnv => sym::env,
ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
ty::BoundRegionKind::NamedForPrinting(_) => bug!("only used for pretty printing"),
};
if cfg!(debug_assertions) {

View file

@ -188,19 +188,6 @@ impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
load
}
}
fn memset(&mut self, ptr: &'ll Value, fill_byte: &'ll Value, size: &'ll Value, align: Align) {
unsafe {
llvm::LLVMRustBuildMemSet(
self.llbuilder,
ptr,
align.bytes() as c_uint,
fill_byte,
size,
false,
);
}
}
}
/// Empty string, to be used where LLVM expects an instruction name, indicating

View file

@ -19,8 +19,6 @@ pub(crate) struct OffloadGlobals<'ll> {
pub launcher_fn: &'ll llvm::Value,
pub launcher_ty: &'ll llvm::Type,
pub bin_desc: &'ll llvm::Type,
pub kernel_args_ty: &'ll llvm::Type,
pub offload_entry_ty: &'ll llvm::Type,
@ -31,8 +29,8 @@ pub(crate) struct OffloadGlobals<'ll> {
pub ident_t_global: &'ll llvm::Value,
pub register_lib: &'ll llvm::Value,
pub unregister_lib: &'ll llvm::Value,
// FIXME(offload): Drop this, once we fully automated our offload compilation pipeline, since
// LLVM will initialize them for us if it sees gpu kernels being registered.
pub init_rtls: &'ll llvm::Value,
}
@ -44,15 +42,6 @@ impl<'ll> OffloadGlobals<'ll> {
let (begin_mapper, _, end_mapper, mapper_fn_ty) = gen_tgt_data_mappers(cx);
let ident_t_global = generate_at_one(cx);
let tptr = cx.type_ptr();
let ti32 = cx.type_i32();
let tgt_bin_desc_ty = vec![ti32, tptr, tptr, tptr];
let bin_desc = cx.type_named_struct("struct.__tgt_bin_desc");
cx.set_struct_body(bin_desc, &tgt_bin_desc_ty, false);
let reg_lib_decl = cx.type_func(&[cx.type_ptr()], cx.type_void());
let register_lib = declare_offload_fn(&cx, "__tgt_register_lib", reg_lib_decl);
let unregister_lib = declare_offload_fn(&cx, "__tgt_unregister_lib", reg_lib_decl);
let init_ty = cx.type_func(&[], cx.type_void());
let init_rtls = declare_offload_fn(cx, "__tgt_init_all_rtls", init_ty);
@ -63,20 +52,84 @@ impl<'ll> OffloadGlobals<'ll> {
OffloadGlobals {
launcher_fn,
launcher_ty,
bin_desc,
kernel_args_ty,
offload_entry_ty,
begin_mapper,
end_mapper,
mapper_fn_ty,
ident_t_global,
register_lib,
unregister_lib,
init_rtls,
}
}
}
// We need to register offload before using it. We also should unregister it once we are done, for
// good measures. Previously we have done so before and after each individual offload intrinsic
// call, but that comes at a performance cost. The repeated (un)register calls might also confuse
// the LLVM ompOpt pass, which tries to move operations to a better location. The easiest solution,
// which we copy from clang, is to just have those two calls once, in the global ctor/dtor section
// of the final binary.
pub(crate) fn register_offload<'ll>(cx: &CodegenCx<'ll, '_>) {
// First we check quickly whether we already have done our setup, in which case we return early.
// Shouldn't be needed for correctness.
let register_lib_name = "__tgt_register_lib";
if cx.get_function(register_lib_name).is_some() {
return;
}
let reg_lib_decl = cx.type_func(&[cx.type_ptr()], cx.type_void());
let register_lib = declare_offload_fn(&cx, register_lib_name, reg_lib_decl);
let unregister_lib = declare_offload_fn(&cx, "__tgt_unregister_lib", reg_lib_decl);
let ptr_null = cx.const_null(cx.type_ptr());
let const_struct = cx.const_struct(&[cx.get_const_i32(0), ptr_null, ptr_null, ptr_null], false);
let omp_descriptor =
add_global(cx, ".omp_offloading.descriptor", const_struct, InternalLinkage);
// @.omp_offloading.descriptor = internal constant %__tgt_bin_desc { i32 1, ptr @.omp_offloading.device_images, ptr @__start_llvm_offload_entries, ptr @__stop_llvm_offload_entries }
// @.omp_offloading.descriptor = internal constant %__tgt_bin_desc { i32 0, ptr null, ptr null, ptr null }
let atexit = cx.type_func(&[cx.type_ptr()], cx.type_i32());
let atexit_fn = declare_offload_fn(cx, "atexit", atexit);
let desc_ty = cx.type_func(&[], cx.type_void());
let reg_name = ".omp_offloading.descriptor_reg";
let unreg_name = ".omp_offloading.descriptor_unreg";
let desc_reg_fn = declare_offload_fn(cx, reg_name, desc_ty);
let desc_unreg_fn = declare_offload_fn(cx, unreg_name, desc_ty);
llvm::set_linkage(desc_reg_fn, InternalLinkage);
llvm::set_linkage(desc_unreg_fn, InternalLinkage);
llvm::set_section(desc_reg_fn, c".text.startup");
llvm::set_section(desc_unreg_fn, c".text.startup");
// define internal void @.omp_offloading.descriptor_reg() section ".text.startup" {
// entry:
// call void @__tgt_register_lib(ptr @.omp_offloading.descriptor)
// %0 = call i32 @atexit(ptr @.omp_offloading.descriptor_unreg)
// ret void
// }
let bb = Builder::append_block(cx, desc_reg_fn, "entry");
let mut a = Builder::build(cx, bb);
a.call(reg_lib_decl, None, None, register_lib, &[omp_descriptor], None, None);
a.call(atexit, None, None, atexit_fn, &[desc_unreg_fn], None, None);
a.ret_void();
// define internal void @.omp_offloading.descriptor_unreg() section ".text.startup" {
// entry:
// call void @__tgt_unregister_lib(ptr @.omp_offloading.descriptor)
// ret void
// }
let bb = Builder::append_block(cx, desc_unreg_fn, "entry");
let mut a = Builder::build(cx, bb);
a.call(reg_lib_decl, None, None, unregister_lib, &[omp_descriptor], None, None);
a.ret_void();
// @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 101, ptr @.omp_offloading.descriptor_reg, ptr null }]
let args = vec![cx.get_const_i32(101), desc_reg_fn, ptr_null];
let const_struct = cx.const_struct(&args, false);
let arr = cx.const_array(cx.val_ty(const_struct), &[const_struct]);
add_global(cx, "llvm.global_ctors", arr, AppendingLinkage);
}
pub(crate) struct OffloadKernelDims<'ll> {
num_workgroups: &'ll Value,
threads_per_block: &'ll Value,
@ -487,9 +540,6 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
let tgt_decl = offload_globals.launcher_fn;
let tgt_target_kernel_ty = offload_globals.launcher_ty;
// %struct.__tgt_bin_desc = type { i32, ptr, ptr, ptr }
let tgt_bin_desc = offload_globals.bin_desc;
let tgt_kernel_decl = offload_globals.kernel_args_ty;
let begin_mapper_decl = offload_globals.begin_mapper;
let end_mapper_decl = offload_globals.end_mapper;
@ -513,12 +563,9 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
}
// Step 0)
// %struct.__tgt_bin_desc = type { i32, ptr, ptr, ptr }
// %6 = alloca %struct.__tgt_bin_desc, align 8
unsafe {
llvm::LLVMRustPositionBuilderPastAllocas(&builder.llbuilder, builder.llfn());
}
let tgt_bin_desc_alloca = builder.direct_alloca(tgt_bin_desc, Align::EIGHT, "EmptyDesc");
let ty = cx.type_array(cx.type_ptr(), num_args);
// Baseptr are just the input pointer to the kernel, stored in a local alloca
@ -536,7 +583,6 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
unsafe {
llvm::LLVMPositionBuilderAtEnd(&builder.llbuilder, bb);
}
builder.memset(tgt_bin_desc_alloca, cx.get_const_i8(0), cx.get_const_i64(32), Align::EIGHT);
// Now we allocate once per function param, a copy to be passed to one of our maps.
let mut vals = vec![];
@ -574,15 +620,9 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
geps.push(gep);
}
let mapper_fn_ty = cx.type_func(&[cx.type_ptr()], cx.type_void());
let register_lib_decl = offload_globals.register_lib;
let unregister_lib_decl = offload_globals.unregister_lib;
let init_ty = cx.type_func(&[], cx.type_void());
let init_rtls_decl = offload_globals.init_rtls;
// FIXME(offload): Later we want to add them to the wrapper code, rather than our main function.
// call void @__tgt_register_lib(ptr noundef %6)
builder.call(mapper_fn_ty, None, None, register_lib_decl, &[tgt_bin_desc_alloca], None, None);
// call void @__tgt_init_all_rtls()
builder.call(init_ty, None, None, init_rtls_decl, &[], None, None);
@ -679,6 +719,4 @@ pub(crate) fn gen_call_handling<'ll, 'tcx>(
num_args,
s_ident_t,
);
builder.call(mapper_fn_ty, None, None, unregister_lib_decl, &[tgt_bin_desc_alloca], None, None);
}

View file

@ -124,6 +124,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
pub(crate) fn const_null(&self, t: &'ll Type) -> &'ll Value {
unsafe { llvm::LLVMConstNull(t) }
}
pub(crate) fn const_struct(&self, elts: &[&'ll Value], packed: bool) -> &'ll Value {
struct_in_context(self.llcx(), elts, packed)
}
}
impl<'ll, 'tcx> ConstCodegenMethods for CodegenCx<'ll, 'tcx> {

View file

@ -30,7 +30,9 @@ use tracing::debug;
use crate::abi::FnAbiLlvmExt;
use crate::builder::Builder;
use crate::builder::autodiff::{adjust_activity_to_abi, generate_enzyme_call};
use crate::builder::gpu_offload::{OffloadKernelDims, gen_call_handling, gen_define_handling};
use crate::builder::gpu_offload::{
OffloadKernelDims, gen_call_handling, gen_define_handling, register_offload,
};
use crate::context::CodegenCx;
use crate::declare::declare_raw_fn;
use crate::errors::{
@ -1402,6 +1404,7 @@ fn codegen_offload<'ll, 'tcx>(
return;
}
};
register_offload(cx);
let offload_data = gen_define_handling(&cx, &metadata, target_symbol, offload_globals);
gen_call_handling(bx, &offload_data, &args, &types, &metadata, offload_globals, &offload_dims);
}

View file

@ -1208,10 +1208,23 @@ impl<'a> Linker for EmLinker<'a> {
fn set_output_kind(
&mut self,
_output_kind: LinkOutputKind,
output_kind: LinkOutputKind,
_crate_type: CrateType,
_out_filename: &Path,
) {
match output_kind {
LinkOutputKind::DynamicNoPicExe | LinkOutputKind::DynamicPicExe => {
self.cmd.arg("-sMAIN_MODULE=2");
}
LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => {
self.cmd.arg("-sSIDE_MODULE=2");
}
// -fno-pie is the default on Emscripten.
LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => {}
LinkOutputKind::WasiReactorExe => {
unreachable!();
}
}
}
fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) {

View file

@ -189,7 +189,7 @@ fn process_builtin_attrs(
},
AttributeKind::FfiConst(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST,
AttributeKind::FfiPure(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE,
AttributeKind::StdInternalSymbol(_) => {
AttributeKind::RustcStdInternalSymbol(_) => {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL
}
AttributeKind::Linkage(linkage, span) => {
@ -217,10 +217,10 @@ fn process_builtin_attrs(
AttributeKind::Sanitize { span, .. } => {
interesting_spans.sanitize = Some(*span);
}
AttributeKind::ObjcClass { classname, .. } => {
AttributeKind::RustcObjcClass { classname, .. } => {
codegen_fn_attrs.objc_class = Some(*classname);
}
AttributeKind::ObjcSelector { methname, .. } => {
AttributeKind::RustcObjcSelector { methname, .. } => {
codegen_fn_attrs.objc_selector = Some(*methname);
}
AttributeKind::EiiForeignItem => {

View file

@ -83,7 +83,7 @@ pub fn rustc_allow_const_fn_unstable(
) -> bool {
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
find_attr!(attrs, AttributeKind::AllowConstFnUnstable(syms, _) if syms.contains(&feature_gate))
find_attr!(attrs, AttributeKind::RustcAllowConstFnUnstable(syms, _) if syms.contains(&feature_gate))
}
/// Returns `true` if the given `def_id` (trait or function) is "safe to expose on stable".

View file

@ -0,0 +1,178 @@
use rustc_middle::mir::interpret::{CtfeProvenance, InterpResult, Scalar, interp_ok};
use rustc_middle::ty::{Region, Ty};
use rustc_middle::{span_bug, ty};
use rustc_span::def_id::DefId;
use rustc_span::sym;
use crate::const_eval::CompileTimeMachine;
use crate::interpret::{Immediate, InterpCx, MPlaceTy, MemoryKind, Writeable};
impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
pub(crate) fn write_dyn_trait_type_info(
&mut self,
dyn_place: impl Writeable<'tcx, CtfeProvenance>,
data: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
region: Region<'tcx>,
) -> InterpResult<'tcx> {
let tcx = self.tcx.tcx;
// Find the principal trait ref (for super trait collection), collect auto traits,
// and collect all projection predicates (used when computing TypeId for each supertrait).
let mut principal: Option<ty::Binder<'tcx, ty::ExistentialTraitRef<'tcx>>> = None;
let mut auto_traits_def_ids: Vec<ty::Binder<'tcx, DefId>> = Vec::new();
let mut projections: Vec<ty::Binder<'tcx, ty::ExistentialProjection<'tcx>>> = Vec::new();
for b in data.iter() {
match b.skip_binder() {
ty::ExistentialPredicate::Trait(tr) => principal = Some(b.rebind(tr)),
ty::ExistentialPredicate::AutoTrait(did) => auto_traits_def_ids.push(b.rebind(did)),
ty::ExistentialPredicate::Projection(p) => projections.push(b.rebind(p)),
}
}
// This is to make principal dyn type include Trait and projection predicates, excluding auto traits.
let principal_ty: Option<Ty<'tcx>> = principal.map(|_tr| {
let preds = tcx
.mk_poly_existential_predicates_from_iter(data.iter().filter(|b| {
!matches!(b.skip_binder(), ty::ExistentialPredicate::AutoTrait(_))
}));
Ty::new_dynamic(tcx, preds, region)
});
// DynTrait { predicates: &'static [Trait] }
for (field_idx, field) in
dyn_place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
{
let field_place = self.project_field(&dyn_place, field_idx)?;
match field.name {
sym::predicates => {
self.write_dyn_trait_predicates_slice(
&field_place,
principal_ty,
&auto_traits_def_ids,
region,
)?;
}
other => {
span_bug!(self.tcx.def_span(field.did), "unimplemented DynTrait field {other}")
}
}
}
interp_ok(())
}
fn mk_dyn_principal_auto_trait_ty(
&self,
auto_trait_def_id: ty::Binder<'tcx, DefId>,
region: Region<'tcx>,
) -> Ty<'tcx> {
let tcx = self.tcx.tcx;
// Preserve the binder vars from the original auto-trait predicate.
let pred_inner = ty::ExistentialPredicate::AutoTrait(auto_trait_def_id.skip_binder());
let pred = ty::Binder::bind_with_vars(pred_inner, auto_trait_def_id.bound_vars());
let preds = tcx.mk_poly_existential_predicates_from_iter([pred].into_iter());
Ty::new_dynamic(tcx, preds, region)
}
fn write_dyn_trait_predicates_slice(
&mut self,
slice_place: &impl Writeable<'tcx, CtfeProvenance>,
principal_ty: Option<Ty<'tcx>>,
auto_trait_def_ids: &[ty::Binder<'tcx, DefId>],
region: Region<'tcx>,
) -> InterpResult<'tcx> {
let tcx = self.tcx.tcx;
// total entries in DynTrait predicates
let total_len = principal_ty.map(|_| 1).unwrap_or(0) + auto_trait_def_ids.len();
// element type = DynTraitPredicate
let slice_ty = slice_place.layout().ty.builtin_deref(false).unwrap(); // [DynTraitPredicate]
let elem_ty = slice_ty.sequence_element_type(tcx); // DynTraitPredicate
let arr_layout = self.layout_of(Ty::new_array(tcx, elem_ty, total_len as u64))?;
let arr_place = self.allocate(arr_layout, MemoryKind::Stack)?;
let mut elems = self.project_array_fields(&arr_place)?;
// principal entry (if any) - NOT an auto trait
if let Some(principal_ty) = principal_ty {
let Some((_i, elem_place)) = elems.next(self)? else {
span_bug!(self.tcx.span, "DynTrait.predicates length computed wrong (principal)");
};
self.write_dyn_trait_predicate(elem_place, principal_ty, false)?;
}
// auto trait entries - these ARE auto traits
for auto in auto_trait_def_ids {
let Some((_i, elem_place)) = elems.next(self)? else {
span_bug!(self.tcx.span, "DynTrait.predicates length computed wrong (auto)");
};
let auto_ty = self.mk_dyn_principal_auto_trait_ty(*auto, region);
self.write_dyn_trait_predicate(elem_place, auto_ty, true)?;
}
let arr_place = arr_place.map_provenance(CtfeProvenance::as_immutable);
let imm = Immediate::new_slice(arr_place.ptr(), total_len as u64, self);
self.write_immediate(imm, slice_place)
}
fn write_dyn_trait_predicate(
&mut self,
predicate_place: MPlaceTy<'tcx>,
trait_ty: Ty<'tcx>,
is_auto: bool,
) -> InterpResult<'tcx> {
// DynTraitPredicate { trait_ty: Trait }
for (field_idx, field) in predicate_place
.layout
.ty
.ty_adt_def()
.unwrap()
.non_enum_variant()
.fields
.iter_enumerated()
{
let field_place = self.project_field(&predicate_place, field_idx)?;
match field.name {
sym::trait_ty => {
// Now write the Trait struct
self.write_trait(field_place, trait_ty, is_auto)?;
}
other => {
span_bug!(
self.tcx.def_span(field.did),
"unimplemented DynTraitPredicate field {other}"
)
}
}
}
interp_ok(())
}
fn write_trait(
&mut self,
trait_place: MPlaceTy<'tcx>,
trait_ty: Ty<'tcx>,
is_auto: bool,
) -> InterpResult<'tcx> {
// Trait { ty: TypeId, is_auto: bool }
for (field_idx, field) in
trait_place.layout.ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated()
{
let field_place = self.project_field(&trait_place, field_idx)?;
match field.name {
sym::ty => {
self.write_type_id(trait_ty, &field_place)?;
}
sym::is_auto => {
self.write_scalar(Scalar::from_bool(is_auto), &field_place)?;
}
other => {
span_bug!(self.tcx.def_span(field.did), "unimplemented Trait field {other}")
}
}
}
interp_ok(())
}
}

View file

@ -9,6 +9,7 @@ use tracing::instrument;
use crate::interpret::InterpCx;
mod dummy_machine;
mod dyn_trait;
mod error;
mod eval_queries;
mod fn_queries;

View file

@ -129,13 +129,18 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> {
variant
}
ty::Dynamic(predicates, region) => {
let (variant, variant_place) = downcast(sym::DynTrait)?;
let dyn_place = self.project_field(&variant_place, FieldIdx::ZERO)?;
self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?;
variant
}
ty::Adt(_, _)
| ty::Foreign(_)
| ty::Pat(_, _)
| ty::FnDef(..)
| ty::FnPtr(..)
| ty::UnsafeBinder(..)
| ty::Dynamic(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)

View file

@ -27,26 +27,18 @@ mod tests;
/// the max/min element of the SCC, or all of the above.
///
/// Concretely, the both merge operations must commute, e.g. where `merge`
/// is `merge_scc` and `merge_reached`: `a.merge(b) == b.merge(a)`
/// is `update_scc` and `update_reached`: `a.merge(b) == b.merge(a)`
///
/// In general, what you want is probably always min/max according
/// to some ordering, potentially with side constraints (min x such
/// that P holds).
pub trait Annotation: Debug + Copy {
/// Merge two existing annotations into one during
/// path compression.o
fn merge_scc(self, other: Self) -> Self;
/// path compression.
fn update_scc(&mut self, other: &Self);
/// Merge a successor into this annotation.
fn merge_reached(self, other: Self) -> Self;
fn update_scc(&mut self, other: Self) {
*self = self.merge_scc(other)
}
fn update_reachable(&mut self, other: Self) {
*self = self.merge_reached(other)
}
fn update_reachable(&mut self, other: &Self);
}
/// An accumulator for annotations.
@ -70,12 +62,8 @@ impl<N: Idx, S: Idx + Ord> Annotations<N> for NoAnnotations<S> {
/// The empty annotation, which does nothing.
impl Annotation for () {
fn merge_reached(self, _other: Self) -> Self {
()
}
fn merge_scc(self, _other: Self) -> Self {
()
}
fn update_reachable(&mut self, _other: &Self) {}
fn update_scc(&mut self, _other: &Self) {}
}
/// Strongly connected components (SCC) of a graph. The type `N` is
@ -614,7 +602,7 @@ where
*min_depth = successor_min_depth;
*min_cycle_root = successor_node;
}
current_component_annotation.update_scc(successor_annotation);
current_component_annotation.update_scc(&successor_annotation);
}
// The starting node `node` is succeeded by a fully identified SCC
// which is now added to the set under `scc_index`.
@ -629,7 +617,7 @@ where
// the `successors_stack` for later.
trace!(?node, ?successor_scc_index);
successors_stack.push(successor_scc_index);
current_component_annotation.update_reachable(successor_annotation);
current_component_annotation.update_reachable(&successor_annotation);
}
// `node` has no more (direct) successors; search recursively.
None => {

View file

@ -32,12 +32,12 @@ impl Maxes {
}
impl Annotation for MaxReached {
fn merge_scc(self, other: Self) -> Self {
Self(std::cmp::max(other.0, self.0))
fn update_scc(&mut self, other: &Self) {
self.0 = self.0.max(other.0);
}
fn merge_reached(self, other: Self) -> Self {
Self(std::cmp::max(other.0, self.0))
fn update_reachable(&mut self, other: &Self) {
self.0 = self.0.max(other.0);
}
}
@ -75,13 +75,12 @@ impl Annotations<usize> for MinMaxes {
}
impl Annotation for MinMaxIn {
fn merge_scc(self, other: Self) -> Self {
Self { min: std::cmp::min(self.min, other.min), max: std::cmp::max(self.max, other.max) }
fn update_scc(&mut self, other: &Self) {
self.min = self.min.min(other.min);
self.max = self.max.max(other.max);
}
fn merge_reached(self, _other: Self) -> Self {
self
}
fn update_reachable(&mut self, _other: &Self) {}
}
#[test]

View file

@ -963,13 +963,16 @@ impl SyntaxExtension {
let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability);
// FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem
if let Some(sp) = find_attr!(attrs, AttributeKind::ConstStability { span, .. } => *span) {
if let Some(sp) =
find_attr!(attrs, AttributeKind::RustcConstStability { span, .. } => *span)
{
sess.dcx().emit_err(errors::MacroConstStability {
span: sp,
head_span: sess.source_map().guess_head_span(span),
});
}
if let Some(sp) = find_attr!(attrs, AttributeKind::BodyStability{ span, .. } => *span) {
if let Some(sp) = find_attr!(attrs, AttributeKind::RustcBodyStability{ span, .. } => *span)
{
sess.dcx().emit_err(errors::MacroBodyStability {
span: sp,
head_span: sess.source_map().guess_head_span(span),

View file

@ -819,7 +819,7 @@ pub fn compile_declarative_macro(
}
assert!(!kinds.is_empty());
let transparency = find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x)
let transparency = find_attr!(attrs, AttributeKind::RustcMacroTransparency(x) => *x)
.unwrap_or(Transparency::fallback(macro_rules));
if let Some(guar) = guar {

View file

@ -746,31 +746,15 @@ pub enum AttributeKind {
// FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
Align { align: Align, span: Span },
/// Represents `#[rustc_allow_const_fn_unstable]`.
AllowConstFnUnstable(ThinVec<Symbol>, Span),
/// Represents `#[rustc_allow_incoherent_impl]`.
AllowIncoherentImpl(Span),
/// Represents `#[allow_internal_unsafe]`.
AllowInternalUnsafe(Span),
/// Represents `#[allow_internal_unstable]`.
AllowInternalUnstable(ThinVec<(Symbol, Span)>, Span),
/// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint).
AsPtr(Span),
/// Represents `#[automatically_derived]`
AutomaticallyDerived(Span),
/// Represents `#[rustc_default_body_unstable]`.
BodyStability {
stability: DefaultBodyStability,
/// Span of the `#[rustc_default_body_unstable(...)]` attribute
span: Span,
},
/// Represents the trace attribute of `#[cfg_attr]`
CfgAttrTrace,
@ -780,9 +764,6 @@ pub enum AttributeKind {
/// Represents `#[cfi_encoding]`
CfiEncoding { encoding: Symbol },
/// Represents `#[rustc_coinductive]`.
Coinductive(Span),
/// Represents `#[cold]`.
Cold(Span),
@ -792,26 +773,9 @@ pub enum AttributeKind {
/// Represents `#[compiler_builtins]`.
CompilerBuiltins,
/// Represents `#[rustc_confusables]`.
Confusables {
symbols: ThinVec<Symbol>,
// FIXME(jdonszelmann): remove when target validation code is moved
first_span: Span,
},
/// Represents `#[const_continue]`.
ConstContinue(Span),
/// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`.
ConstStability {
stability: PartialConstStability,
/// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute
span: Span,
},
/// Represents `#[rustc_const_stable_indirect]`.
ConstStabilityIndirect,
/// Represents `#[coroutine]`.
Coroutine(Span),
@ -830,9 +794,6 @@ pub enum AttributeKind {
/// Represents `#[debugger_visualizer]`.
DebuggerVisualizer(ThinVec<DebugVisualizer>),
/// Represents `#[rustc_deny_explicit_impl]`.
DenyExplicitImpl(Span),
/// Represents [`#[deprecated]`](https://doc.rust-lang.org/stable/reference/attributes/diagnostics.html#the-deprecated-attribute).
Deprecation { deprecation: Deprecation, span: Span },
@ -848,12 +809,6 @@ pub enum AttributeKind {
/// i.e. doc comments.
DocComment { style: AttrStyle, kind: DocFragmentKind, span: Span, comment: Symbol },
/// Represents `#[rustc_dummy]`.
Dummy,
/// Represents `#[rustc_dyn_incompatible_trait]`.
DynIncompatibleTrait(Span),
/// Implementation detail of `#[eii]`
EiiDeclaration(EiiDecl),
@ -920,9 +875,6 @@ pub enum AttributeKind {
/// Represents [`#[macro_export]`](https://doc.rust-lang.org/reference/macros-by-example.html#r-macro.decl.scope.path).
MacroExport { span: Span, local_inner_macros: bool },
/// Represents `#[rustc_macro_transparency]`.
MacroTransparency(Transparency),
/// Represents `#[macro_use]`.
MacroUse { span: Span, arguments: MacroUseArgs },
@ -978,24 +930,12 @@ pub enum AttributeKind {
/// Represents `#[non_exhaustive]`
NonExhaustive(Span),
/// Represents `#[rustc_objc_class]`
ObjcClass { classname: Symbol, span: Span },
/// Represents `#[rustc_objc_selector]`
ObjcSelector { methname: Symbol, span: Span },
/// Represents `#[optimize(size|speed)]`
Optimize(OptimizeAttr, Span),
/// Represents `#[panic_runtime]`
PanicRuntime,
/// Represents `#[rustc_paren_sugar]`.
ParenSugar(Span),
/// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint).
PassByValue(Span),
/// Represents `#[patchable_function_entry]`
PatchableFunctionEntry { prefix: u8, entry: u8 },
@ -1023,9 +963,6 @@ pub enum AttributeKind {
/// Represents `#[profiler_runtime]`
ProfilerRuntime,
/// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
PubTransparent(Span),
/// Represents [`#[recursion_limit]`](https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute)
RecursionLimit { attr_span: Span, limit_span: Span, limit: Limit },
@ -1041,15 +978,55 @@ pub enum AttributeKind {
/// Represents `#[rustc_allocator_zeroed_variant]`
RustcAllocatorZeroedVariant { name: Symbol },
/// Represents `#[rustc_allow_const_fn_unstable]`.
RustcAllowConstFnUnstable(ThinVec<Symbol>, Span),
/// Represents `#[rustc_allow_incoherent_impl]`.
RustcAllowIncoherentImpl(Span),
/// Represents `#[rustc_as_ptr]` (used by the `dangling_pointers_from_temporaries` lint).
RustcAsPtr(Span),
/// Represents `#[rustc_default_body_unstable]`.
RustcBodyStability {
stability: DefaultBodyStability,
/// Span of the `#[rustc_default_body_unstable(...)]` attribute
span: Span,
},
/// Represents `#[rustc_builtin_macro]`.
RustcBuiltinMacro { builtin_name: Option<Symbol>, helper_attrs: ThinVec<Symbol>, span: Span },
/// Represents `#[rustc_coherence_is_core]`
RustcCoherenceIsCore(Span),
/// Represents `#[rustc_coinductive]`.
RustcCoinductive(Span),
/// Represents `#[rustc_confusables]`.
RustcConfusables {
symbols: ThinVec<Symbol>,
// FIXME(jdonszelmann): remove when target validation code is moved
first_span: Span,
},
/// Represents `#[rustc_const_stable]` and `#[rustc_const_unstable]`.
RustcConstStability {
stability: PartialConstStability,
/// Span of the `#[rustc_const_stable(...)]` or `#[rustc_const_unstable(...)]` attribute
span: Span,
},
/// Represents `#[rustc_const_stable_indirect]`.
RustcConstStabilityIndirect,
/// Represents `#[rustc_deallocator]`
RustcDeallocator,
/// Represents `#[rustc_deny_explicit_impl]`.
RustcDenyExplicitImpl(Span),
/// Represents `#[rustc_dummy]`.
RustcDummy,
/// Represents `#[rustc_dump_def_parents]`
RustcDumpDefParents,
@ -1065,6 +1042,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_dump_vtable]`
RustcDumpVtable(Span),
/// Represents `#[rustc_dyn_incompatible_trait]`.
RustcDynIncompatibleTrait(Span),
/// Represents `#[rustc_has_incoherent_inherent_impls]`
RustcHasIncoherentInherentImpls,
@ -1089,6 +1069,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_lint_untracked_query_information]`
RustcLintUntrackedQueryInformation,
/// Represents `#[rustc_macro_transparency]`.
RustcMacroTransparency(Transparency),
/// Represents `#[rustc_main]`.
RustcMain,
@ -1104,15 +1087,30 @@ pub enum AttributeKind {
/// Represents `#[rustc_nounwind]`
RustcNounwind,
/// Represents `#[rustc_objc_class]`
RustcObjcClass { classname: Symbol, span: Span },
/// Represents `#[rustc_objc_selector]`
RustcObjcSelector { methname: Symbol, span: Span },
/// Represents `#[rustc_object_lifetime_default]`.
RustcObjectLifetimeDefault,
/// Represents `#[rustc_offload_kernel]`
RustcOffloadKernel,
/// Represents `#[rustc_paren_sugar]`.
RustcParenSugar(Span),
/// Represents `#[rustc_pass_by_value]` (used by the `rustc_pass_by_value` lint).
RustcPassByValue(Span),
/// Represents `#[rustc_pass_indirectly_in_non_rustic_abis]`
RustcPassIndirectlyInNonRusticAbis(Span),
/// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint).
RustcPubTransparent(Span),
/// Represents `#[rustc_reallocator]`
RustcReallocator,
@ -1130,6 +1128,18 @@ pub enum AttributeKind {
/// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`.
RustcSimdMonomorphizeLaneLimit(Limit),
/// Represents `#[rustc_skip_during_method_dispatch]`.
RustcSkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },
/// Represents `#[rustc_specialization_trait]`.
RustcSpecializationTrait(Span),
/// Represents `#[rustc_std_internal_symbol]`.
RustcStdInternalSymbol(Span),
/// Represents `#[rustc_unsafe_specialization_marker]`.
RustcUnsafeSpecializationMarker(Span),
/// Represents `#[rustc_variance]`
RustcVariance,
@ -1151,22 +1161,12 @@ pub enum AttributeKind {
/// Represents `#[should_panic]`
ShouldPanic { reason: Option<Symbol>, span: Span },
/// Represents `#[rustc_skip_during_method_dispatch]`.
SkipDuringMethodDispatch { array: bool, boxed_slice: bool, span: Span },
/// Represents `#[rustc_specialization_trait]`.
SpecializationTrait(Span),
/// Represents `#[stable]`, `#[unstable]` and `#[rustc_allowed_through_unstable_modules]`.
Stability {
stability: Stability,
/// Span of the attribute.
span: Span,
},
/// Represents `#[rustc_std_internal_symbol]`.
StdInternalSymbol(Span),
/// Represents `#[target_feature(enable = "...")]` and
/// `#[unsafe(force_target_feature(enable = "...")]`.
TargetFeature { features: ThinVec<(Symbol, Span)>, attr_span: Span, was_forced: bool },
@ -1183,9 +1183,6 @@ pub enum AttributeKind {
/// Represents `#[type_length_limit]`
TypeLengthLimit { attr_span: Span, limit_span: Span, limit: Limit },
/// Represents `#[rustc_unsafe_specialization_marker]`.
UnsafeSpecializationMarker(Span),
/// Represents `#[unstable_feature_bound]`.
UnstableFeatureBound(ThinVec<(Symbol, Span)>),

View file

@ -19,37 +19,26 @@ impl AttributeKind {
match self {
// tidy-alphabetical-start
Align { .. } => No,
AllowConstFnUnstable(..) => No,
AllowIncoherentImpl(..) => No,
AllowInternalUnsafe(..) => Yes,
AllowInternalUnstable(..) => Yes,
AsPtr(..) => Yes,
AutomaticallyDerived(..) => Yes,
BodyStability { .. } => No,
CfgAttrTrace => Yes,
CfgTrace(..) => Yes,
CfiEncoding { .. } => Yes,
Coinductive(..) => No,
Cold(..) => No,
CollapseDebugInfo(..) => Yes,
CompilerBuiltins => No,
Confusables { .. } => Yes,
ConstContinue(..) => No,
ConstStability { .. } => Yes,
ConstStabilityIndirect => No,
Coroutine(..) => No,
Coverage(..) => No,
CrateName { .. } => No,
CrateType(_) => No,
CustomMir(_, _, _) => Yes,
DebuggerVisualizer(..) => No,
DenyExplicitImpl(..) => No,
Deprecation { .. } => Yes,
DoNotRecommend { .. } => Yes,
Doc(_) => Yes,
DocComment { .. } => Yes,
Dummy => No,
DynIncompatibleTrait(..) => No,
EiiDeclaration(_) => Yes,
EiiForeignItem => No,
EiiImpls(..) => No,
@ -69,7 +58,6 @@ impl AttributeKind {
LoopMatch(..) => No,
MacroEscape(..) => No,
MacroExport { .. } => Yes,
MacroTransparency(..) => Yes,
MacroUse { .. } => No,
Marker(..) => No,
MayDangle(..) => No,
@ -87,12 +75,8 @@ impl AttributeKind {
NoMangle(..) => Yes, // Needed for rustdoc
NoStd(..) => No,
NonExhaustive(..) => Yes, // Needed for rustdoc
ObjcClass { .. } => No,
ObjcSelector { .. } => No,
Optimize(..) => No,
PanicRuntime => No,
ParenSugar(..) => No,
PassByValue(..) => Yes,
PatchableFunctionEntry { .. } => Yes,
Path(..) => No,
PatternComplexityLimit { .. } => No,
@ -102,20 +86,30 @@ impl AttributeKind {
ProcMacroAttribute(..) => No,
ProcMacroDerive { .. } => No,
ProfilerRuntime => No,
PubTransparent(..) => Yes,
RecursionLimit { .. } => No,
Repr { .. } => No,
RustcAllocator => No,
RustcAllocatorZeroed => No,
RustcAllocatorZeroedVariant { .. } => Yes,
RustcAllowConstFnUnstable(..) => No,
RustcAllowIncoherentImpl(..) => No,
RustcAsPtr(..) => Yes,
RustcBodyStability { .. } => No,
RustcBuiltinMacro { .. } => Yes,
RustcCoherenceIsCore(..) => No,
RustcCoinductive(..) => No,
RustcConfusables { .. } => Yes,
RustcConstStability { .. } => Yes,
RustcConstStabilityIndirect => No,
RustcDeallocator => No,
RustcDenyExplicitImpl(..) => No,
RustcDummy => No,
RustcDumpDefParents => No,
RustcDumpItemBounds => No,
RustcDumpPredicates => No,
RustcDumpUserArgs => No,
RustcDumpVtable(..) => No,
RustcDynIncompatibleTrait(..) => No,
RustcHasIncoherentInherentImpls => Yes,
RustcLayoutScalarValidRangeEnd(..) => Yes,
RustcLayoutScalarValidRangeStart(..) => Yes,
@ -124,32 +118,38 @@ impl AttributeKind {
RustcLintOptTy => Yes,
RustcLintQueryInstability => Yes,
RustcLintUntrackedQueryInformation => Yes,
RustcMacroTransparency(..) => Yes,
RustcMain => No,
RustcMustImplementOneOf { .. } => No,
RustcNeverReturnsNullPointer => Yes,
RustcNoImplicitAutorefs => Yes,
RustcNounwind => No,
RustcObjcClass { .. } => No,
RustcObjcSelector { .. } => No,
RustcObjectLifetimeDefault => No,
RustcOffloadKernel => Yes,
RustcParenSugar(..) => No,
RustcPassByValue(..) => Yes,
RustcPassIndirectlyInNonRusticAbis(..) => No,
RustcPubTransparent(..) => Yes,
RustcReallocator => No,
RustcScalableVector { .. } => Yes,
RustcShouldNotBeCalledOnConstItems(..) => Yes,
RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate
RustcSkipDuringMethodDispatch { .. } => No,
RustcSpecializationTrait(..) => No,
RustcStdInternalSymbol(..) => No,
RustcUnsafeSpecializationMarker(..) => No,
RustcVariance => No,
RustcVarianceOfOpaques => No,
Sanitize { .. } => No,
ShouldPanic { .. } => No,
SkipDuringMethodDispatch { .. } => No,
SpecializationTrait(..) => No,
Stability { .. } => Yes,
StdInternalSymbol(..) => No,
TargetFeature { .. } => No,
ThreadLocal => No,
TrackCaller(..) => Yes,
TypeConst(..) => Yes,
TypeLengthLimit { .. } => No,
UnsafeSpecializationMarker(..) => No,
UnstableFeatureBound(..) => No,
Used { .. } => No,
WindowsSubsystem(..) => No,

View file

@ -224,6 +224,10 @@ hir_analysis_impl_not_marked_default = `{$ident}` specializes an item from a par
hir_analysis_impl_not_marked_default_err = `{$ident}` specializes an item from a parent `impl`, but that item is not marked `default`
.note = parent implementation is in crate `{$cname}`
hir_analysis_impl_unpin_for_pin_projected_type = explicit impls for the `Unpin` trait are not permitted for structurally pinned types
.label = impl of `Unpin` not allowed
.help = `{$adt_name}` is structurally pinned because it is marked as `#[pin_v2]`
hir_analysis_inherent_dyn = cannot define inherent `impl` for a dyn auto trait
.label = impl requires at least one non-auto trait
.note = define and implement a new trait or type instead

View file

@ -1701,7 +1701,10 @@ pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
ty::Array(ty, _) => check_unsuited(tcx, typing_env, *ty),
ty::Adt(def, args) => {
if !def.did().is_local()
&& !find_attr!(tcx.get_all_attrs(def.did()), AttributeKind::PubTransparent(_))
&& !find_attr!(
tcx.get_all_attrs(def.did()),
AttributeKind::RustcPubTransparent(_)
)
{
let non_exhaustive = def.is_variant_list_non_exhaustive()
|| def.variants().iter().any(ty::VariantDef::is_field_list_non_exhaustive);

View file

@ -592,7 +592,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
ty,
Ty::new_placeholder(
tcx,
ty::Placeholder::new(
ty::PlaceholderType::new(
universe,
ty::BoundTy { var: idx, kind: ty::BoundTyKind::Anon },
),
@ -2551,7 +2551,7 @@ fn param_env_with_gat_bounds<'tcx>(
}
};
let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind<'tcx>; 8]> =
smallvec::SmallVec::with_capacity(tcx.generics_of(impl_ty.def_id).own_params.len());
// Extend the impl's identity args with late-bound GAT vars
let normalize_impl_ty_args = ty::GenericArgs::identity_for_item(tcx, container_id)
@ -2587,7 +2587,7 @@ fn param_env_with_gat_bounds<'tcx>(
ty::Const::new_bound(
tcx,
ty::INNERMOST,
ty::BoundConst { var: ty::BoundVar::from_usize(bound_vars.len() - 1) },
ty::BoundConst::new(ty::BoundVar::from_usize(bound_vars.len() - 1)),
)
.into()
}

View file

@ -38,6 +38,7 @@ pub(super) fn check_trait<'tcx>(
checker.check(lang_items.drop_trait(), visit_implementation_of_drop)?;
checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?;
checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?;
checker.check(lang_items.unpin_trait(), visit_implementation_of_unpin)?;
checker.check(lang_items.const_param_ty_trait(), |checker| {
visit_implementation_of_const_param_ty(checker)
})?;
@ -134,6 +135,41 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran
}
}
fn visit_implementation_of_unpin(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
let tcx = checker.tcx;
let impl_header = checker.impl_header;
let impl_did = checker.impl_def_id;
debug!("visit_implementation_of_unpin: impl_did={:?}", impl_did);
let self_type = impl_header.trait_ref.instantiate_identity().self_ty();
debug!("visit_implementation_of_unpin: self_type={:?}", self_type);
let span = tcx.def_span(impl_did);
if tcx.features().pin_ergonomics() {
match self_type.kind() {
// Soundness concerns: a type `T` annotated with `#[pin_v2]` is allowed to project
// `Pin<&mut T>` to its field `Pin<&mut U>` safely (even if `U: !Unpin`).
// If `T` is allowed to impl `Unpin` manually (note that `Unpin` is a safe trait,
// which cannot carry safety properties), then `&mut U` could be obtained from
// `&mut T` that dereferenced by `Pin<&mut T>`, which breaks the safety contract of
// `Pin<&mut U>` for `U: !Unpin`.
ty::Adt(adt, _) if adt.is_pin_project() => {
return Err(tcx.dcx().emit_err(crate::errors::ImplUnpinForPinProjectedType {
span,
adt_span: tcx.def_span(adt.did()),
adt_name: tcx.item_name(adt.did()),
}));
}
ty::Adt(_, _) => {}
_ => {
return Err(tcx.dcx().span_delayed_bug(span, "impl of `Unpin` for a non-adt type"));
}
};
}
Ok(())
}
fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
let tcx = checker.tcx;
let header = checker.impl_header;

View file

@ -91,7 +91,7 @@ impl<'tcx> InherentCollect<'tcx> {
for &impl_item in items {
if !find_attr!(
self.tcx.get_all_attrs(impl_item),
AttributeKind::AllowIncoherentImpl(_)
AttributeKind::RustcAllowIncoherentImpl(_)
) {
let impl_span = self.tcx.def_span(impl_def_id);
return Err(self.tcx.dcx().emit_err(errors::InherentTyOutsideRelevant {
@ -125,7 +125,7 @@ impl<'tcx> InherentCollect<'tcx> {
for &impl_item in items {
if !find_attr!(
self.tcx.get_all_attrs(impl_item),
AttributeKind::AllowIncoherentImpl(_)
AttributeKind::RustcAllowIncoherentImpl(_)
) {
let span = self.tcx.def_span(impl_def_id);
return Err(self.tcx.dcx().emit_err(errors::InherentTyOutsidePrimitive {

View file

@ -889,7 +889,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
let attrs = tcx.get_all_attrs(def_id);
let paren_sugar = find_attr!(attrs, AttributeKind::ParenSugar(_));
let paren_sugar = find_attr!(attrs, AttributeKind::RustcParenSugar(_));
if paren_sugar && !tcx.features().unboxed_closures() {
tcx.dcx().emit_err(errors::ParenSugarAttribute { span: item.span });
}
@ -897,22 +897,23 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
// Only regular traits can be marker.
let is_marker = !is_alias && find_attr!(attrs, AttributeKind::Marker(_));
let rustc_coinductive = find_attr!(attrs, AttributeKind::Coinductive(_));
let rustc_coinductive = find_attr!(attrs, AttributeKind::RustcCoinductive(_));
let is_fundamental = find_attr!(attrs, AttributeKind::Fundamental);
let [skip_array_during_method_dispatch, skip_boxed_slice_during_method_dispatch] = find_attr!(
attrs,
AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span: _ } => [*array, *boxed_slice]
AttributeKind::RustcSkipDuringMethodDispatch { array, boxed_slice, span: _ } => [*array, *boxed_slice]
)
.unwrap_or([false; 2]);
let specialization_kind = if find_attr!(attrs, AttributeKind::UnsafeSpecializationMarker(_)) {
ty::trait_def::TraitSpecializationKind::Marker
} else if find_attr!(attrs, AttributeKind::SpecializationTrait(_)) {
ty::trait_def::TraitSpecializationKind::AlwaysApplicable
} else {
ty::trait_def::TraitSpecializationKind::None
};
let specialization_kind =
if find_attr!(attrs, AttributeKind::RustcUnsafeSpecializationMarker(_)) {
ty::trait_def::TraitSpecializationKind::Marker
} else if find_attr!(attrs, AttributeKind::RustcSpecializationTrait(_)) {
ty::trait_def::TraitSpecializationKind::AlwaysApplicable
} else {
ty::trait_def::TraitSpecializationKind::None
};
let must_implement_one_of = find_attr!(
attrs,
@ -923,9 +924,9 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
.collect::<Box<[_]>>()
);
let deny_explicit_impl = find_attr!(attrs, AttributeKind::DenyExplicitImpl(_));
let deny_explicit_impl = find_attr!(attrs, AttributeKind::RustcDenyExplicitImpl(_));
let force_dyn_incompatible =
find_attr!(attrs, AttributeKind::DynIncompatibleTrait(span) => *span);
find_attr!(attrs, AttributeKind::RustcDynIncompatibleTrait(span) => *span);
ty::TraitDef {
def_id: def_id.to_def_id(),

View file

@ -241,7 +241,7 @@ struct MapAndCompressBoundVars<'tcx> {
binder: ty::DebruijnIndex,
/// List of bound vars that remain unsubstituted because they were not
/// mentioned in the GAT's args.
still_bound_vars: Vec<ty::BoundVariableKind>,
still_bound_vars: Vec<ty::BoundVariableKind<'tcx>>,
/// Subtle invariant: If the `GenericArg` is bound, then it should be
/// stored with the debruijn index of `INNERMOST` so it can be shifted
/// correctly during substitution.
@ -330,7 +330,8 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for MapAndCompressBoundVars<'tcx> {
} else {
let var = ty::BoundVar::from_usize(self.still_bound_vars.len());
self.still_bound_vars.push(ty::BoundVariableKind::Const);
let mapped = ty::Const::new_bound(self.tcx, ty::INNERMOST, ty::BoundConst { var });
let mapped =
ty::Const::new_bound(self.tcx, ty::INNERMOST, ty::BoundConst::new(var));
self.mapping.insert(old_bound.var, mapped.into());
mapped
};

View file

@ -63,9 +63,9 @@ impl ResolvedArg {
struct BoundVarContext<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
rbv: &'a mut ResolveBoundVars,
rbv: &'a mut ResolveBoundVars<'tcx>,
disambiguator: &'a mut DisambiguatorState,
scope: ScopeRef<'a>,
scope: ScopeRef<'a, 'tcx>,
opaque_capture_errors: RefCell<Option<OpaqueHigherRankedLifetimeCaptureErrors>>,
}
@ -76,7 +76,7 @@ struct OpaqueHigherRankedLifetimeCaptureErrors {
}
#[derive(Debug)]
enum Scope<'a> {
enum Scope<'a, 'tcx> {
/// Declares lifetimes, and each can be early-bound or late-bound.
/// The `DebruijnIndex` of late-bound lifetimes starts at `1` and
/// it should be shifted by the number of `Binder`s in between the
@ -94,7 +94,7 @@ enum Scope<'a> {
/// to append to.
hir_id: HirId,
s: ScopeRef<'a>,
s: ScopeRef<'a, 'tcx>,
/// If this binder comes from a where clause, specify how it was created.
/// This is used to diagnose inaccessible lifetimes in APIT:
@ -110,7 +110,7 @@ enum Scope<'a> {
/// e.g., `(&T, fn(&T) -> &T);` becomes `(&'_ T, for<'a> fn(&'a T) -> &'a T)`.
Body {
id: hir::BodyId,
s: ScopeRef<'a>,
s: ScopeRef<'a, 'tcx>,
},
/// Use a specific lifetime (if `Some`) or leave it unset (to be
@ -118,7 +118,7 @@ enum Scope<'a> {
/// for the default choice of lifetime in a trait object type.
ObjectLifetimeDefault {
lifetime: Option<ResolvedArg>,
s: ScopeRef<'a>,
s: ScopeRef<'a, 'tcx>,
},
/// When we have nested trait refs, we concatenate late bound vars for inner
@ -126,12 +126,12 @@ enum Scope<'a> {
/// lifetimes encountered when identifying the trait that an associated type
/// is declared on.
Supertrait {
bound_vars: Vec<ty::BoundVariableKind>,
s: ScopeRef<'a>,
bound_vars: Vec<ty::BoundVariableKind<'tcx>>,
s: ScopeRef<'a, 'tcx>,
},
TraitRefBoundary {
s: ScopeRef<'a>,
s: ScopeRef<'a, 'tcx>,
},
/// Remap lifetimes that appear in opaque types to fresh lifetime parameters. Given:
@ -148,7 +148,7 @@ enum Scope<'a> {
/// Mapping from each captured lifetime `'a` to the duplicate generic parameter `'b`.
captures: &'a RefCell<FxIndexMap<ResolvedArg, LocalDefId>>,
s: ScopeRef<'a>,
s: ScopeRef<'a, 'tcx>,
},
/// Disallows capturing late-bound vars from parent scopes.
@ -157,7 +157,7 @@ enum Scope<'a> {
/// since we don't do something more correct like replacing any captured
/// late-bound vars with early-bound params in the const's own generics.
LateBoundary {
s: ScopeRef<'a>,
s: ScopeRef<'a, 'tcx>,
what: &'static str,
deny_late_regions: bool,
},
@ -167,7 +167,7 @@ enum Scope<'a> {
},
}
impl<'a> Scope<'a> {
impl<'a, 'tcx> Scope<'a, 'tcx> {
// A helper for debugging scopes without printing parent scopes
fn debug_truncated(&self) -> impl fmt::Debug {
fmt::from_fn(move |f| match self {
@ -227,7 +227,7 @@ enum BinderScopeType {
Concatenating,
}
type ScopeRef<'a> = &'a Scope<'a>;
type ScopeRef<'a, 'tcx> = &'a Scope<'a, 'tcx>;
/// Adds query implementations to the [Providers] vtable, see [`rustc_middle::query`]
pub(crate) fn provide(providers: &mut Providers) {
@ -253,7 +253,7 @@ pub(crate) fn provide(providers: &mut Providers) {
/// You should not read the result of this query directly, but rather use
/// `named_variable_map`, `late_bound_vars_map`, etc.
#[instrument(level = "debug", skip(tcx))]
fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBoundVars {
fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBoundVars<'_> {
let mut rbv = ResolveBoundVars::default();
let mut visitor = BoundVarContext {
tcx,
@ -287,7 +287,7 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou
rbv
}
fn late_arg_as_bound_arg<'tcx>(param: &GenericParam<'tcx>) -> ty::BoundVariableKind {
fn late_arg_as_bound_arg<'tcx>(param: &GenericParam<'tcx>) -> ty::BoundVariableKind<'tcx> {
let def_id = param.def_id.to_def_id();
match param.kind {
GenericParamKind::Lifetime { .. } => {
@ -301,7 +301,9 @@ fn late_arg_as_bound_arg<'tcx>(param: &GenericParam<'tcx>) -> ty::BoundVariableK
/// Turn a [`ty::GenericParamDef`] into a bound arg. Generally, this should only
/// be used when turning early-bound vars into late-bound vars when lowering
/// return type notation.
fn generic_param_def_as_bound_arg(param: &ty::GenericParamDef) -> ty::BoundVariableKind {
fn generic_param_def_as_bound_arg<'tcx>(
param: &ty::GenericParamDef,
) -> ty::BoundVariableKind<'tcx> {
match param.kind {
ty::GenericParamDefKind::Lifetime => {
ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(param.def_id))
@ -329,7 +331,9 @@ fn opaque_captures_all_in_scope_lifetimes<'tcx>(opaque: &'tcx hir::OpaqueTy<'tcx
impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
/// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref.
fn poly_trait_ref_binder_info(&mut self) -> (Vec<ty::BoundVariableKind>, BinderScopeType) {
fn poly_trait_ref_binder_info(
&mut self,
) -> (Vec<ty::BoundVariableKind<'tcx>>, BinderScopeType) {
let mut scope = self.scope;
let mut supertrait_bound_vars = vec![];
loop {
@ -364,7 +368,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
Scope::Binder { hir_id, .. } => {
// Nested poly trait refs have the binders concatenated
let mut full_binders =
let mut full_binders: Vec<ty::BoundVariableKind<'tcx>> =
self.rbv.late_bound_vars.get_mut_or_insert_default(hir_id.local_id).clone();
full_binders.extend(supertrait_bound_vars);
break (full_binders, BinderScopeType::Concatenating);
@ -1094,7 +1098,7 @@ fn object_lifetime_default(tcx: TyCtxt<'_>, param_def_id: LocalDefId) -> ObjectL
}
impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
fn with<F>(&mut self, wrap_scope: Scope<'_>, f: F)
fn with<F>(&mut self, wrap_scope: Scope<'_, 'tcx>, f: F)
where
F: for<'b> FnOnce(&mut BoundVarContext<'b, 'tcx>),
{
@ -1115,7 +1119,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
*self.opaque_capture_errors.borrow_mut() = this.opaque_capture_errors.into_inner();
}
fn record_late_bound_vars(&mut self, hir_id: HirId, binder: Vec<ty::BoundVariableKind>) {
fn record_late_bound_vars(&mut self, hir_id: HirId, binder: Vec<ty::BoundVariableKind<'tcx>>) {
if let Some(old) = self.rbv.late_bound_vars.insert(hir_id.local_id, binder) {
bug!(
"overwrote bound vars for {hir_id:?}:\nold={old:?}\nnew={:?}",
@ -1931,7 +1935,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
def_id: DefId,
assoc_ident: Ident,
assoc_tag: ty::AssocTag,
) -> Option<(Vec<ty::BoundVariableKind>, &'tcx ty::AssocItem)> {
) -> Option<(Vec<ty::BoundVariableKind<'tcx>>, &'tcx ty::AssocItem)> {
let trait_defines_associated_item_named = |trait_def_id: DefId| {
tcx.associated_items(trait_def_id).find_by_ident_and_kind(
tcx,
@ -1942,7 +1946,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
};
use smallvec::{SmallVec, smallvec};
let mut stack: SmallVec<[(DefId, SmallVec<[ty::BoundVariableKind; 8]>); 8]> =
let mut stack: SmallVec<[(DefId, SmallVec<[ty::BoundVariableKind<'tcx>; 8]>); 8]> =
smallvec![(def_id, smallvec![])];
let mut visited: FxHashSet<DefId> = FxHashSet::default();
loop {

View file

@ -1690,3 +1690,14 @@ pub(crate) struct EiiWithGenerics {
pub eii_name: Symbol,
pub impl_name: Symbol,
}
#[derive(Diagnostic)]
#[diag(hir_analysis_impl_unpin_for_pin_projected_type)]
pub(crate) struct ImplUnpinForPinProjectedType {
#[primary_span]
#[label]
pub span: Span,
#[help]
pub adt_span: Span,
pub adt_name: Symbol,
}

View file

@ -362,7 +362,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
param_ty: Ty<'tcx>,
hir_bounds: I,
bounds: &mut Vec<(ty::Clause<'tcx>, Span)>,
bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
bound_vars: &'tcx ty::List<ty::BoundVariableKind<'tcx>>,
predicate_filter: PredicateFilter,
overlapping_assoc_constraints: OverlappingAsssocItemConstraints,
) where
@ -1000,7 +1000,9 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 't
.delayed_bug(format!("unexpected bound region kind: {:?}", br.kind));
return ControlFlow::Break(guar);
}
ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
ty::BoundRegionKind::NamedForPrinting(_) => {
bug!("only used for pretty printing")
}
});
}
_ => {}

View file

@ -2310,7 +2310,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
Some(rbv::ResolvedArg::LateBound(debruijn, index, _)) => ty::Const::new_bound(
tcx,
debruijn,
ty::BoundConst { var: ty::BoundVar::from_u32(index) },
ty::BoundConst::new(ty::BoundVar::from_u32(index)),
),
Some(rbv::ResolvedArg::Error(guar)) => ty::Const::new_error(tcx, guar),
arg => bug!("unexpected bound var resolution for {:?}: {arg:?}", path_hir_id),
@ -3196,8 +3196,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
#[instrument(level = "trace", skip(self, generate_err))]
fn validate_late_bound_regions<'cx>(
&'cx self,
constrained_regions: FxIndexSet<ty::BoundRegionKind>,
referenced_regions: FxIndexSet<ty::BoundRegionKind>,
constrained_regions: FxIndexSet<ty::BoundRegionKind<'tcx>>,
referenced_regions: FxIndexSet<ty::BoundRegionKind<'tcx>>,
generate_err: impl Fn(&str) -> Diag<'cx>,
) {
for br in referenced_regions.difference(&constrained_regions) {

View file

@ -13,7 +13,9 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_data_structures::sorted_map::SortedMap;
use rustc_data_structures::unord::UnordSet;
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, MultiSpan, StashKey, pluralize, struct_span_code_err};
use rustc_errors::{
Applicability, Diag, MultiSpan, StashKey, listify, pluralize, struct_span_code_err,
};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
@ -50,6 +52,51 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
use crate::method::probe::UnsatisfiedPredicates;
use crate::{Expectation, FnCtxt};
/// Tracks trait bounds and detects duplicates between ref and non-ref versions of self types.
/// This is used to condense error messages when the same trait bound appears for both
/// `T` and `&T` (or `&mut T`).
struct TraitBoundDuplicateTracker {
trait_def_ids: FxIndexSet<DefId>,
seen_ref: FxIndexSet<DefId>,
seen_non_ref: FxIndexSet<DefId>,
has_ref_dupes: bool,
}
impl TraitBoundDuplicateTracker {
fn new() -> Self {
Self {
trait_def_ids: FxIndexSet::default(),
seen_ref: FxIndexSet::default(),
seen_non_ref: FxIndexSet::default(),
has_ref_dupes: false,
}
}
/// Track a trait bound. `is_ref` indicates whether the self type is a reference.
fn track(&mut self, def_id: DefId, is_ref: bool) {
self.trait_def_ids.insert(def_id);
if is_ref {
if self.seen_non_ref.contains(&def_id) {
self.has_ref_dupes = true;
}
self.seen_ref.insert(def_id);
} else {
if self.seen_ref.contains(&def_id) {
self.has_ref_dupes = true;
}
self.seen_non_ref.insert(def_id);
}
}
fn has_ref_dupes(&self) -> bool {
self.has_ref_dupes
}
fn into_trait_def_ids(self) -> FxIndexSet<DefId> {
self.trait_def_ids
}
}
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
self.autoderef(span, ty)
@ -1004,6 +1051,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
item_ident: Ident,
item_kind: &str,
bound_spans: SortedMap<Span, Vec<String>>,
unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>,
) {
let mut ty_span = match rcvr_ty.kind() {
ty::Param(param_type) => {
@ -1012,13 +1060,61 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
ty::Adt(def, _) if def.did().is_local() => Some(self.tcx.def_span(def.did())),
_ => None,
};
let rcvr_ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path());
let mut tracker = TraitBoundDuplicateTracker::new();
for (predicate, _parent_pred, _cause) in unsatisfied_predicates {
if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
predicate.kind().skip_binder()
&& let self_ty = pred.trait_ref.self_ty()
&& self_ty.peel_refs() == rcvr_ty
{
let is_ref = matches!(self_ty.kind(), ty::Ref(..));
tracker.track(pred.trait_ref.def_id, is_ref);
}
}
let has_ref_dupes = tracker.has_ref_dupes();
let mut missing_trait_names = tracker
.into_trait_def_ids()
.into_iter()
.map(|def_id| format!("`{}`", self.tcx.def_path_str(def_id)))
.collect::<Vec<_>>();
missing_trait_names.sort();
let should_condense =
has_ref_dupes && missing_trait_names.len() > 1 && matches!(rcvr_ty.kind(), ty::Adt(..));
let missing_trait_list = if should_condense {
Some(match missing_trait_names.as_slice() {
[only] => only.clone(),
[first, second] => format!("{first} or {second}"),
[rest @ .., last] => format!("{} or {last}", rest.join(", ")),
[] => String::new(),
})
} else {
None
};
for (span, mut bounds) in bound_spans {
if !self.tcx.sess.source_map().is_span_accessible(span) {
continue;
}
bounds.sort();
bounds.dedup();
let pre = if Some(span) == ty_span {
let is_ty_span = Some(span) == ty_span;
if is_ty_span && should_condense {
ty_span.take();
let label = if let Some(missing_trait_list) = &missing_trait_list {
format!(
"{item_kind} `{item_ident}` not found for this {} because `{rcvr_ty_str}` doesn't implement {missing_trait_list}",
rcvr_ty.prefix_string(self.tcx)
)
} else {
format!(
"{item_kind} `{item_ident}` not found for this {}",
rcvr_ty.prefix_string(self.tcx)
)
};
err.span_label(span, label);
continue;
}
let pre = if is_ty_span {
ty_span.take();
format!(
"{item_kind} `{item_ident}` not found for this {} because it ",
@ -1248,6 +1344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
item_ident,
item_kind,
bound_spans,
unsatisfied_predicates,
);
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_ident, expected);
@ -1507,6 +1604,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
bound_spans: &mut SortedMap<Span, Vec<String>>,
) {
let tcx = self.tcx;
let rcvr_ty_str = self.tcx.short_string(rcvr_ty, err.long_ty_path());
let mut type_params = FxIndexMap::default();
// Pick out the list of unimplemented traits on the receiver.
@ -1798,6 +1896,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect();
spanned_predicates.sort_by_key(|(span, _)| *span);
for (_, (primary_spans, span_labels, predicates)) in spanned_predicates {
let mut tracker = TraitBoundDuplicateTracker::new();
let mut all_trait_bounds_for_rcvr = true;
for pred in &predicates {
match pred.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
let self_ty = pred.trait_ref.self_ty();
if self_ty.peel_refs() != rcvr_ty {
all_trait_bounds_for_rcvr = false;
break;
}
let is_ref = matches!(self_ty.kind(), ty::Ref(..));
tracker.track(pred.trait_ref.def_id, is_ref);
}
_ => {
all_trait_bounds_for_rcvr = false;
break;
}
}
}
let has_ref_dupes = tracker.has_ref_dupes();
let trait_def_ids = tracker.into_trait_def_ids();
let mut preds: Vec<_> = predicates
.iter()
.filter_map(|pred| format_pred(**pred))
@ -1805,7 +1924,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.collect();
preds.sort();
preds.dedup();
let msg = if let [pred] = &preds[..] {
let availability_note = if all_trait_bounds_for_rcvr
&& has_ref_dupes
&& trait_def_ids.len() > 1
&& matches!(rcvr_ty.kind(), ty::Adt(..))
{
let mut trait_names = trait_def_ids
.into_iter()
.map(|def_id| format!("`{}`", tcx.def_path_str(def_id)))
.collect::<Vec<_>>();
trait_names.sort();
listify(&trait_names, |name| name.to_string()).map(|traits| {
format!(
"for `{item_ident}` to be available, `{rcvr_ty_str}` must implement {traits}"
)
})
} else {
None
};
let msg = if let Some(availability_note) = availability_note {
availability_note
} else if let [pred] = &preds[..] {
format!("trait bound {pred} was not satisfied")
} else {
format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),)
@ -2103,7 +2242,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
for inherent_method in
self.tcx.associated_items(inherent_impl_did).in_definition_order()
{
if let Some(candidates) = find_attr!(self.tcx.get_all_attrs(inherent_method.def_id), AttributeKind::Confusables{symbols, ..} => symbols)
if let Some(candidates) = find_attr!(self.tcx.get_all_attrs(inherent_method.def_id), AttributeKind::RustcConfusables{symbols, ..} => symbols)
&& candidates.contains(&item_name.name)
&& inherent_method.is_fn()
{
@ -3281,6 +3420,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
/// Checks if we can suggest a derive macro for the unmet trait bound.
/// Returns Some(list_of_derives) if possible, or None if not.
fn consider_suggesting_derives_for_ty(
&self,
trait_pred: ty::TraitPredicate<'tcx>,
adt: ty::AdtDef<'tcx>,
) -> Option<Vec<(String, Span, Symbol)>> {
let diagnostic_name = self.tcx.get_diagnostic_name(trait_pred.def_id())?;
let can_derive = match diagnostic_name {
sym::Default
| sym::Eq
| sym::PartialEq
| sym::Ord
| sym::PartialOrd
| sym::Clone
| sym::Copy
| sym::Hash
| sym::Debug => true,
_ => false,
};
if !can_derive {
return None;
}
let trait_def_id = trait_pred.def_id();
let self_ty = trait_pred.self_ty();
// We need to check if there is already a manual implementation of the trait
// for this specific ADT to avoid suggesting `#[derive(..)]` that would conflict.
if self.tcx.non_blanket_impls_for_ty(trait_def_id, self_ty).any(|impl_def_id| {
self.tcx
.type_of(impl_def_id)
.instantiate_identity()
.ty_adt_def()
.is_some_and(|def| def.did() == adt.did())
}) {
return None;
}
let mut derives = Vec::new();
let self_name = self_ty.to_string();
let self_span = self.tcx.def_span(adt.did());
for super_trait in supertraits(self.tcx, ty::Binder::dummy(trait_pred.trait_ref)) {
if let Some(parent_diagnostic_name) = self.tcx.get_diagnostic_name(super_trait.def_id())
{
derives.push((self_name.clone(), self_span, parent_diagnostic_name));
}
}
derives.push((self_name, self_span, diagnostic_name));
Some(derives)
}
fn note_predicate_source_and_get_derives(
&self,
err: &mut Diag<'_>,
@ -3298,35 +3494,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some(adt) if adt.did().is_local() => adt,
_ => continue,
};
if let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) {
let can_derive = match diagnostic_name {
sym::Default
| sym::Eq
| sym::PartialEq
| sym::Ord
| sym::PartialOrd
| sym::Clone
| sym::Copy
| sym::Hash
| sym::Debug => true,
_ => false,
};
if can_derive {
let self_name = trait_pred.self_ty().to_string();
let self_span = self.tcx.def_span(adt.did());
for super_trait in
supertraits(self.tcx, ty::Binder::dummy(trait_pred.trait_ref))
{
if let Some(parent_diagnostic_name) =
self.tcx.get_diagnostic_name(super_trait.def_id())
{
derives.push((self_name.clone(), self_span, parent_diagnostic_name));
}
}
derives.push((self_name, self_span, diagnostic_name));
} else {
traits.push(trait_pred.def_id());
}
if let Some(new_derives) = self.consider_suggesting_derives_for_ty(trait_pred, adt) {
derives.extend(new_derives);
} else {
traits.push(trait_pred.def_id());
}

View file

@ -684,19 +684,19 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> {
CanonicalVarKind::Region(u) => CanonicalVarKind::Region(reverse_universe_map[&u]),
CanonicalVarKind::Const(u) => CanonicalVarKind::Const(reverse_universe_map[&u]),
CanonicalVarKind::PlaceholderTy(placeholder) => {
CanonicalVarKind::PlaceholderTy(ty::Placeholder::new(
CanonicalVarKind::PlaceholderTy(ty::PlaceholderType::new(
reverse_universe_map[&placeholder.universe],
placeholder.bound,
))
}
CanonicalVarKind::PlaceholderRegion(placeholder) => {
CanonicalVarKind::PlaceholderRegion(ty::Placeholder::new(
CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion::new(
reverse_universe_map[&placeholder.universe],
placeholder.bound,
))
}
CanonicalVarKind::PlaceholderConst(placeholder) => {
CanonicalVarKind::PlaceholderConst(ty::Placeholder::new(
CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst::new(
reverse_universe_map[&placeholder.universe],
placeholder.bound,
))

View file

@ -447,7 +447,7 @@ pub enum RegionVariableOrigin<'tcx> {
/// Region variables created when instantiating a binder with
/// existential variables, e.g. when calling a function or method.
BoundRegion(Span, ty::BoundRegionKind, BoundRegionConversionTime),
BoundRegion(Span, ty::BoundRegionKind<'tcx>, BoundRegionConversionTime),
UpvarRegion(ty::UpvarId, Span),
@ -1300,13 +1300,13 @@ impl<'tcx> InferCtxt<'tcx> {
}
impl<'tcx> BoundVarReplacerDelegate<'tcx> for ToFreshVars<'tcx> {
fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> {
self.args[br.var.index()].expect_region()
}
fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx> {
self.args[bt.var.index()].expect_ty()
}
fn replace_const(&mut self, bc: ty::BoundConst) -> ty::Const<'tcx> {
fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx> {
self.args[bc.var.index()].expect_const()
}
}

View file

@ -91,7 +91,7 @@ pub(super) fn can_match_erased_ty<'tcx>(
struct MatchAgainstHigherRankedOutlives<'tcx> {
tcx: TyCtxt<'tcx>,
pattern_depth: ty::DebruijnIndex,
map: FxHashMap<ty::BoundRegion, ty::Region<'tcx>>,
map: FxHashMap<ty::BoundRegion<'tcx>, ty::Region<'tcx>>,
}
impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> {
@ -115,7 +115,7 @@ impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> {
#[instrument(level = "trace", skip(self))]
fn bind(
&mut self,
br: ty::BoundRegion,
br: ty::BoundRegion<'tcx>,
value: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
match self.map.entry(br) {

View file

@ -33,13 +33,13 @@ impl<'tcx> InferCtxt<'tcx> {
let next_universe = self.create_next_universe();
let delegate = FnMutDelegate {
regions: &mut |br: ty::BoundRegion| {
regions: &mut |br: ty::BoundRegion<'tcx>| {
ty::Region::new_placeholder(self.tcx, ty::PlaceholderRegion::new(next_universe, br))
},
types: &mut |bound_ty: ty::BoundTy| {
types: &mut |bound_ty: ty::BoundTy<'tcx>| {
Ty::new_placeholder(self.tcx, ty::PlaceholderType::new(next_universe, bound_ty))
},
consts: &mut |bound_const: ty::BoundConst| {
consts: &mut |bound_const: ty::BoundConst<'tcx>| {
ty::Const::new_placeholder(
self.tcx,
ty::PlaceholderConst::new(next_universe, bound_const),

View file

@ -268,7 +268,7 @@ fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) {
&& let ty = cx.typeck_results().expr_ty(receiver)
&& owns_allocation(cx.tcx, ty)
&& let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
&& find_attr!(cx.tcx.get_all_attrs(fn_id), AttributeKind::AsPtr(_))
&& find_attr!(cx.tcx.get_all_attrs(fn_id), AttributeKind::RustcAsPtr(_))
{
// FIXME: use `emit_node_lint` when `#[primary_span]` is added.
cx.tcx.emit_node_span_lint(

View file

@ -211,7 +211,7 @@ where
// When we get into a binder, we need to add its own bound vars to the scope.
let mut added = vec![];
for arg in t.bound_vars() {
let arg: ty::BoundVariableKind = arg;
let arg: ty::BoundVariableKind<'tcx> = arg;
match arg {
ty::BoundVariableKind::Region(ty::BoundRegionKind::Named(def_id))
| ty::BoundVariableKind::Ty(ty::BoundTyKind::Param(def_id)) => {

View file

@ -44,7 +44,7 @@ fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option<Stri
if let TyKind::Path(QPath::Resolved(_, path)) = &ty.kind {
match path.res {
Res::Def(_, def_id)
if find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::PassByValue(_)) =>
if find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::RustcPassByValue(_)) =>
{
let name = cx.tcx.item_ident(def_id);
let path_segment = path.segments.last().unwrap();
@ -52,7 +52,10 @@ fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option<Stri
}
Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => {
if let ty::Adt(adt, args) = cx.tcx.type_of(did).instantiate_identity().kind() {
if find_attr!(cx.tcx.get_all_attrs(adt.did()), AttributeKind::PassByValue(_)) {
if find_attr!(
cx.tcx.get_all_attrs(adt.did()),
AttributeKind::RustcPassByValue(_)
) {
return Some(cx.tcx.def_path_str_with_args(adt.did(), args));
}
}

View file

@ -539,10 +539,19 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
);
}
MustUsePath::Def(span, def_id, reason) => {
let span = span.find_ancestor_not_from_macro().unwrap_or(*span);
let ancenstor_span = span.find_ancestor_not_from_macro().unwrap_or(*span);
let is_redundant_let_ignore = cx
.sess()
.source_map()
.span_to_prev_source(ancenstor_span)
.ok()
.map(|prev| prev.trim_end().ends_with("let _ ="))
.unwrap_or(false);
let suggestion_span =
if is_redundant_let_ignore { *span } else { ancenstor_span };
cx.emit_span_lint(
UNUSED_MUST_USE,
span,
ancenstor_span,
UnusedDef {
pre: descr_pre,
post: descr_post,
@ -551,11 +560,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
note: *reason,
suggestion: (!is_inner).then_some(if expr_is_from_block {
UnusedDefSuggestion::BlockTailExpr {
before_span: span.shrink_to_lo(),
after_span: span.shrink_to_hi(),
before_span: suggestion_span.shrink_to_lo(),
after_span: suggestion_span.shrink_to_hi(),
}
} else {
UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() }
UnusedDefSuggestion::NormalExpr {
span: suggestion_span.shrink_to_lo(),
}
}),
},
);

View file

@ -2,6 +2,7 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use syn::parse::ParseStream;
use syn::spanned::Spanned;
use syn::{Attribute, Meta, Path, Token, Type, parse_quote};
use synstructure::{BindingInfo, Structure, VariantInfo};
@ -11,7 +12,7 @@ use crate::diagnostics::error::{
DiagnosticDeriveError, span_err, throw_invalid_attr, throw_span_err,
};
use crate::diagnostics::utils::{
FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error,
should_generate_arg, type_is_bool, type_is_unit, type_matches_path,
};
@ -42,19 +43,13 @@ pub(crate) struct DiagnosticDeriveVariantBuilder {
/// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
/// has the actual diagnostic message.
pub slug: SpannedOption<Path>,
pub slug: Option<Path>,
/// Error codes are a optional part of the struct attribute - this is only set to detect
/// multiple specifications.
pub code: SpannedOption<()>,
}
impl HasFieldMap for DiagnosticDeriveVariantBuilder {
fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
self.field_map.get(field)
}
}
impl DiagnosticDeriveKind {
/// Call `f` for the struct or for each variant of the enum, returning a `TokenStream` with the
/// tokens from `f` wrapped in an `match` expression. Emits errors for use of derive on unions
@ -111,7 +106,7 @@ impl DiagnosticDeriveKind {
impl DiagnosticDeriveVariantBuilder {
pub(crate) fn primary_message(&self) -> Option<&Path> {
match self.slug.value_ref() {
match self.slug.as_ref() {
None => {
span_err(self.span, "diagnostic slug not specified")
.help(
@ -169,7 +164,7 @@ impl DiagnosticDeriveVariantBuilder {
&self,
attr: &Attribute,
) -> Result<Option<(SubdiagnosticKind, Path, bool)>, DiagnosticDeriveError> {
let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, self)? else {
let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, &self.field_map)? else {
// Some attributes aren't errors - like documentation comments - but also aren't
// subdiagnostics.
return Ok(None);
@ -191,7 +186,7 @@ impl DiagnosticDeriveVariantBuilder {
SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(),
});
Ok(Some((subdiag.kind, slug, subdiag.no_span)))
Ok(Some((subdiag.kind, slug, false)))
}
/// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
@ -209,47 +204,54 @@ impl DiagnosticDeriveVariantBuilder {
let name = attr.path().segments.last().unwrap().ident.to_string();
let name = name.as_str();
let mut first = true;
if name == "diag" {
let mut tokens = TokenStream::new();
attr.parse_nested_meta(|nested| {
let path = &nested.path;
attr.parse_args_with(|input: ParseStream<'_>| {
let mut input = &*input;
let slug_recovery_point = input.fork();
if first && (nested.input.is_empty() || nested.input.peek(Token![,])) {
self.slug.set_once(path.clone(), path.span().unwrap());
first = false;
return Ok(());
let slug = input.parse::<Path>()?;
if input.is_empty() || input.peek(Token![,]) {
self.slug = Some(slug);
} else {
input = &slug_recovery_point;
}
first = false;
let Ok(nested) = nested.value() else {
span_err(
nested.input.span().unwrap(),
"diagnostic slug must be the first argument",
)
.emit();
return Ok(());
};
if path.is_ident("code") {
self.code.set_once((), path.span().unwrap());
let code = nested.parse::<syn::Expr>()?;
tokens.extend(quote! {
diag.code(#code);
});
} else {
span_err(path.span().unwrap(), "unknown argument")
.note("only the `code` parameter is valid after the slug")
while !input.is_empty() {
input.parse::<Token![,]>()?;
// Allow trailing comma
if input.is_empty() {
break;
}
let arg_name: Path = input.parse::<Path>()?;
if input.peek(Token![,]) {
span_err(
arg_name.span().unwrap(),
"diagnostic slug must be the first argument",
)
.emit();
// consume the buffer so we don't have syntax errors from syn
let _ = nested.parse::<TokenStream>();
continue;
}
let arg_name = arg_name.require_ident()?;
input.parse::<Token![=]>()?;
let arg_value = input.parse::<syn::Expr>()?;
match arg_name.to_string().as_str() {
"code" => {
self.code.set_once((), arg_name.span().unwrap());
tokens.extend(quote! {
diag.code(#arg_value);
});
}
_ => {
span_err(arg_name.span().unwrap(), "unknown argument")
.note("only the `code` parameter is valid after the slug")
.emit();
}
}
}
Ok(())
})?;
return Ok(tokens);
}

View file

@ -1,9 +1,10 @@
#![deny(unused_must_use)]
use proc_macro2::TokenStream;
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use syn::parse::ParseStream;
use syn::spanned::Spanned;
use syn::{Attribute, Meta, MetaList, Path};
use syn::{Attribute, Meta, MetaList, Path, Token};
use synstructure::{BindingInfo, Structure, VariantInfo};
use super::utils::SubdiagnosticVariant;
@ -11,10 +12,10 @@ use crate::diagnostics::error::{
DiagnosticDeriveError, invalid_attr, span_err, throw_invalid_attr, throw_span_err,
};
use crate::diagnostics::utils::{
AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce,
SpannedOption, SubdiagnosticKind, build_field_mapping, build_suggestion_code, is_doc_comment,
new_code_ident, report_error_if_not_applied_to_applicability,
report_error_if_not_applied_to_span, should_generate_arg,
AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption,
SubdiagnosticKind, build_field_mapping, build_suggestion_code, is_doc_comment, new_code_ident,
report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span,
should_generate_arg,
};
/// The central struct for constructing the `add_to_diag` method from an annotated struct.
@ -142,12 +143,6 @@ struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
is_enum: bool,
}
impl<'parent, 'a> HasFieldMap for SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
self.fields.get(field)
}
}
/// Provides frequently-needed information about the diagnostic kinds being derived for this type.
#[derive(Clone, Copy, Debug)]
struct KindsStatistics {
@ -187,14 +182,12 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics {
}
impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
fn identify_kind(
&mut self,
) -> Result<Vec<(SubdiagnosticKind, Path, bool)>, DiagnosticDeriveError> {
fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
let mut kind_slugs = vec![];
for attr in self.variant.ast().attrs {
let Some(SubdiagnosticVariant { kind, slug, no_span }) =
SubdiagnosticVariant::from_attr(attr, self)?
let Some(SubdiagnosticVariant { kind, slug }) =
SubdiagnosticVariant::from_attr(attr, &self.fields)?
else {
// Some attributes aren't errors - like documentation comments - but also aren't
// subdiagnostics.
@ -213,7 +206,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
);
};
kind_slugs.push((kind, slug, no_span));
kind_slugs.push((kind, slug));
}
Ok(kind_slugs)
@ -437,23 +430,35 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
let mut code = None;
list.parse_nested_meta(|nested| {
if nested.path.is_ident("code") {
let code_field = new_code_ident();
let span = nested.path.span().unwrap();
let formatting_init = build_suggestion_code(
&code_field,
nested,
self,
AllowMultipleAlternatives::No,
);
code.set_once((code_field, formatting_init), span);
} else {
span_err(
nested.path.span().unwrap(),
"`code` is the only valid nested attribute",
)
.emit();
list.parse_args_with(|input: ParseStream<'_>| {
while !input.is_empty() {
let arg_name = input.parse::<Ident>()?;
match arg_name.to_string().as_str() {
"code" => {
let code_field = new_code_ident();
let formatting_init = build_suggestion_code(
&code_field,
input,
&self.fields,
AllowMultipleAlternatives::No,
)?;
code.set_once(
(code_field, formatting_init),
arg_name.span().unwrap(),
);
}
_ => {
span_err(
arg_name.span().unwrap(),
"`code` is the only valid nested attribute",
)
.emit();
}
}
if input.is_empty() {
break;
}
input.parse::<Token![,]>()?;
}
Ok(())
})?;
@ -492,8 +497,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
pub(crate) fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
let kind_slugs = self.identify_kind()?;
let kind_stats: KindsStatistics =
kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect();
let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect();
let init = if kind_stats.has_multipart_suggestion {
quote! { let mut suggestions = Vec::new(); }
@ -526,17 +530,13 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
let diag = &self.parent.diag;
let mut calls = TokenStream::new();
for (kind, slug, no_span) in kind_slugs {
for (kind, slug) in kind_slugs {
let message = format_ident!("__message");
calls.extend(
quote! { let #message = #diag.eagerly_translate(crate::fluent_generated::#slug); },
);
let name = format_ident!(
"{}{}",
if span_field.is_some() && !no_span { "span_" } else { "" },
kind
);
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
let call = match kind {
SubdiagnosticKind::Suggestion {
suggestion_kind,
@ -588,9 +588,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
}
}
_ => {
if let Some(span) = span_field
&& !no_span
{
if let Some(span) = span_field {
quote! { #diag.#name(#span, #message); }
} else {
quote! { #diag.#name(#message); }

View file

@ -6,7 +6,7 @@ use std::str::FromStr;
use proc_macro::Span;
use proc_macro2::{Ident, TokenStream};
use quote::{ToTokens, format_ident, quote};
use syn::meta::ParseNestedMeta;
use syn::parse::ParseStream;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{Attribute, Field, LitStr, Meta, Path, Token, Type, TypeTuple, parenthesized};
@ -261,108 +261,104 @@ impl<T> SetOnce<T> for SpannedOption<T> {
pub(super) type FieldMap = HashMap<String, TokenStream>;
pub(crate) trait HasFieldMap {
/// Returns the binding for the field with the given name, if it exists on the type.
fn get_field_binding(&self, field: &String) -> Option<&TokenStream>;
/// In the strings in the attributes supplied to this macro, we want callers to be able to
/// reference fields in the format string. For example:
///
/// ```ignore (not-usage-example)
/// /// Suggest `==` when users wrote `===`.
/// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")]
/// struct NotJavaScriptEq {
/// #[primary_span]
/// span: Span,
/// lhs: Ident,
/// rhs: Ident,
/// }
/// ```
///
/// We want to automatically pick up that `{lhs}` refers `self.lhs` and `{rhs}` refers to
/// `self.rhs`, then generate this call to `format!`:
///
/// ```ignore (not-usage-example)
/// format!("{lhs} == {rhs}", lhs = self.lhs, rhs = self.rhs)
/// ```
///
/// This function builds the entire call to `format!`.
pub(super) fn build_format(
field_map: &FieldMap,
input: &str,
span: proc_macro2::Span,
) -> TokenStream {
// This set is used later to generate the final format string. To keep builds reproducible,
// the iteration order needs to be deterministic, hence why we use a `BTreeSet` here
// instead of a `HashSet`.
let mut referenced_fields: BTreeSet<String> = BTreeSet::new();
/// In the strings in the attributes supplied to this macro, we want callers to be able to
/// reference fields in the format string. For example:
///
/// ```ignore (not-usage-example)
/// /// Suggest `==` when users wrote `===`.
/// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")]
/// struct NotJavaScriptEq {
/// #[primary_span]
/// span: Span,
/// lhs: Ident,
/// rhs: Ident,
/// }
/// ```
///
/// We want to automatically pick up that `{lhs}` refers `self.lhs` and `{rhs}` refers to
/// `self.rhs`, then generate this call to `format!`:
///
/// ```ignore (not-usage-example)
/// format!("{lhs} == {rhs}", lhs = self.lhs, rhs = self.rhs)
/// ```
///
/// This function builds the entire call to `format!`.
fn build_format(&self, input: &str, span: proc_macro2::Span) -> TokenStream {
// This set is used later to generate the final format string. To keep builds reproducible,
// the iteration order needs to be deterministic, hence why we use a `BTreeSet` here
// instead of a `HashSet`.
let mut referenced_fields: BTreeSet<String> = BTreeSet::new();
// At this point, we can start parsing the format string.
let mut it = input.chars().peekable();
// At this point, we can start parsing the format string.
let mut it = input.chars().peekable();
// Once the start of a format string has been found, process the format string and spit out
// the referenced fields. Leaves `it` sitting on the closing brace of the format string, so
// the next call to `it.next()` retrieves the next character.
while let Some(c) = it.next() {
if c != '{' {
continue;
}
if *it.peek().unwrap_or(&'\0') == '{' {
assert_eq!(it.next().unwrap(), '{');
continue;
}
let mut eat_argument = || -> Option<String> {
let mut result = String::new();
// Format specifiers look like:
//
// format := '{' [ argument ] [ ':' format_spec ] '}' .
//
// Therefore, we only need to eat until ':' or '}' to find the argument.
while let Some(c) = it.next() {
result.push(c);
let next = *it.peek().unwrap_or(&'\0');
if next == '}' {
break;
} else if next == ':' {
// Eat the ':' character.
assert_eq!(it.next().unwrap(), ':');
break;
}
}
// Eat until (and including) the matching '}'
while it.next()? != '}' {
continue;
}
Some(result)
};
if let Some(referenced_field) = eat_argument() {
referenced_fields.insert(referenced_field);
}
// Once the start of a format string has been found, process the format string and spit out
// the referenced fields. Leaves `it` sitting on the closing brace of the format string, so
// the next call to `it.next()` retrieves the next character.
while let Some(c) = it.next() {
if c != '{' {
continue;
}
if *it.peek().unwrap_or(&'\0') == '{' {
assert_eq!(it.next().unwrap(), '{');
continue;
}
let mut eat_argument = || -> Option<String> {
let mut result = String::new();
// Format specifiers look like:
//
// format := '{' [ argument ] [ ':' format_spec ] '}' .
//
// Therefore, we only need to eat until ':' or '}' to find the argument.
while let Some(c) = it.next() {
result.push(c);
let next = *it.peek().unwrap_or(&'\0');
if next == '}' {
break;
} else if next == ':' {
// Eat the ':' character.
assert_eq!(it.next().unwrap(), ':');
break;
}
}
// Eat until (and including) the matching '}'
while it.next()? != '}' {
continue;
}
Some(result)
};
// At this point, `referenced_fields` contains a set of the unique fields that were
// referenced in the format string. Generate the corresponding "x = self.x" format
// string parameters:
let args = referenced_fields.into_iter().map(|field: String| {
let field_ident = format_ident!("{}", field);
let value = match self.get_field_binding(&field) {
Some(value) => value.clone(),
// This field doesn't exist. Emit a diagnostic.
None => {
span_err(
span.unwrap(),
format!("`{field}` doesn't refer to a field on this type"),
)
if let Some(referenced_field) = eat_argument() {
referenced_fields.insert(referenced_field);
}
}
// At this point, `referenced_fields` contains a set of the unique fields that were
// referenced in the format string. Generate the corresponding "x = self.x" format
// string parameters:
let args = referenced_fields.into_iter().map(|field: String| {
let field_ident = format_ident!("{}", field);
let value = match field_map.get(&field) {
Some(value) => value.clone(),
// This field doesn't exist. Emit a diagnostic.
None => {
span_err(span.unwrap(), format!("`{field}` doesn't refer to a field on this type"))
.emit();
quote! {
"{#field}"
}
quote! {
"{#field}"
}
};
quote! {
#field_ident = #value
}
});
};
quote! {
format!(#input #(,#args)*)
#field_ident = #value
}
});
quote! {
format!(#input #(,#args)*)
}
}
@ -428,76 +424,63 @@ pub(super) enum AllowMultipleAlternatives {
}
fn parse_suggestion_values(
nested: ParseNestedMeta<'_>,
nested: ParseStream<'_>,
allow_multiple: AllowMultipleAlternatives,
) -> syn::Result<Vec<LitStr>> {
let values = if let Ok(val) = nested.value() {
vec![val.parse()?]
} else {
let content;
parenthesized!(content in nested.input);
if nested.parse::<Token![=]>().is_ok() {
return Ok(vec![nested.parse::<LitStr>()?]);
}
if let AllowMultipleAlternatives::No = allow_multiple {
let content;
parenthesized!(content in nested);
if let AllowMultipleAlternatives::No = allow_multiple {
span_err(content.span().unwrap(), "expected exactly one string literal for `code = ...`")
.emit();
return Ok(vec![]);
}
let literals = Punctuated::<LitStr, Token![,]>::parse_terminated(&content);
Ok(match literals {
Ok(p) if p.is_empty() => {
span_err(
nested.input.span().unwrap(),
"expected exactly one string literal for `code = ...`",
content.span().unwrap(),
"expected at least one string literal for `code(...)`",
)
.emit();
vec![]
} else {
let literals = Punctuated::<LitStr, Token![,]>::parse_terminated(&content);
match literals {
Ok(p) if p.is_empty() => {
span_err(
content.span().unwrap(),
"expected at least one string literal for `code(...)`",
)
.emit();
vec![]
}
Ok(p) => p.into_iter().collect(),
Err(_) => {
span_err(
content.span().unwrap(),
"`code(...)` must contain only string literals",
)
.emit();
vec![]
}
}
}
};
Ok(values)
Ok(p) => p.into_iter().collect(),
Err(_) => {
span_err(content.span().unwrap(), "`code(...)` must contain only string literals")
.emit();
vec![]
}
})
}
/// Constructs the `format!()` invocation(s) necessary for a `#[suggestion*(code = "foo")]` or
/// `#[suggestion*(code("foo", "bar"))]` attribute field
pub(super) fn build_suggestion_code(
code_field: &Ident,
nested: ParseNestedMeta<'_>,
fields: &impl HasFieldMap,
nested: ParseStream<'_>,
fields: &FieldMap,
allow_multiple: AllowMultipleAlternatives,
) -> TokenStream {
let values = match parse_suggestion_values(nested, allow_multiple) {
Ok(x) => x,
Err(e) => return e.into_compile_error(),
};
) -> Result<TokenStream, syn::Error> {
let values = parse_suggestion_values(nested, allow_multiple)?;
if let AllowMultipleAlternatives::Yes = allow_multiple {
Ok(if let AllowMultipleAlternatives::Yes = allow_multiple {
let formatted_strings: Vec<_> = values
.into_iter()
.map(|value| fields.build_format(&value.value(), value.span()))
.map(|value| build_format(fields, &value.value(), value.span()))
.collect();
quote! { let #code_field = [#(#formatted_strings),*].into_iter(); }
} else if let [value] = values.as_slice() {
let formatted_str = fields.build_format(&value.value(), value.span());
let formatted_str = build_format(fields, &value.value(), value.span());
quote! { let #code_field = #formatted_str; }
} else {
// error handled previously
quote! { let #code_field = String::new(); }
}
})
}
/// Possible styles for suggestion subdiagnostics.
@ -605,16 +588,15 @@ pub(super) enum SubdiagnosticKind {
pub(super) struct SubdiagnosticVariant {
pub(super) kind: SubdiagnosticKind,
pub(super) slug: Option<Path>,
pub(super) no_span: bool,
}
impl SubdiagnosticVariant {
/// Constructs a `SubdiagnosticVariant` from a field or type attribute such as `#[note]`,
/// `#[error(parser::add_paren, no_span)]` or `#[suggestion(code = "...")]`. Returns the
/// `#[error(parser::add_paren)]` or `#[suggestion(code = "...")]`. Returns the
/// `SubdiagnosticKind` and the diagnostic slug, if specified.
pub(super) fn from_attr(
attr: &Attribute,
fields: &impl HasFieldMap,
fields: &FieldMap,
) -> Result<Option<SubdiagnosticVariant>, DiagnosticDeriveError> {
// Always allow documentation comments.
if is_doc_comment(attr) {
@ -694,7 +676,7 @@ impl SubdiagnosticVariant {
| SubdiagnosticKind::HelpOnce
| SubdiagnosticKind::Warn
| SubdiagnosticKind::MultipartSuggestion { .. } => {
return Ok(Some(SubdiagnosticVariant { kind, slug: None, no_span: false }));
return Ok(Some(SubdiagnosticVariant { kind, slug: None }));
}
SubdiagnosticKind::Suggestion { .. } => {
throw_span_err!(span, "suggestion without `code = \"...\"`")
@ -709,112 +691,93 @@ impl SubdiagnosticVariant {
let mut code = None;
let mut suggestion_kind = None;
let mut first = true;
let mut slug = None;
let mut no_span = false;
list.parse_nested_meta(|nested| {
if nested.input.is_empty() || nested.input.peek(Token![,]) {
if first {
slug = Some(nested.path);
} else if nested.path.is_ident("no_span") {
no_span = true;
} else {
span_err(nested.input.span().unwrap(), "a diagnostic slug must be the first argument to the attribute").emit();
list.parse_args_with(|input: ParseStream<'_>| {
let mut is_first = true;
while !input.is_empty() {
let arg_name: Path = input.parse::<Path>()?;
let arg_name_span = arg_name.span().unwrap();
if input.is_empty() || input.parse::<Token![,]>().is_ok() {
if is_first {
slug = Some(arg_name);
is_first = false;
} else {
span_err(arg_name_span, "a diagnostic slug must be the first argument to the attribute").emit();
}
continue
}
is_first = false;
first = false;
return Ok(());
}
match (arg_name.require_ident()?.to_string().as_str(), &mut kind) {
("code", SubdiagnosticKind::Suggestion { code_field, .. }) => {
let code_init = build_suggestion_code(
&code_field,
&input,
fields,
AllowMultipleAlternatives::Yes,
)?;
code.set_once(code_init, arg_name_span);
}
(
"applicability",
SubdiagnosticKind::Suggestion { applicability, .. }
| SubdiagnosticKind::MultipartSuggestion { applicability, .. },
) => {
input.parse::<Token![=]>()?;
let value = input.parse::<LitStr>()?;
let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| {
span_err(value.span().unwrap(), "invalid applicability").emit();
Applicability::Unspecified
});
applicability.set_once(value, span);
}
(
"style",
SubdiagnosticKind::Suggestion { .. }
| SubdiagnosticKind::MultipartSuggestion { .. },
) => {
input.parse::<Token![=]>()?;
let value = input.parse::<LitStr>()?;
first = false;
let value = value.value().parse().unwrap_or_else(|()| {
span_err(value.span().unwrap(), "invalid suggestion style")
.help("valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`")
.emit();
SuggestionKind::Normal
});
let nested_name = nested.path.segments.last().unwrap().ident.to_string();
let nested_name = nested_name.as_str();
suggestion_kind.set_once(value, span);
}
let path_span = nested.path.span().unwrap();
let val_span = nested.input.span().unwrap();
macro_rules! get_string {
() => {{
let Ok(value) = nested.value().and_then(|x| x.parse::<LitStr>()) else {
span_err(val_span, "expected `= \"xxx\"`").emit();
return Ok(());
};
value
}};
}
let mut has_errors = false;
let input = nested.input;
match (nested_name, &mut kind) {
("code", SubdiagnosticKind::Suggestion { code_field, .. }) => {
let code_init = build_suggestion_code(
code_field,
nested,
fields,
AllowMultipleAlternatives::Yes,
);
code.set_once(code_init, path_span);
}
(
"applicability",
SubdiagnosticKind::Suggestion { applicability, .. }
| SubdiagnosticKind::MultipartSuggestion { applicability, .. },
) => {
let value = get_string!();
let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| {
span_err(value.span().unwrap(), "invalid applicability").emit();
has_errors = true;
Applicability::Unspecified
});
applicability.set_once(value, span);
}
(
"style",
SubdiagnosticKind::Suggestion { .. }
| SubdiagnosticKind::MultipartSuggestion { .. },
) => {
let value = get_string!();
let value = value.value().parse().unwrap_or_else(|()| {
span_err(value.span().unwrap(), "invalid suggestion style")
.help("valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`")
// Invalid nested attribute
(_, SubdiagnosticKind::Suggestion { .. }) => {
span_err(arg_name_span, "invalid nested attribute")
.help(
"only `style`, `code` and `applicability` are valid nested attributes",
)
.emit();
has_errors = true;
SuggestionKind::Normal
});
suggestion_kind.set_once(value, span);
// Consume the rest of the input to avoid spamming errors
let _ = input.parse::<TokenStream>();
}
(_, SubdiagnosticKind::MultipartSuggestion { .. }) => {
span_err(arg_name_span, "invalid nested attribute")
.help("only `style` and `applicability` are valid nested attributes")
.emit();
// Consume the rest of the input to avoid spamming errors
let _ = input.parse::<TokenStream>();
}
_ => {
span_err(arg_name_span, "no nested attribute expected here").emit();
// Consume the rest of the input to avoid spamming errors
let _ = input.parse::<TokenStream>();
}
}
// Invalid nested attribute
(_, SubdiagnosticKind::Suggestion { .. }) => {
span_err(path_span, "invalid nested attribute")
.help(
"only `no_span`, `style`, `code` and `applicability` are valid nested attributes",
)
.emit();
has_errors = true;
}
(_, SubdiagnosticKind::MultipartSuggestion { .. }) => {
span_err(path_span, "invalid nested attribute")
.help("only `no_span`, `style` and `applicability` are valid nested attributes")
.emit();
has_errors = true;
}
_ => {
span_err(path_span, "only `no_span` is a valid nested attribute").emit();
has_errors = true;
}
if input.is_empty() { break }
input.parse::<Token![,]>()?;
}
if has_errors {
// Consume the rest of the input to avoid spamming errors
let _ = input.parse::<TokenStream>();
}
Ok(())
})?;
@ -851,7 +814,7 @@ impl SubdiagnosticVariant {
| SubdiagnosticKind::Warn => {}
}
Ok(Some(SubdiagnosticVariant { kind, slug, no_span }))
Ok(Some(SubdiagnosticVariant { kind, slug }))
}
}

View file

@ -48,7 +48,7 @@ pub enum ObjectLifetimeDefault {
/// Maps the id of each bound variable reference to the variable decl
/// that it corresponds to.
#[derive(Debug, Default, HashStable)]
pub struct ResolveBoundVars {
pub struct ResolveBoundVars<'tcx> {
// Maps from every use of a named (not anonymous) bound var to a
// `ResolvedArg` describing how that variable is bound.
pub defs: SortedMap<ItemLocalId, ResolvedArg>,
@ -59,7 +59,7 @@ pub struct ResolveBoundVars {
// - closures
// - trait refs
// - bound types (like `T` in `for<'a> T<'a>: Foo`)
pub late_bound_vars: SortedMap<ItemLocalId, Vec<ty::BoundVariableKind>>,
pub late_bound_vars: SortedMap<ItemLocalId, Vec<ty::BoundVariableKind<'tcx>>>,
// List captured variables for each opaque type.
pub opaque_captured_lifetimes: LocalDefIdMap<Vec<(ResolvedArg, LocalDefId)>>,

View file

@ -1,3 +1,10 @@
//! To improve compile times and code size for the compiler itself, query
//! values are "erased" in some contexts (e.g. inside in-memory cache types),
//! to reduce the number of generic instantiations created during codegen.
//!
//! See <https://github.com/rust-lang/rust/pull/151715> for some bootstrap-time
//! and performance benchmarks.
use std::ffi::OsStr;
use std::intrinsics::transmute_unchecked;
use std::mem::MaybeUninit;
@ -14,138 +21,169 @@ use crate::ty::adjustment::CoerceUnsizedInfo;
use crate::ty::{self, Ty, TyCtxt};
use crate::{mir, traits};
/// Internal implementation detail of [`Erased`].
#[derive(Copy, Clone)]
pub struct Erased<T: Copy> {
// We use `MaybeUninit` here so we can store any value
// in `data` since we aren't actually storing a `T`.
data: MaybeUninit<T>,
pub struct ErasedData<Storage: Copy> {
/// We use `MaybeUninit` here to make sure it's legal to store a transmuted
/// value that isn't actually of type `Storage`.
data: MaybeUninit<Storage>,
}
pub trait EraseType: Copy {
type Result: Copy;
/// Trait for types that can be erased into [`Erased<Self>`].
///
/// Erasing and unerasing values is performed by [`erase_val`] and [`restore_val`].
///
/// FIXME: This whole trait could potentially be replaced by `T: Copy` and the
/// storage type `[u8; size_of::<T>()]` when support for that is more mature.
pub trait Erasable: Copy {
/// Storage type to used for erased values of this type.
/// Should be `[u8; N]`, where N is equal to `size_of::<Self>`.
///
/// [`ErasedData`] wraps this storage type in `MaybeUninit` to ensure that
/// transmutes to/from erased storage are well-defined.
type Storage: Copy;
}
// Allow `type_alias_bounds` since compilation will fail without `EraseType`.
#[allow(type_alias_bounds)]
pub type Erase<T: EraseType> = Erased<impl Copy>;
/// A value of `T` that has been "erased" into some opaque storage type.
///
/// This is helpful for reducing the number of concrete instantiations needed
/// during codegen when building the compiler.
///
/// Using an opaque type alias allows the type checker to enforce that
/// `Erased<T>` and `Erased<U>` are still distinct types, while allowing
/// monomorphization to see that they might actually use the same storage type.
pub type Erased<T: Erasable> = ErasedData<impl Copy>;
/// Erases a value of type `T` into `Erased<T>`.
///
/// `Erased<T>` and `Erased<U>` are type-checked as distinct types, but codegen
/// can see whether they actually have the same storage type.
///
/// FIXME: This might have soundness issues with erasable types that don't
/// implement the same auto-traits as `[u8; _]`; see
/// <https://github.com/rust-lang/rust/pull/151715#discussion_r2740113250>
#[inline(always)]
#[define_opaque(Erase)]
pub fn erase<T: EraseType>(src: T) -> Erase<T> {
#[define_opaque(Erased)]
pub fn erase_val<T: Erasable>(value: T) -> Erased<T> {
// Ensure the sizes match
const {
if size_of::<T>() != size_of::<T::Result>() {
panic!("size of T must match erased type T::Result")
if size_of::<T>() != size_of::<T::Storage>() {
panic!("size of T must match erased type <T as Erasable>::Storage")
}
};
Erased::<<T as EraseType>::Result> {
ErasedData::<<T as Erasable>::Storage> {
// `transmute_unchecked` is needed here because it does not have `transmute`'s size check
// (and thus allows to transmute between `T` and `MaybeUninit<T::Result>`) (we do the size
// (and thus allows to transmute between `T` and `MaybeUninit<T::Storage>`) (we do the size
// check ourselves in the `const` block above).
//
// `transmute_copy` is also commonly used for this (and it would work here since
// `EraseType: Copy`), but `transmute_unchecked` better explains the intent.
// `Erasable: Copy`), but `transmute_unchecked` better explains the intent.
//
// SAFETY: It is safe to transmute to MaybeUninit for types with the same sizes.
data: unsafe { transmute_unchecked::<T, MaybeUninit<T::Result>>(src) },
data: unsafe { transmute_unchecked::<T, MaybeUninit<T::Storage>>(value) },
}
}
/// Restores an erased value.
/// Restores an erased value to its real type.
///
/// This relies on the fact that `Erased<T>` and `Erased<U>` are type-checked
/// as distinct types, even if they use the same storage type.
#[inline(always)]
#[define_opaque(Erase)]
pub fn restore<T: EraseType>(value: Erase<T>) -> T {
let value: Erased<<T as EraseType>::Result> = value;
// See comment in `erase` for why we use `transmute_unchecked`.
#[define_opaque(Erased)]
pub fn restore_val<T: Erasable>(erased_value: Erased<T>) -> T {
let ErasedData { data }: ErasedData<<T as Erasable>::Storage> = erased_value;
// See comment in `erase_val` for why we use `transmute_unchecked`.
//
// SAFETY: Due to the use of impl Trait in `Erase` the only way to safely create an instance
// of `Erase` is to call `erase`, so we know that `value.data` is a valid instance of `T` of
// the right size.
unsafe { transmute_unchecked::<MaybeUninit<T::Result>, T>(value.data) }
// SAFETY: Due to the use of impl Trait in `Erased` the only way to safely create an instance
// of `Erased` is to call `erase_val`, so we know that `erased_value.data` is a valid instance
// of `T` of the right size.
unsafe { transmute_unchecked::<MaybeUninit<T::Storage>, T>(data) }
}
impl<T> EraseType for &'_ T {
type Result = [u8; size_of::<&'static ()>()];
// FIXME(#151565): Using `T: ?Sized` here should let us remove the separate
// impls for fat reference types.
impl<T> Erasable for &'_ T {
type Storage = [u8; size_of::<&'static ()>()];
}
impl<T> EraseType for &'_ [T] {
type Result = [u8; size_of::<&'static [()]>()];
impl<T> Erasable for &'_ [T] {
type Storage = [u8; size_of::<&'static [()]>()];
}
impl EraseType for &'_ OsStr {
type Result = [u8; size_of::<&'static OsStr>()];
impl Erasable for &'_ OsStr {
type Storage = [u8; size_of::<&'static OsStr>()];
}
impl<T> EraseType for &'_ ty::List<T> {
type Result = [u8; size_of::<&'static ty::List<()>>()];
impl<T> Erasable for &'_ ty::List<T> {
type Storage = [u8; size_of::<&'static ty::List<()>>()];
}
impl<T> EraseType for &'_ ty::ListWithCachedTypeInfo<T> {
type Result = [u8; size_of::<&'static ty::ListWithCachedTypeInfo<()>>()];
impl<T> Erasable for &'_ ty::ListWithCachedTypeInfo<T> {
type Storage = [u8; size_of::<&'static ty::ListWithCachedTypeInfo<()>>()];
}
impl<I: rustc_index::Idx, T> EraseType for &'_ rustc_index::IndexSlice<I, T> {
type Result = [u8; size_of::<&'static rustc_index::IndexSlice<u32, ()>>()];
impl<I: rustc_index::Idx, T> Erasable for &'_ rustc_index::IndexSlice<I, T> {
type Storage = [u8; size_of::<&'static rustc_index::IndexSlice<u32, ()>>()];
}
impl<T> EraseType for Result<&'_ T, traits::query::NoSolution> {
type Result = [u8; size_of::<Result<&'static (), traits::query::NoSolution>>()];
impl<T> Erasable for Result<&'_ T, traits::query::NoSolution> {
type Storage = [u8; size_of::<Result<&'static (), traits::query::NoSolution>>()];
}
impl<T> EraseType for Result<&'_ [T], traits::query::NoSolution> {
type Result = [u8; size_of::<Result<&'static [()], traits::query::NoSolution>>()];
impl<T> Erasable for Result<&'_ [T], traits::query::NoSolution> {
type Storage = [u8; size_of::<Result<&'static [()], traits::query::NoSolution>>()];
}
impl<T> EraseType for Result<&'_ T, rustc_errors::ErrorGuaranteed> {
type Result = [u8; size_of::<Result<&'static (), rustc_errors::ErrorGuaranteed>>()];
impl<T> Erasable for Result<&'_ T, rustc_errors::ErrorGuaranteed> {
type Storage = [u8; size_of::<Result<&'static (), rustc_errors::ErrorGuaranteed>>()];
}
impl<T> EraseType for Result<&'_ [T], rustc_errors::ErrorGuaranteed> {
type Result = [u8; size_of::<Result<&'static [()], rustc_errors::ErrorGuaranteed>>()];
impl<T> Erasable for Result<&'_ [T], rustc_errors::ErrorGuaranteed> {
type Storage = [u8; size_of::<Result<&'static [()], rustc_errors::ErrorGuaranteed>>()];
}
impl<T> EraseType for Result<&'_ T, traits::CodegenObligationError> {
type Result = [u8; size_of::<Result<&'static (), traits::CodegenObligationError>>()];
impl<T> Erasable for Result<&'_ T, traits::CodegenObligationError> {
type Storage = [u8; size_of::<Result<&'static (), traits::CodegenObligationError>>()];
}
impl<T> EraseType for Result<&'_ T, &'_ ty::layout::FnAbiError<'_>> {
type Result = [u8; size_of::<Result<&'static (), &'static ty::layout::FnAbiError<'static>>>()];
impl<T> Erasable for Result<&'_ T, &'_ ty::layout::FnAbiError<'_>> {
type Storage = [u8; size_of::<Result<&'static (), &'static ty::layout::FnAbiError<'static>>>()];
}
impl<T> EraseType for Result<(&'_ T, crate::thir::ExprId), rustc_errors::ErrorGuaranteed> {
type Result = [u8; size_of::<
impl<T> Erasable for Result<(&'_ T, crate::thir::ExprId), rustc_errors::ErrorGuaranteed> {
type Storage = [u8; size_of::<
Result<(&'static (), crate::thir::ExprId), rustc_errors::ErrorGuaranteed>,
>()];
}
impl EraseType for Result<Option<ty::Instance<'_>>, rustc_errors::ErrorGuaranteed> {
type Result =
impl Erasable for Result<Option<ty::Instance<'_>>, rustc_errors::ErrorGuaranteed> {
type Storage =
[u8; size_of::<Result<Option<ty::Instance<'static>>, rustc_errors::ErrorGuaranteed>>()];
}
impl EraseType for Result<CoerceUnsizedInfo, rustc_errors::ErrorGuaranteed> {
type Result = [u8; size_of::<Result<CoerceUnsizedInfo, rustc_errors::ErrorGuaranteed>>()];
impl Erasable for Result<CoerceUnsizedInfo, rustc_errors::ErrorGuaranteed> {
type Storage = [u8; size_of::<Result<CoerceUnsizedInfo, rustc_errors::ErrorGuaranteed>>()];
}
impl EraseType
impl Erasable
for Result<Option<ty::EarlyBinder<'_, ty::Const<'_>>>, rustc_errors::ErrorGuaranteed>
{
type Result = [u8; size_of::<
type Storage = [u8; size_of::<
Result<Option<ty::EarlyBinder<'static, ty::Const<'static>>>, rustc_errors::ErrorGuaranteed>,
>()];
}
impl EraseType for Result<ty::GenericArg<'_>, traits::query::NoSolution> {
type Result = [u8; size_of::<Result<ty::GenericArg<'static>, traits::query::NoSolution>>()];
impl Erasable for Result<ty::GenericArg<'_>, traits::query::NoSolution> {
type Storage = [u8; size_of::<Result<ty::GenericArg<'static>, traits::query::NoSolution>>()];
}
impl EraseType for Result<bool, &ty::layout::LayoutError<'_>> {
type Result = [u8; size_of::<Result<bool, &'static ty::layout::LayoutError<'static>>>()];
impl Erasable for Result<bool, &ty::layout::LayoutError<'_>> {
type Storage = [u8; size_of::<Result<bool, &'static ty::layout::LayoutError<'static>>>()];
}
impl EraseType for Result<rustc_abi::TyAndLayout<'_, Ty<'_>>, &ty::layout::LayoutError<'_>> {
type Result = [u8; size_of::<
impl Erasable for Result<rustc_abi::TyAndLayout<'_, Ty<'_>>, &ty::layout::LayoutError<'_>> {
type Storage = [u8; size_of::<
Result<
rustc_abi::TyAndLayout<'static, Ty<'static>>,
&'static ty::layout::LayoutError<'static>,
@ -153,35 +191,36 @@ impl EraseType for Result<rustc_abi::TyAndLayout<'_, Ty<'_>>, &ty::layout::Layou
>()];
}
impl EraseType for Result<mir::ConstAlloc<'_>, mir::interpret::ErrorHandled> {
type Result = [u8; size_of::<Result<mir::ConstAlloc<'static>, mir::interpret::ErrorHandled>>()];
impl Erasable for Result<mir::ConstAlloc<'_>, mir::interpret::ErrorHandled> {
type Storage =
[u8; size_of::<Result<mir::ConstAlloc<'static>, mir::interpret::ErrorHandled>>()];
}
impl EraseType for Result<mir::ConstValue, mir::interpret::ErrorHandled> {
type Result = [u8; size_of::<Result<mir::ConstValue, mir::interpret::ErrorHandled>>()];
impl Erasable for Result<mir::ConstValue, mir::interpret::ErrorHandled> {
type Storage = [u8; size_of::<Result<mir::ConstValue, mir::interpret::ErrorHandled>>()];
}
impl EraseType for Option<(mir::ConstValue, Ty<'_>)> {
type Result = [u8; size_of::<Option<(mir::ConstValue, Ty<'_>)>>()];
impl Erasable for Option<(mir::ConstValue, Ty<'_>)> {
type Storage = [u8; size_of::<Option<(mir::ConstValue, Ty<'_>)>>()];
}
impl EraseType for EvalToValTreeResult<'_> {
type Result = [u8; size_of::<EvalToValTreeResult<'static>>()];
impl Erasable for EvalToValTreeResult<'_> {
type Storage = [u8; size_of::<EvalToValTreeResult<'static>>()];
}
impl EraseType for Result<&'_ ty::List<Ty<'_>>, ty::util::AlwaysRequiresDrop> {
type Result =
impl Erasable for Result<&'_ ty::List<Ty<'_>>, ty::util::AlwaysRequiresDrop> {
type Storage =
[u8; size_of::<Result<&'static ty::List<Ty<'static>>, ty::util::AlwaysRequiresDrop>>()];
}
impl EraseType for Result<ty::EarlyBinder<'_, Ty<'_>>, CyclePlaceholder> {
type Result = [u8; size_of::<Result<ty::EarlyBinder<'static, Ty<'_>>, CyclePlaceholder>>()];
impl Erasable for Result<ty::EarlyBinder<'_, Ty<'_>>, CyclePlaceholder> {
type Storage = [u8; size_of::<Result<ty::EarlyBinder<'static, Ty<'_>>, CyclePlaceholder>>()];
}
impl EraseType
impl Erasable
for Result<(&'_ [Spanned<MonoItem<'_>>], &'_ [Spanned<MonoItem<'_>>]), NormalizationErrorInMono>
{
type Result = [u8; size_of::<
type Storage = [u8; size_of::<
Result<
(&'static [Spanned<MonoItem<'static>>], &'static [Spanned<MonoItem<'static>>]),
NormalizationErrorInMono,
@ -189,86 +228,89 @@ impl EraseType
>()];
}
impl EraseType for Result<&'_ TokenStream, ()> {
type Result = [u8; size_of::<Result<&'static TokenStream, ()>>()];
impl Erasable for Result<&'_ TokenStream, ()> {
type Storage = [u8; size_of::<Result<&'static TokenStream, ()>>()];
}
impl<T> EraseType for Option<&'_ T> {
type Result = [u8; size_of::<Option<&'static ()>>()];
impl<T> Erasable for Option<&'_ T> {
type Storage = [u8; size_of::<Option<&'static ()>>()];
}
impl<T> EraseType for Option<&'_ [T]> {
type Result = [u8; size_of::<Option<&'static [()]>>()];
impl<T> Erasable for Option<&'_ [T]> {
type Storage = [u8; size_of::<Option<&'static [()]>>()];
}
impl EraseType for Option<&'_ OsStr> {
type Result = [u8; size_of::<Option<&'static OsStr>>()];
impl Erasable for Option<&'_ OsStr> {
type Storage = [u8; size_of::<Option<&'static OsStr>>()];
}
impl EraseType for Option<mir::DestructuredConstant<'_>> {
type Result = [u8; size_of::<Option<mir::DestructuredConstant<'static>>>()];
impl Erasable for Option<mir::DestructuredConstant<'_>> {
type Storage = [u8; size_of::<Option<mir::DestructuredConstant<'static>>>()];
}
impl EraseType for ty::ImplTraitHeader<'_> {
type Result = [u8; size_of::<ty::ImplTraitHeader<'static>>()];
impl Erasable for ty::ImplTraitHeader<'_> {
type Storage = [u8; size_of::<ty::ImplTraitHeader<'static>>()];
}
impl EraseType for Option<ty::EarlyBinder<'_, Ty<'_>>> {
type Result = [u8; size_of::<Option<ty::EarlyBinder<'static, Ty<'static>>>>()];
impl Erasable for Option<ty::EarlyBinder<'_, Ty<'_>>> {
type Storage = [u8; size_of::<Option<ty::EarlyBinder<'static, Ty<'static>>>>()];
}
impl EraseType for rustc_hir::MaybeOwner<'_> {
type Result = [u8; size_of::<rustc_hir::MaybeOwner<'static>>()];
impl Erasable for rustc_hir::MaybeOwner<'_> {
type Storage = [u8; size_of::<rustc_hir::MaybeOwner<'static>>()];
}
impl<T: EraseType> EraseType for ty::EarlyBinder<'_, T> {
type Result = T::Result;
impl<T: Erasable> Erasable for ty::EarlyBinder<'_, T> {
type Storage = T::Storage;
}
impl EraseType for ty::Binder<'_, ty::FnSig<'_>> {
type Result = [u8; size_of::<ty::Binder<'static, ty::FnSig<'static>>>()];
impl Erasable for ty::Binder<'_, ty::FnSig<'_>> {
type Storage = [u8; size_of::<ty::Binder<'static, ty::FnSig<'static>>>()];
}
impl EraseType for ty::Binder<'_, ty::CoroutineWitnessTypes<TyCtxt<'_>>> {
type Result =
impl Erasable for ty::Binder<'_, ty::CoroutineWitnessTypes<TyCtxt<'_>>> {
type Storage =
[u8; size_of::<ty::Binder<'static, ty::CoroutineWitnessTypes<TyCtxt<'static>>>>()];
}
impl EraseType for ty::Binder<'_, &'_ ty::List<Ty<'_>>> {
type Result = [u8; size_of::<ty::Binder<'static, &'static ty::List<Ty<'static>>>>()];
impl Erasable for ty::Binder<'_, &'_ ty::List<Ty<'_>>> {
type Storage = [u8; size_of::<ty::Binder<'static, &'static ty::List<Ty<'static>>>>()];
}
impl<T0, T1> EraseType for (&'_ T0, &'_ T1) {
type Result = [u8; size_of::<(&'static (), &'static ())>()];
impl<T0, T1> Erasable for (&'_ T0, &'_ T1) {
type Storage = [u8; size_of::<(&'static (), &'static ())>()];
}
impl<T0> EraseType for (solve::QueryResult<'_>, &'_ T0) {
type Result = [u8; size_of::<(solve::QueryResult<'static>, &'static ())>()];
impl<T0> Erasable for (solve::QueryResult<'_>, &'_ T0) {
type Storage = [u8; size_of::<(solve::QueryResult<'static>, &'static ())>()];
}
impl<T0, T1> EraseType for (&'_ T0, &'_ [T1]) {
type Result = [u8; size_of::<(&'static (), &'static [()])>()];
impl<T0, T1> Erasable for (&'_ T0, &'_ [T1]) {
type Storage = [u8; size_of::<(&'static (), &'static [()])>()];
}
impl<T0, T1> EraseType for (&'_ [T0], &'_ [T1]) {
type Result = [u8; size_of::<(&'static [()], &'static [()])>()];
impl<T0, T1> Erasable for (&'_ [T0], &'_ [T1]) {
type Storage = [u8; size_of::<(&'static [()], &'static [()])>()];
}
impl<T0> EraseType for (&'_ T0, Result<(), ErrorGuaranteed>) {
type Result = [u8; size_of::<(&'static (), Result<(), ErrorGuaranteed>)>()];
impl<T0> Erasable for (&'_ T0, Result<(), ErrorGuaranteed>) {
type Storage = [u8; size_of::<(&'static (), Result<(), ErrorGuaranteed>)>()];
}
macro_rules! trivial {
macro_rules! impl_erasable_for_simple_types {
($($ty:ty),+ $(,)?) => {
$(
impl EraseType for $ty {
type Result = [u8; size_of::<$ty>()];
impl Erasable for $ty {
type Storage = [u8; size_of::<$ty>()];
}
)*
}
}
trivial! {
// For concrete types with no lifetimes, the erased storage for `Foo` is
// `[u8; size_of::<Foo>()]`.
impl_erasable_for_simple_types! {
// FIXME(#151565): Add `tidy-alphabetical-{start,end}` and sort this.
(),
bool,
Option<(rustc_span::def_id::DefId, rustc_session::config::EntryFnType)>,
@ -346,7 +388,6 @@ trivial! {
rustc_middle::ty::AssocContainer,
rustc_middle::ty::Asyncness,
rustc_middle::ty::AsyncDestructor,
rustc_middle::ty::BoundVariableKind,
rustc_middle::ty::AnonConstKind,
rustc_middle::ty::Destructor,
rustc_middle::ty::fast_reject::SimplifiedType,
@ -378,17 +419,23 @@ trivial! {
usize,
}
macro_rules! tcx_lifetime {
macro_rules! impl_erasable_for_single_lifetime_types {
($($($fake_path:ident)::+),+ $(,)?) => {
$(
impl<'tcx> EraseType for $($fake_path)::+<'tcx> {
type Result = [u8; size_of::<$($fake_path)::+<'static>>()];
impl<'tcx> Erasable for $($fake_path)::+<'tcx> {
type Storage = [u8; size_of::<$($fake_path)::+<'static>>()];
}
)*
}
}
tcx_lifetime! {
// For types containing a single lifetime and no other generics, e.g.
// `Foo<'tcx>`, the erased storage is `[u8; size_of::<Foo<'static>>()]`.
//
// FIXME(#151565): Some of the hand-written impls above that only use one
// lifetime can probably be migrated here.
impl_erasable_for_single_lifetime_types! {
// FIXME(#151565): Add `tidy-alphabetical-{start,end}` and sort this.
rustc_middle::middle::exported_symbols::ExportedSymbol,
rustc_middle::mir::Const,
rustc_middle::mir::DestructuredConstant,
@ -415,6 +462,7 @@ tcx_lifetime! {
rustc_middle::ty::ConstConditions,
rustc_middle::ty::inhabitedness::InhabitedPredicate,
rustc_middle::ty::Instance,
rustc_middle::ty::BoundVariableKind,
rustc_middle::ty::InstanceKind,
rustc_middle::ty::layout::FnAbiError,
rustc_middle::ty::layout::LayoutError,

View file

@ -11,7 +11,7 @@ use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span};
use crate::dep_graph;
use crate::query::IntoQueryParam;
use crate::query::erase::{self, Erase, EraseType};
use crate::query::erase::{self, Erasable, Erased};
use crate::ty::TyCtxt;
/// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)`
@ -63,15 +63,15 @@ pub(crate) fn query_ensure_error_guaranteed<'tcx, Cache, T>(
check_cache: bool,
) -> Result<(), ErrorGuaranteed>
where
Cache: QueryCache<Value = Erase<Result<T, ErrorGuaranteed>>>,
Result<T, ErrorGuaranteed>: EraseType,
Cache: QueryCache<Value = Erased<Result<T, ErrorGuaranteed>>>,
Result<T, ErrorGuaranteed>: Erasable,
{
let key = key.into_query_param();
if let Some(res) = try_get_cached(tcx, query_cache, &key) {
erase::restore(res).map(drop)
erase::restore_val(res).map(drop)
} else {
execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache })
.map(erase::restore)
.map(erase::restore_val)
.map(|res| res.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
@ -90,17 +90,17 @@ pub(crate) fn query_feed<'tcx, Cache, Value>(
hasher: Option<fn(&mut StableHashingContext<'_>, &Value) -> Fingerprint>,
cache: &Cache,
key: Cache::Key,
erased: Erase<Value>,
erased: Erased<Value>,
) where
Cache: QueryCache<Value = Erase<Value>>,
Cache: QueryCache<Value = Erased<Value>>,
Cache::Key: DepNodeParams<TyCtxt<'tcx>>,
Value: EraseType + Debug,
Value: Erasable + Debug,
{
let value = erase::restore::<Value>(erased);
let value = erase::restore_val::<Value>(erased);
match try_get_cached(tcx, cache, &key) {
Some(old) => {
let old = erase::restore::<Value>(old);
let old = erase::restore_val::<Value>(old);
if let Some(hasher) = hasher {
let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx
.with_stable_hashing_context(|mut hcx| {

View file

@ -121,8 +121,7 @@ use crate::mir::interpret::{
use crate::mir::mono::{
CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono,
};
use crate::query::erase::{Erase, erase, restore};
use crate::query::plumbing::{CyclePlaceholder, DynamicQuery};
use crate::query::plumbing::CyclePlaceholder;
use crate::traits::query::{
CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal,
CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal,
@ -2116,7 +2115,7 @@ rustc_queries! {
/// Does lifetime resolution on items. Importantly, we can't resolve
/// lifetimes directly on things like trait methods, because of trait params.
/// See `rustc_resolve::late::lifetimes` for details.
query resolve_bound_vars(owner_id: hir::OwnerId) -> &'tcx ResolveBoundVars {
query resolve_bound_vars(owner_id: hir::OwnerId) -> &'tcx ResolveBoundVars<'tcx> {
arena_cache
desc { |tcx| "resolving lifetimes for `{}`", tcx.def_path_str(owner_id) }
}
@ -2145,7 +2144,7 @@ rustc_queries! {
separate_provide_extern
}
query late_bound_vars_map(owner_id: hir::OwnerId)
-> &'tcx SortedMap<ItemLocalId, Vec<ty::BoundVariableKind>> {
-> &'tcx SortedMap<ItemLocalId, Vec<ty::BoundVariableKind<'tcx>>> {
desc { |tcx| "looking up late bound vars inside `{}`", tcx.def_path_str(owner_id) }
}
/// For an opaque type, return the list of (captured lifetime, inner generic param).

View file

@ -14,11 +14,14 @@ use crate::dep_graph;
use crate::dep_graph::DepKind;
use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache};
use crate::query::{
DynamicQueries, ExternProviders, Providers, QueryArenas, QueryCaches, QueryEngine, QueryStates,
ExternProviders, PerQueryVTables, Providers, QueryArenas, QueryCaches, QueryEngine, QueryStates,
};
use crate::ty::TyCtxt;
pub struct DynamicQuery<'tcx, C: QueryCache> {
/// Stores function pointers and other metadata for a particular query.
///
/// Used indirectly by query plumbing in `rustc_query_system`, via a trait.
pub struct QueryVTable<'tcx, C: QueryCache> {
pub name: &'static str,
pub eval_always: bool,
pub dep_kind: DepKind,
@ -62,7 +65,7 @@ pub struct QuerySystem<'tcx> {
pub states: QueryStates<'tcx>,
pub arenas: WorkerLocal<QueryArenas<'tcx>>,
pub caches: QueryCaches<'tcx>,
pub dynamic_queries: DynamicQueries<'tcx>,
pub query_vtables: PerQueryVTables<'tcx>,
/// This provides access to the incremental compilation on-disk cache for query results.
/// Do not access this directly. It is only meant to be used by
@ -263,6 +266,7 @@ macro_rules! define_callbacks {
pub mod queries {
$(pub mod $name {
use super::super::*;
use $crate::query::erase::{self, Erased};
pub type Key<'tcx> = $($K)*;
pub type Value<'tcx> = $V;
@ -285,29 +289,33 @@ macro_rules! define_callbacks {
#[inline(always)]
pub fn provided_to_erased<'tcx>(
_tcx: TyCtxt<'tcx>,
value: ProvidedValue<'tcx>,
) -> Erase<Value<'tcx>> {
erase(query_if_arena!([$($modifiers)*]
provided_value: ProvidedValue<'tcx>,
) -> Erased<Value<'tcx>> {
// Store the provided value in an arena and get a reference
// to it, for queries with `arena_cache`.
let value: Value<'tcx> = query_if_arena!([$($modifiers)*]
{
use $crate::query::arena_cached::ArenaCached;
if mem::needs_drop::<<$V as ArenaCached<'tcx>>::Allocated>() {
<$V as ArenaCached>::alloc_in_arena(
|v| _tcx.query_system.arenas.$name.alloc(v),
value,
provided_value,
)
} else {
<$V as ArenaCached>::alloc_in_arena(
|v| _tcx.arena.dropless.alloc(v),
value,
provided_value,
)
}
}
(value)
))
// Otherwise, the provided value is the value.
(provided_value)
);
erase::erase_val(value)
}
pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache<Erase<$V>>;
pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache<Erased<$V>>;
// 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
@ -408,7 +416,9 @@ macro_rules! define_callbacks {
#[inline(always)]
pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V
{
restore::<$V>(crate::query::inner::query_get_at(
use $crate::query::{erase, inner};
erase::restore_val::<$V>(inner::query_get_at(
self.tcx,
self.tcx.query_system.fns.engine.$name,
&self.tcx.query_system.caches.$name,
@ -418,9 +428,12 @@ macro_rules! define_callbacks {
})*
}
pub struct DynamicQueries<'tcx> {
/// Holds a `QueryVTable` for each query.
///
/// ("Per" just makes this pluralized name more visually distinct.)
pub struct PerQueryVTables<'tcx> {
$(
pub $name: DynamicQuery<'tcx, queries::$name::Storage<'tcx>>,
pub $name: ::rustc_middle::query::plumbing::QueryVTable<'tcx, queries::$name::Storage<'tcx>>,
)*
}
@ -474,7 +487,7 @@ macro_rules! define_callbacks {
Span,
queries::$name::Key<'tcx>,
QueryMode,
) -> Option<Erase<$V>>,)*
) -> Option<$crate::query::erase::Erased<$V>>,)*
}
};
}

View file

@ -427,11 +427,11 @@ impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for [Spanned<MonoItem<'tcx>
}
}
impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List<ty::BoundVariableKind> {
impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::List<ty::BoundVariableKind<'tcx>> {
fn decode(decoder: &mut D) -> &'tcx Self {
let len = decoder.read_usize();
decoder.interner().mk_bound_variable_kinds_from_iter(
(0..len).map::<ty::BoundVariableKind, _>(|_| Decodable::decode(decoder)),
(0..len).map::<ty::BoundVariableKind<'tcx>, _>(|_| Decodable::decode(decoder)),
)
}
}
@ -495,7 +495,7 @@ impl_decodable_via_ref! {
&'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
&'tcx traits::ImplSource<'tcx, ()>,
&'tcx mir::Body<'tcx>,
&'tcx ty::List<ty::BoundVariableKind>,
&'tcx ty::List<ty::BoundVariableKind<'tcx>>,
&'tcx ty::List<ty::Pattern<'tcx>>,
&'tcx ty::ListWithCachedTypeInfo<ty::Clause<'tcx>>,
}

View file

@ -94,7 +94,7 @@ impl<'tcx> Const<'tcx> {
pub fn new_bound(
tcx: TyCtxt<'tcx>,
debruijn: ty::DebruijnIndex,
bound_const: ty::BoundConst,
bound_const: ty::BoundConst<'tcx>,
) -> Const<'tcx> {
Const::new(tcx, ty::ConstKind::Bound(ty::BoundVarIndexKind::Bound(debruijn), bound_const))
}
@ -103,7 +103,7 @@ impl<'tcx> Const<'tcx> {
pub fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: ty::BoundVar) -> Const<'tcx> {
Const::new(
tcx,
ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, ty::BoundConst { var }),
ty::ConstKind::Bound(ty::BoundVarIndexKind::Canonical, ty::BoundConst::new(var)),
)
}
@ -183,13 +183,13 @@ impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> {
fn new_bound(
interner: TyCtxt<'tcx>,
debruijn: ty::DebruijnIndex,
bound_const: ty::BoundConst,
bound_const: ty::BoundConst<'tcx>,
) -> Self {
Const::new_bound(interner, debruijn, bound_const)
}
fn new_anon_bound(tcx: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self {
Const::new_bound(tcx, debruijn, ty::BoundConst { var })
Const::new_bound(tcx, debruijn, ty::BoundConst::new(var))
}
fn new_canonical_bound(tcx: TyCtxt<'tcx>, var: rustc_type_ir::BoundVar) -> Self {

View file

@ -108,9 +108,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type GenericArgsSlice = &'tcx [ty::GenericArg<'tcx>];
type GenericArg = ty::GenericArg<'tcx>;
type Term = ty::Term<'tcx>;
type BoundVarKinds = &'tcx List<ty::BoundVariableKind>;
type BoundVarKinds = &'tcx List<ty::BoundVariableKind<'tcx>>;
type BoundVarKind = ty::BoundVariableKind;
type PredefinedOpaques = solve::PredefinedOpaques<'tcx>;
fn mk_predefined_opaques_in_body(
@ -144,10 +143,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type FnInputTys = &'tcx [Ty<'tcx>];
type ParamTy = ParamTy;
type BoundTy = ty::BoundTy;
type Symbol = Symbol;
type PlaceholderTy = ty::PlaceholderType<'tcx>;
type ErrorGuaranteed = ErrorGuaranteed;
type BoundExistentialPredicates = &'tcx List<PolyExistentialPredicate<'tcx>>;
@ -157,10 +154,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type Safety = hir::Safety;
type Abi = ExternAbi;
type Const = ty::Const<'tcx>;
type PlaceholderConst = ty::PlaceholderConst<'tcx>;
type ParamConst = ty::ParamConst;
type BoundConst = ty::BoundConst;
type ValueConst = ty::Value<'tcx>;
type ExprConst = ty::Expr<'tcx>;
type ValTree = ty::ValTree<'tcx>;
@ -169,8 +164,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type Region = Region<'tcx>;
type EarlyParamRegion = ty::EarlyParamRegion;
type LateParamRegion = ty::LateParamRegion;
type BoundRegion = ty::BoundRegion;
type PlaceholderRegion = ty::PlaceholderRegion<'tcx>;
type RegionAssumptions = &'tcx ty::List<ty::ArgOutlivesPredicate<'tcx>>;
@ -776,6 +769,13 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
) -> (QueryResult<'tcx>, &'tcx inspect::Probe<TyCtxt<'tcx>>) {
self.evaluate_root_goal_for_proof_tree_raw(canonical_goal)
}
fn item_name(self, id: DefId) -> Symbol {
let id = id.into_query_param();
self.opt_item_name(id).unwrap_or_else(|| {
bug!("item_name: no name for {:?}", self.def_path(id));
})
}
}
macro_rules! bidirectional_lang_item_map {
@ -938,7 +938,7 @@ pub struct CtxtInterners<'tcx> {
const_: InternedSet<'tcx, WithCachedTypeInfo<ty::ConstKind<'tcx>>>,
pat: InternedSet<'tcx, PatternKind<'tcx>>,
const_allocation: InternedSet<'tcx, Allocation>,
bound_variable_kinds: InternedSet<'tcx, List<ty::BoundVariableKind>>,
bound_variable_kinds: InternedSet<'tcx, List<ty::BoundVariableKind<'tcx>>>,
layout: InternedSet<'tcx, LayoutData<FieldIdx, VariantIdx>>,
adt_def: InternedSet<'tcx, AdtDefData>,
external_constraints: InternedSet<'tcx, ExternalConstraintsData<TyCtxt<'tcx>>>,
@ -2530,7 +2530,7 @@ nop_list_lift! { type_lists; Ty<'a> => Ty<'tcx> }
nop_list_lift! {
poly_existential_predicates; PolyExistentialPredicate<'a> => PolyExistentialPredicate<'tcx>
}
nop_list_lift! { bound_variable_kinds; ty::BoundVariableKind => ty::BoundVariableKind }
nop_list_lift! { bound_variable_kinds; ty::BoundVariableKind<'a> => ty::BoundVariableKind<'tcx> }
// This is the impl for `&'a GenericArgs<'a>`.
nop_list_lift! { args; GenericArg<'a> => GenericArg<'tcx> }
@ -2817,7 +2817,7 @@ slice_interners!(
poly_existential_predicates: intern_poly_existential_predicates(PolyExistentialPredicate<'tcx>),
projs: pub mk_projs(ProjectionKind),
place_elems: pub mk_place_elems(PlaceElem<'tcx>),
bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind),
bound_variable_kinds: pub mk_bound_variable_kinds(ty::BoundVariableKind<'tcx>),
fields: pub mk_fields(FieldIdx),
local_def_ids: intern_local_def_ids(LocalDefId),
captures: intern_captures(&'tcx ty::CapturedPlace<'tcx>),
@ -3242,7 +3242,7 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn mk_bound_variable_kinds_from_iter<I, T>(self, iter: I) -> T::Output
where
I: Iterator<Item = T>,
T: CollectAndApply<ty::BoundVariableKind, &'tcx List<ty::BoundVariableKind>>,
T: CollectAndApply<ty::BoundVariableKind<'tcx>, &'tcx List<ty::BoundVariableKind<'tcx>>>,
{
T::collect_and_apply(iter, |xs| self.mk_bound_variable_kinds(xs))
}
@ -3362,7 +3362,7 @@ impl<'tcx> TyCtxt<'tcx> {
self.is_late_bound_map(id.owner).is_some_and(|set| set.contains(&id.local_id))
}
pub fn late_bound_vars(self, id: HirId) -> &'tcx List<ty::BoundVariableKind> {
pub fn late_bound_vars(self, id: HirId) -> &'tcx List<ty::BoundVariableKind<'tcx>> {
self.mk_bound_variable_kinds(
&self
.late_bound_vars_map(id.owner)

View file

@ -3,7 +3,7 @@ use rustc_hir::def_id::DefId;
use rustc_type_ir::data_structures::DelayedMap;
use crate::ty::{
self, Binder, BoundConst, BoundTy, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
self, Binder, BoundTy, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
TypeVisitableExt,
};
@ -58,28 +58,28 @@ where
/// gets mapped to the same result. `BoundVarReplacer` caches by using
/// a `DelayedMap` which does not cache the first few types it encounters.
pub trait BoundVarReplacerDelegate<'tcx> {
fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx>;
fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx>;
fn replace_const(&mut self, bc: ty::BoundConst) -> ty::Const<'tcx>;
fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx>;
fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx>;
fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx>;
}
/// A simple delegate taking 3 mutable functions. The used functions must
/// always return the same result for each bound variable, no matter how
/// frequently they are called.
pub struct FnMutDelegate<'a, 'tcx> {
pub regions: &'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a),
pub types: &'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a),
pub consts: &'a mut (dyn FnMut(ty::BoundConst) -> ty::Const<'tcx> + 'a),
pub regions: &'a mut (dyn FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx> + 'a),
pub types: &'a mut (dyn FnMut(ty::BoundTy<'tcx>) -> Ty<'tcx> + 'a),
pub consts: &'a mut (dyn FnMut(ty::BoundConst<'tcx>) -> ty::Const<'tcx> + 'a),
}
impl<'a, 'tcx> BoundVarReplacerDelegate<'tcx> for FnMutDelegate<'a, 'tcx> {
fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> {
(self.regions)(br)
}
fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx> {
(self.types)(bt)
}
fn replace_const(&mut self, bc: ty::BoundConst) -> ty::Const<'tcx> {
fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx> {
(self.consts)(bc)
}
}
@ -207,13 +207,14 @@ impl<'tcx> TyCtxt<'tcx> {
self,
value: Binder<'tcx, T>,
mut fld_r: F,
) -> (T, FxIndexMap<ty::BoundRegion, ty::Region<'tcx>>)
) -> (T, FxIndexMap<ty::BoundRegion<'tcx>, ty::Region<'tcx>>)
where
F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
F: FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx>,
T: TypeFoldable<TyCtxt<'tcx>>,
{
let mut region_map = FxIndexMap::default();
let real_fld_r = |br: ty::BoundRegion| *region_map.entry(br).or_insert_with(|| fld_r(br));
let real_fld_r =
|br: ty::BoundRegion<'tcx>| *region_map.entry(br).or_insert_with(|| fld_r(br));
let value = self.instantiate_bound_regions_uncached(value, real_fld_r);
(value, region_map)
}
@ -224,7 +225,7 @@ impl<'tcx> TyCtxt<'tcx> {
mut replace_regions: F,
) -> T
where
F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
F: FnMut(ty::BoundRegion<'tcx>) -> ty::Region<'tcx>,
T: TypeFoldable<TyCtxt<'tcx>>,
{
let value = value.skip_binder();
@ -292,14 +293,14 @@ impl<'tcx> TyCtxt<'tcx> {
self.replace_escaping_bound_vars_uncached(
value,
FnMutDelegate {
regions: &mut |r: ty::BoundRegion| {
regions: &mut |r: ty::BoundRegion<'tcx>| {
ty::Region::new_bound(
self,
ty::INNERMOST,
ty::BoundRegion { var: shift_bv(r.var), kind: r.kind },
)
},
types: &mut |t: ty::BoundTy| {
types: &mut |t: ty::BoundTy<'tcx>| {
Ty::new_bound(
self,
ty::INNERMOST,
@ -307,11 +308,7 @@ impl<'tcx> TyCtxt<'tcx> {
)
},
consts: &mut |c| {
ty::Const::new_bound(
self,
ty::INNERMOST,
ty::BoundConst { var: shift_bv(c.var) },
)
ty::Const::new_bound(self, ty::INNERMOST, ty::BoundConst::new(shift_bv(c.var)))
},
},
)
@ -333,10 +330,10 @@ impl<'tcx> TyCtxt<'tcx> {
{
struct Anonymize<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
map: &'a mut FxIndexMap<ty::BoundVar, ty::BoundVariableKind>,
map: &'a mut FxIndexMap<ty::BoundVar, ty::BoundVariableKind<'tcx>>,
}
impl<'tcx> BoundVarReplacerDelegate<'tcx> for Anonymize<'_, 'tcx> {
fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> {
fn replace_region(&mut self, br: ty::BoundRegion<'tcx>) -> ty::Region<'tcx> {
let entry = self.map.entry(br.var);
let index = entry.index();
let var = ty::BoundVar::from_usize(index);
@ -346,7 +343,7 @@ impl<'tcx> TyCtxt<'tcx> {
let br = ty::BoundRegion { var, kind };
ty::Region::new_bound(self.tcx, ty::INNERMOST, br)
}
fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> {
fn replace_ty(&mut self, bt: ty::BoundTy<'tcx>) -> Ty<'tcx> {
let entry = self.map.entry(bt.var);
let index = entry.index();
let var = ty::BoundVar::from_usize(index);
@ -355,12 +352,12 @@ impl<'tcx> TyCtxt<'tcx> {
.expect_ty();
Ty::new_bound(self.tcx, ty::INNERMOST, BoundTy { var, kind })
}
fn replace_const(&mut self, bc: ty::BoundConst) -> ty::Const<'tcx> {
fn replace_const(&mut self, bc: ty::BoundConst<'tcx>) -> ty::Const<'tcx> {
let entry = self.map.entry(bc.var);
let index = entry.index();
let var = ty::BoundVar::from_usize(index);
let () = entry.or_insert_with(|| ty::BoundVariableKind::Const).expect_const();
ty::Const::new_bound(self.tcx, ty::INNERMOST, BoundConst { var })
ty::Const::new_bound(self.tcx, ty::INNERMOST, ty::BoundConst::new(var))
}
}

View file

@ -97,13 +97,13 @@ pub use self::predicate::{
RegionOutlivesPredicate, SubtypePredicate, TraitPredicate, TraitRef, TypeOutlivesPredicate,
};
pub use self::region::{
BoundRegion, BoundRegionKind, EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region,
RegionKind, RegionVid,
EarlyParamRegion, LateParamRegion, LateParamRegionKind, Region, RegionKind, RegionVid,
};
pub use self::sty::{
AliasTy, Article, Binder, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig,
CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs, InlineConstArgsParts, ParamConst,
ParamTy, PolyFnSig, TyKind, TypeAndMut, TypingMode, UpvarArgs,
AliasTy, Article, Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind,
BoundVariableKind, CanonicalPolyFnSig, CoroutineArgsExt, EarlyBinder, FnSig, InlineConstArgs,
InlineConstArgsParts, ParamConst, ParamTy, PlaceholderConst, PlaceholderRegion,
PlaceholderType, PolyFnSig, TyKind, TypeAndMut, TypingMode, UpvarArgs,
};
pub use self::trait_def::TraitDef;
pub use self::typeck_results::{
@ -914,100 +914,6 @@ impl<'tcx> DefinitionSiteHiddenType<'tcx> {
}
}
pub type PlaceholderRegion<'tcx> = ty::Placeholder<TyCtxt<'tcx>, BoundRegion>;
impl<'tcx> rustc_type_ir::inherent::PlaceholderLike<TyCtxt<'tcx>> for PlaceholderRegion<'tcx> {
type Bound = BoundRegion;
fn universe(self) -> UniverseIndex {
self.universe
}
fn var(self) -> BoundVar {
self.bound.var
}
fn with_updated_universe(self, ui: UniverseIndex) -> Self {
ty::Placeholder::new(ui, self.bound)
}
fn new(ui: UniverseIndex, bound: BoundRegion) -> Self {
ty::Placeholder::new(ui, bound)
}
fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self {
ty::Placeholder::new(ui, BoundRegion { var, kind: BoundRegionKind::Anon })
}
}
pub type PlaceholderType<'tcx> = ty::Placeholder<TyCtxt<'tcx>, BoundTy>;
impl<'tcx> rustc_type_ir::inherent::PlaceholderLike<TyCtxt<'tcx>> for PlaceholderType<'tcx> {
type Bound = BoundTy;
fn universe(self) -> UniverseIndex {
self.universe
}
fn var(self) -> BoundVar {
self.bound.var
}
fn with_updated_universe(self, ui: UniverseIndex) -> Self {
ty::Placeholder::new(ui, self.bound)
}
fn new(ui: UniverseIndex, bound: BoundTy) -> Self {
ty::Placeholder::new(ui, bound)
}
fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self {
ty::Placeholder::new(ui, BoundTy { var, kind: BoundTyKind::Anon })
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
#[derive(TyEncodable, TyDecodable)]
pub struct BoundConst {
pub var: BoundVar,
}
impl<'tcx> rustc_type_ir::inherent::BoundVarLike<TyCtxt<'tcx>> for BoundConst {
fn var(self) -> BoundVar {
self.var
}
fn assert_eq(self, var: ty::BoundVariableKind) {
var.expect_const()
}
}
pub type PlaceholderConst<'tcx> = ty::Placeholder<TyCtxt<'tcx>, BoundConst>;
impl<'tcx> rustc_type_ir::inherent::PlaceholderLike<TyCtxt<'tcx>> for PlaceholderConst<'tcx> {
type Bound = BoundConst;
fn universe(self) -> UniverseIndex {
self.universe
}
fn var(self) -> BoundVar {
self.bound.var
}
fn with_updated_universe(self, ui: UniverseIndex) -> Self {
ty::Placeholder::new(ui, self.bound)
}
fn new(ui: UniverseIndex, bound: BoundConst) -> Self {
ty::Placeholder::new(ui, bound)
}
fn new_anon(ui: UniverseIndex, var: BoundVar) -> Self {
ty::Placeholder::new(ui, BoundConst { var })
}
}
pub type Clauses<'tcx> = &'tcx ListWithCachedTypeInfo<Clause<'tcx>>;
impl<'tcx> rustc_type_ir::Flags for Clauses<'tcx> {

View file

@ -197,7 +197,7 @@ pub struct RegionHighlightMode<'tcx> {
/// This is used when you have a signature like `fn foo(x: &u32,
/// y: &'a u32)` and we want to give a name to the region of the
/// reference `x`.
highlight_bound_region: Option<(ty::BoundRegionKind, usize)>,
highlight_bound_region: Option<(ty::BoundRegionKind<'tcx>, usize)>,
}
impl<'tcx> RegionHighlightMode<'tcx> {
@ -246,7 +246,7 @@ impl<'tcx> RegionHighlightMode<'tcx> {
/// Highlight the given bound region.
/// We can only highlight one bound region at a time. See
/// the field `highlight_bound_region` for more detailed notes.
pub fn highlighting_bound_region(&mut self, br: ty::BoundRegionKind, number: usize) {
pub fn highlighting_bound_region(&mut self, br: ty::BoundRegionKind<'tcx>, number: usize) {
assert!(self.highlight_bound_region.is_none());
self.highlight_bound_region = Some((br, number));
}
@ -2639,12 +2639,12 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
struct RegionFolder<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
current_index: ty::DebruijnIndex,
region_map: UnordMap<ty::BoundRegion, ty::Region<'tcx>>,
region_map: UnordMap<ty::BoundRegion<'tcx>, ty::Region<'tcx>>,
name: &'a mut (
dyn FnMut(
Option<ty::DebruijnIndex>, // Debruijn index of the folded late-bound region
ty::DebruijnIndex, // Index corresponding to binder level
ty::BoundRegion,
ty::BoundRegion<'tcx>,
) -> ty::Region<'tcx>
+ 'a
),
@ -2717,7 +2717,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
&mut self,
value: &ty::Binder<'tcx, T>,
mode: WrapBinderMode,
) -> Result<(T, UnordMap<ty::BoundRegion, ty::Region<'tcx>>), fmt::Error>
) -> Result<(T, UnordMap<ty::BoundRegion<'tcx>, ty::Region<'tcx>>), fmt::Error>
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
@ -2810,12 +2810,12 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
// see issue #102392.
let mut name = |lifetime_idx: Option<ty::DebruijnIndex>,
binder_level_idx: ty::DebruijnIndex,
br: ty::BoundRegion| {
br: ty::BoundRegion<'tcx>| {
let (name, kind) = if let Some(name) = br.kind.get_name(tcx) {
(name, br.kind)
} else {
let name = next_name(self);
(name, ty::BoundRegionKind::NamedAnon(name))
(name, ty::BoundRegionKind::NamedForPrinting(name))
};
if let Some(lt_idx) = lifetime_idx {

View file

@ -50,7 +50,7 @@ impl<'tcx> Region<'tcx> {
pub fn new_bound(
tcx: TyCtxt<'tcx>,
debruijn: ty::DebruijnIndex,
bound_region: ty::BoundRegion,
bound_region: ty::BoundRegion<'tcx>,
) -> Region<'tcx> {
// Use a pre-interned one when possible.
if let ty::BoundRegion { var, kind: ty::BoundRegionKind::Anon } = bound_region
@ -160,7 +160,7 @@ impl<'tcx> rustc_type_ir::inherent::Region<TyCtxt<'tcx>> for Region<'tcx> {
fn new_bound(
interner: TyCtxt<'tcx>,
debruijn: ty::DebruijnIndex,
var: ty::BoundRegion,
var: ty::BoundRegion<'tcx>,
) -> Self {
Region::new_bound(interner, debruijn, var)
}
@ -388,7 +388,7 @@ pub struct LateParamRegion {
pub kind: LateParamRegionKind,
}
/// When liberating bound regions, we map their [`BoundRegionKind`]
/// When liberating bound regions, we map their [`ty::BoundRegionKind`]
/// to this as we need to track the index of anonymous regions. We
/// otherwise end up liberating multiple bound regions to the same
/// late-bound region.
@ -397,7 +397,7 @@ pub struct LateParamRegion {
pub enum LateParamRegionKind {
/// An anonymous region parameter for a given fn (&T)
///
/// Unlike [`BoundRegionKind::Anon`], this tracks the index of the
/// Unlike [`ty::BoundRegionKind::Anon`], this tracks the index of the
/// liberated bound region.
///
/// We should ideally never liberate anonymous regions, but do so for the
@ -418,12 +418,14 @@ pub enum LateParamRegionKind {
}
impl LateParamRegionKind {
pub fn from_bound(var: BoundVar, br: BoundRegionKind) -> LateParamRegionKind {
pub fn from_bound(var: BoundVar, br: ty::BoundRegionKind<'_>) -> LateParamRegionKind {
match br {
BoundRegionKind::Anon => LateParamRegionKind::Anon(var.as_u32()),
BoundRegionKind::Named(def_id) => LateParamRegionKind::Named(def_id),
BoundRegionKind::ClosureEnv => LateParamRegionKind::ClosureEnv,
BoundRegionKind::NamedAnon(name) => LateParamRegionKind::NamedAnon(var.as_u32(), name),
ty::BoundRegionKind::Anon => LateParamRegionKind::Anon(var.as_u32()),
ty::BoundRegionKind::Named(def_id) => LateParamRegionKind::Named(def_id),
ty::BoundRegionKind::ClosureEnv => LateParamRegionKind::ClosureEnv,
ty::BoundRegionKind::NamedForPrinting(name) => {
LateParamRegionKind::NamedAnon(var.as_u32(), name)
}
}
}
@ -450,81 +452,6 @@ impl LateParamRegionKind {
}
}
#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Copy)]
#[derive(HashStable)]
pub enum BoundRegionKind {
/// An anonymous region parameter for a given fn (&T)
Anon,
/// An anonymous region parameter with a `Symbol` name.
///
/// Used to give late-bound regions names for things like pretty printing.
NamedAnon(Symbol),
/// Late-bound regions that appear in the AST.
Named(DefId),
/// Anonymous region for the implicit env pointer parameter
/// to a closure
ClosureEnv,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable)]
pub struct BoundRegion {
pub var: BoundVar,
pub kind: BoundRegionKind,
}
impl<'tcx> rustc_type_ir::inherent::BoundVarLike<TyCtxt<'tcx>> for BoundRegion {
fn var(self) -> BoundVar {
self.var
}
fn assert_eq(self, var: ty::BoundVariableKind) {
assert_eq!(self.kind, var.expect_region())
}
}
impl core::fmt::Debug for BoundRegion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.kind {
BoundRegionKind::Anon => write!(f, "{:?}", self.var),
BoundRegionKind::ClosureEnv => write!(f, "{:?}.Env", self.var),
BoundRegionKind::Named(def) => {
write!(f, "{:?}.Named({:?})", self.var, def)
}
BoundRegionKind::NamedAnon(symbol) => {
write!(f, "{:?}.NamedAnon({:?})", self.var, symbol)
}
}
}
}
impl BoundRegionKind {
pub fn is_named(&self, tcx: TyCtxt<'_>) -> bool {
self.get_name(tcx).is_some()
}
pub fn get_name(&self, tcx: TyCtxt<'_>) -> Option<Symbol> {
match *self {
BoundRegionKind::Named(def_id) => {
let name = tcx.item_name(def_id);
if name != kw::UnderscoreLifetime { Some(name) } else { None }
}
BoundRegionKind::NamedAnon(name) => Some(name),
_ => None,
}
}
pub fn get_id(&self) -> Option<DefId> {
match *self {
BoundRegionKind::Named(id) => Some(id),
_ => None,
}
}
}
// Some types are used a lot. Make sure they don't unintentionally get bigger.
#[cfg(target_pointer_width = "64")]
mod size_asserts {

View file

@ -65,21 +65,6 @@ impl<'tcx> fmt::Debug for ty::adjustment::PatAdjustment<'tcx> {
}
}
impl fmt::Debug for ty::BoundRegionKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ty::BoundRegionKind::Anon => write!(f, "BrAnon"),
ty::BoundRegionKind::NamedAnon(name) => {
write!(f, "BrNamedAnon({name})")
}
ty::BoundRegionKind::Named(did) => {
write!(f, "BrNamed({did:?})")
}
ty::BoundRegionKind::ClosureEnv => write!(f, "BrEnv"),
}
}
}
impl fmt::Debug for ty::LateParamRegion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ReLateParam({:?}, {:?})", self.scope, self.kind)
@ -175,15 +160,6 @@ impl<'tcx> fmt::Debug for ty::Const<'tcx> {
}
}
impl fmt::Debug for ty::BoundTy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
ty::BoundTyKind::Anon => write!(f, "{:?}", self.var),
ty::BoundTyKind::Param(def_id) => write!(f, "{def_id:?}"),
}
}
}
impl<'tcx> fmt::Debug for GenericArg<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind() {
@ -255,7 +231,8 @@ TrivialTypeTraversalImpls! {
crate::ty::AdtKind,
crate::ty::AssocItem,
crate::ty::AssocKind,
crate::ty::BoundRegion,
crate::ty::BoundRegion<'tcx>,
crate::ty::BoundTy<'tcx>,
crate::ty::ScalarInt,
crate::ty::UserTypeAnnotationIndex,
crate::ty::abstract_const::NotConstEvaluatable,
@ -284,7 +261,6 @@ TrivialTypeTraversalImpls! {
TrivialTypeTraversalAndLiftImpls! {
// tidy-alphabetical-start
crate::mir::RuntimeChecks,
crate::ty::BoundTy,
crate::ty::ParamTy,
crate::ty::instance::ReifyReason,
rustc_hir::def_id::DefId,

View file

@ -13,7 +13,7 @@ use rustc_hir as hir;
use rustc_hir::LangItem;
use rustc_hir::def_id::DefId;
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, extension};
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
use rustc_span::{DUMMY_SP, Span, Symbol, kw, sym};
use rustc_type_ir::TyKind::*;
use rustc_type_ir::solve::SizedTraitKind;
use rustc_type_ir::walk::TypeWalker;
@ -26,8 +26,8 @@ use crate::infer::canonical::Canonical;
use crate::traits::ObligationCause;
use crate::ty::InferTy::*;
use crate::ty::{
self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv,
Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy,
self, AdtDef, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, Region, Ty,
TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy,
};
// Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here
@ -40,6 +40,15 @@ pub type Binder<'tcx, T> = ir::Binder<TyCtxt<'tcx>, T>;
pub type EarlyBinder<'tcx, T> = ir::EarlyBinder<TyCtxt<'tcx>, T>;
pub type TypingMode<'tcx> = ir::TypingMode<TyCtxt<'tcx>>;
pub type Placeholder<'tcx, T> = ir::Placeholder<TyCtxt<'tcx>, T>;
pub type PlaceholderRegion<'tcx> = ir::PlaceholderRegion<TyCtxt<'tcx>>;
pub type PlaceholderType<'tcx> = ir::PlaceholderType<TyCtxt<'tcx>>;
pub type PlaceholderConst<'tcx> = ir::PlaceholderConst<TyCtxt<'tcx>>;
pub type BoundTy<'tcx> = ir::BoundTy<TyCtxt<'tcx>>;
pub type BoundConst<'tcx> = ir::BoundConst<TyCtxt<'tcx>>;
pub type BoundRegion<'tcx> = ir::BoundRegion<TyCtxt<'tcx>>;
pub type BoundVariableKind<'tcx> = ir::BoundVariableKind<TyCtxt<'tcx>>;
pub type BoundRegionKind<'tcx> = ir::BoundRegionKind<TyCtxt<'tcx>>;
pub type BoundTyKind<'tcx> = ir::BoundTyKind<TyCtxt<'tcx>>;
pub trait Article {
fn article(&self) -> &'static str;
@ -257,37 +266,6 @@ impl<'tcx> InlineConstArgs<'tcx> {
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
#[derive(HashStable)]
pub enum BoundVariableKind {
Ty(BoundTyKind),
Region(BoundRegionKind),
Const,
}
impl BoundVariableKind {
pub fn expect_region(self) -> BoundRegionKind {
match self {
BoundVariableKind::Region(lt) => lt,
_ => bug!("expected a region, but found another kind"),
}
}
pub fn expect_ty(self) -> BoundTyKind {
match self {
BoundVariableKind::Ty(ty) => ty,
_ => bug!("expected a type, but found another kind"),
}
}
pub fn expect_const(self) {
match self {
BoundVariableKind::Const => (),
_ => bug!("expected a const, but found another kind"),
}
}
}
pub type PolyFnSig<'tcx> = Binder<'tcx, FnSig<'tcx>>;
pub type CanonicalPolyFnSig<'tcx> = Canonical<'tcx, Binder<'tcx, FnSig<'tcx>>>;
@ -381,30 +359,6 @@ impl ParamConst {
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable)]
pub struct BoundTy {
pub var: BoundVar,
pub kind: BoundTyKind,
}
impl<'tcx> rustc_type_ir::inherent::BoundVarLike<TyCtxt<'tcx>> for BoundTy {
fn var(self) -> BoundVar {
self.var
}
fn assert_eq(self, var: ty::BoundVariableKind) {
assert_eq!(self.kind, var.expect_ty())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
#[derive(HashStable)]
pub enum BoundTyKind {
Anon,
Param(DefId),
}
/// Constructors for `Ty`
impl<'tcx> Ty<'tcx> {
/// Avoid using this in favour of more specific `new_*` methods, where possible.
@ -479,7 +433,7 @@ impl<'tcx> Ty<'tcx> {
pub fn new_bound(
tcx: TyCtxt<'tcx>,
index: ty::DebruijnIndex,
bound_ty: ty::BoundTy,
bound_ty: ty::BoundTy<'tcx>,
) -> Ty<'tcx> {
// Use a pre-interned one when possible.
if let ty::BoundTy { var, kind: ty::BoundTyKind::Anon } = bound_ty
@ -961,7 +915,11 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
Ty::new_placeholder(tcx, placeholder)
}
fn new_bound(interner: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundTy) -> Self {
fn new_bound(
interner: TyCtxt<'tcx>,
debruijn: ty::DebruijnIndex,
var: ty::BoundTy<'tcx>,
) -> Self {
Ty::new_bound(interner, debruijn, var)
}
@ -2135,6 +2093,12 @@ impl<'tcx> rustc_type_ir::inherent::Tys<TyCtxt<'tcx>> for &'tcx ty::List<Ty<'tcx
}
}
impl<'tcx> rustc_type_ir::inherent::Symbol<TyCtxt<'tcx>> for Symbol {
fn is_kw_underscore_lifetime(self) -> bool {
self == kw::UnderscoreLifetime
}
}
// Some types are used a lot. Make sure they don't unintentionally get bigger.
#[cfg(target_pointer_width = "64")]
mod size_asserts {

View file

@ -113,7 +113,7 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn collect_constrained_late_bound_regions<T>(
self,
value: Binder<'tcx, T>,
) -> FxIndexSet<ty::BoundRegionKind>
) -> FxIndexSet<ty::BoundRegionKind<'tcx>>
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
@ -124,7 +124,7 @@ impl<'tcx> TyCtxt<'tcx> {
pub fn collect_referenced_late_bound_regions<T>(
self,
value: Binder<'tcx, T>,
) -> FxIndexSet<ty::BoundRegionKind>
) -> FxIndexSet<ty::BoundRegionKind<'tcx>>
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
@ -135,7 +135,7 @@ impl<'tcx> TyCtxt<'tcx> {
self,
value: Binder<'tcx, T>,
just_constrained: bool,
) -> FxIndexSet<ty::BoundRegionKind>
) -> FxIndexSet<ty::BoundRegionKind<'tcx>>
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
@ -149,9 +149,9 @@ impl<'tcx> TyCtxt<'tcx> {
/// Collects all the late-bound regions at the innermost binding level
/// into a hash set.
struct LateBoundRegionsCollector {
struct LateBoundRegionsCollector<'tcx> {
current_index: ty::DebruijnIndex,
regions: FxIndexSet<ty::BoundRegionKind>,
regions: FxIndexSet<ty::BoundRegionKind<'tcx>>,
/// `true` if we only want regions that are known to be
/// "constrained" when you equate this type with another type. In
@ -163,13 +163,13 @@ struct LateBoundRegionsCollector {
just_constrained: bool,
}
impl LateBoundRegionsCollector {
impl LateBoundRegionsCollector<'_> {
fn new(just_constrained: bool) -> Self {
Self { current_index: ty::INNERMOST, regions: Default::default(), just_constrained }
}
}
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector {
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector<'tcx> {
fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(&mut self, t: &Binder<'tcx, T>) {
self.current_index.shift_in(1);
t.super_visit_with(self);

View file

@ -91,20 +91,32 @@ macro_rules! declare_passes {
)+
)*
static PASS_NAMES: LazyLock<FxIndexSet<&str>> = LazyLock::new(|| [
static PASS_NAMES: LazyLock<FxIndexSet<&str>> = LazyLock::new(|| {
let mut set = FxIndexSet::default();
// Fake marker pass
"PreCodegen",
set.insert("PreCodegen");
$(
$(
stringify!($pass_name),
$(
$(
$mod_name::$pass_name::$ident.name(),
)*
)?
set.extend(pass_names!($mod_name : $pass_name $( { $($ident),* } )? ));
)+
)*
].into_iter().collect());
set
});
};
}
macro_rules! pass_names {
// pass groups: only pass names inside are considered pass_names
($mod_name:ident : $pass_group:ident { $($pass_name:ident),* $(,)? }) => {
[
$(
$mod_name::$pass_group::$pass_name.name(),
)*
]
};
// lone pass names: stringify the struct or enum name
($mod_name:ident : $pass_name:ident) => {
[stringify!($pass_name)]
};
}

View file

@ -485,47 +485,33 @@ impl<'tcx> Validator<'_, 'tcx> {
if lhs_ty.is_integral() {
let sz = lhs_ty.primitive_size(self.tcx);
// Integer division: the RHS must be a non-zero const.
let rhs_val = match rhs {
Operand::Constant(c)
if self.should_evaluate_for_promotion_checks(c.const_) =>
{
c.const_.try_eval_scalar_int(self.tcx, self.typing_env)
}
_ => None,
};
match rhs_val.map(|x| x.to_uint(sz)) {
let rhs_val = if let Operand::Constant(rhs_c) = rhs
&& self.should_evaluate_for_promotion_checks(rhs_c.const_)
&& let Some(rhs_val) =
rhs_c.const_.try_eval_scalar_int(self.tcx, self.typing_env)
// for the zero test, int vs uint does not matter
Some(x) if x != 0 => {} // okay
_ => return Err(Unpromotable), // value not known or 0 -- not okay
}
&& rhs_val.to_uint(sz) != 0
{
rhs_val
} else {
// value not known or 0 -- not okay
return Err(Unpromotable);
};
// Furthermore, for signed division, we also have to exclude `int::MIN /
// -1`.
if lhs_ty.is_signed() {
match rhs_val.map(|x| x.to_int(sz)) {
Some(-1) | None => {
// The RHS is -1 or unknown, so we have to be careful.
// But is the LHS int::MIN?
let lhs_val = match lhs {
Operand::Constant(c)
if self.should_evaluate_for_promotion_checks(
c.const_,
) =>
{
c.const_
.try_eval_scalar_int(self.tcx, self.typing_env)
}
_ => None,
};
let lhs_min = sz.signed_int_min();
match lhs_val.map(|x| x.to_int(sz)) {
// okay
Some(x) if x != lhs_min => {}
// value not known or int::MIN -- not okay
_ => return Err(Unpromotable),
}
}
_ => {}
if lhs_ty.is_signed() && rhs_val.to_int(sz) == -1 {
// The RHS is -1, so we have to be careful. But is the LHS int::MIN?
if let Operand::Constant(lhs_c) = lhs
&& self.should_evaluate_for_promotion_checks(lhs_c.const_)
&& let Some(lhs_val) =
lhs_c.const_.try_eval_scalar_int(self.tcx, self.typing_env)
&& let lhs_min = sz.signed_int_min()
&& lhs_val.to_int(sz) != lhs_min
{
// okay
} else {
// value not known or int::MIN -- not okay
return Err(Unpromotable);
}
}
}

View file

@ -3,7 +3,8 @@ use rustc_type_ir::inherent::*;
use rustc_type_ir::solve::{Goal, QueryInput};
use rustc_type_ir::{
self as ty, Canonical, CanonicalParamEnvCacheEntry, CanonicalVarKind, Flags, InferCtxtLike,
Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
Interner, PlaceholderConst, PlaceholderType, TypeFlags, TypeFoldable, TypeFolder,
TypeSuperFoldable, TypeVisitableExt,
};
use crate::delegate::SolverDelegate;
@ -357,13 +358,13 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
},
ty::Placeholder(placeholder) => match self.canonicalize_mode {
CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
PlaceholderType::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
),
CanonicalizeMode::Response { .. } => CanonicalVarKind::PlaceholderTy(placeholder),
},
ty::Param(_) => match self.canonicalize_mode {
CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderTy(
PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
PlaceholderType::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
),
CanonicalizeMode::Response { .. } => panic!("param ty in response: {t:?}"),
},
@ -513,17 +514,23 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
ty::InferConst::Fresh(_) => todo!(),
},
ty::ConstKind::Placeholder(placeholder) => match self.canonicalize_mode {
CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
),
CanonicalizeMode::Input { .. } => {
CanonicalVarKind::PlaceholderConst(PlaceholderConst::new_anon(
ty::UniverseIndex::ROOT,
self.variables.len().into(),
))
}
CanonicalizeMode::Response { .. } => {
CanonicalVarKind::PlaceholderConst(placeholder)
}
},
ty::ConstKind::Param(_) => match self.canonicalize_mode {
CanonicalizeMode::Input { .. } => CanonicalVarKind::PlaceholderConst(
PlaceholderLike::new_anon(ty::UniverseIndex::ROOT, self.variables.len().into()),
),
CanonicalizeMode::Input { .. } => {
CanonicalVarKind::PlaceholderConst(PlaceholderConst::new_anon(
ty::UniverseIndex::ROOT,
self.variables.len().into(),
))
}
CanonicalizeMode::Response { .. } => panic!("param ty in response: {c:?}"),
},
// FIXME: See comment above -- we could fold the region separately or something.

View file

@ -177,9 +177,9 @@ where
}
}
ty::GenericArgKind::Const(c) => {
if let ty::ConstKind::Bound(index_kind, bv) = c.kind() {
if let ty::ConstKind::Bound(index_kind, bc) = c.kind() {
assert!(matches!(index_kind, ty::BoundVarIndexKind::Canonical));
opt_values[bv.var()] = Some(*original_value);
opt_values[bc.var()] = Some(*original_value);
}
}
}

View file

@ -3,8 +3,8 @@ use core::panic;
use rustc_type_ir::data_structures::IndexMap;
use rustc_type_ir::inherent::*;
use rustc_type_ir::{
self as ty, InferCtxtLike, Interner, TypeFoldable, TypeFolder, TypeSuperFoldable,
TypeVisitableExt,
self as ty, InferCtxtLike, Interner, PlaceholderConst, PlaceholderRegion, PlaceholderType,
TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
};
pub struct BoundVarReplacer<'a, Infcx, I = <Infcx as InferCtxtLike>::Interner>
@ -16,9 +16,9 @@ where
// These three maps track the bound variable that were replaced by placeholders. It might be
// nice to remove these since we already have the `kind` in the placeholder; we really just need
// the `var` (but we *could* bring that into scope if we were to track them as we pass them).
mapped_regions: IndexMap<I::PlaceholderRegion, I::BoundRegion>,
mapped_types: IndexMap<I::PlaceholderTy, I::BoundTy>,
mapped_consts: IndexMap<I::PlaceholderConst, I::BoundConst>,
mapped_regions: IndexMap<ty::PlaceholderRegion<I>, ty::BoundRegion<I>>,
mapped_types: IndexMap<ty::PlaceholderType<I>, ty::BoundTy<I>>,
mapped_consts: IndexMap<ty::PlaceholderConst<I>, ty::BoundConst<I>>,
// The current depth relative to *this* folding, *not* the entire normalization. In other words,
// the depth of binders we've passed here.
current_index: ty::DebruijnIndex,
@ -40,9 +40,9 @@ where
value: T,
) -> (
T,
IndexMap<I::PlaceholderRegion, I::BoundRegion>,
IndexMap<I::PlaceholderTy, I::BoundTy>,
IndexMap<I::PlaceholderConst, I::BoundConst>,
IndexMap<ty::PlaceholderRegion<I>, ty::BoundRegion<I>>,
IndexMap<ty::PlaceholderType<I>, ty::BoundTy<I>>,
IndexMap<ty::PlaceholderConst<I>, ty::BoundConst<I>>,
) {
let mut replacer = BoundVarReplacer {
infcx,
@ -103,7 +103,7 @@ where
if debruijn >= self.current_index =>
{
let universe = self.universe_for(debruijn);
let p = PlaceholderLike::new(universe, br);
let p = PlaceholderRegion::new(universe, br);
self.mapped_regions.insert(p, br);
Region::new_placeholder(self.cx(), p)
}
@ -126,7 +126,7 @@ where
if debruijn >= self.current_index =>
{
let universe = self.universe_for(debruijn);
let p = PlaceholderLike::new(universe, bound_ty);
let p = PlaceholderType::new(universe, bound_ty);
self.mapped_types.insert(p, bound_ty);
Ty::new_placeholder(self.cx(), p)
}
@ -150,7 +150,7 @@ where
if debruijn >= self.current_index =>
{
let universe = self.universe_for(debruijn);
let p = PlaceholderLike::new(universe, bound_const);
let p = PlaceholderConst::new(universe, bound_const);
self.mapped_consts.insert(p, bound_const);
Const::new_placeholder(self.cx(), p)
}

View file

@ -98,6 +98,8 @@ parse_bare_cr = {$double_quotes ->
}
.escape = escape the character
parse_bare_cr_in_frontmatter = bare CR not allowed in frontmatter
parse_bare_cr_in_raw_string = bare CR not allowed in raw string
parse_binder_and_polarity = `for<...>` binder not allowed with `{$polarity}` trait polarity modifier
@ -352,7 +354,6 @@ parse_frontmatter_length_mismatch = frontmatter close does not match the opening
parse_frontmatter_too_many_dashes = too many `-` symbols: frontmatter openings may be delimited by up to 255 `-` symbols, but found {$len_opening}
parse_frontmatter_unclosed = unclosed frontmatter
.note = frontmatter opening here was not closed
parse_function_body_equals_expr = function body cannot be `= expression;`
.suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;`

View file

@ -829,6 +829,13 @@ pub(crate) struct FrontmatterTooManyDashes {
pub len_opening: usize,
}
#[derive(Diagnostic)]
#[diag(parse_bare_cr_in_frontmatter)]
pub(crate) struct BareCrFrontmatter {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(parse_leading_plus_not_supported)]
pub(crate) struct LeadingPlusNotSupported {

View file

@ -598,9 +598,9 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
let s = self.str_from(start);
let real_start = s.find("---").unwrap();
let frontmatter_opening_pos = BytePos(real_start as u32) + start;
let s_new = &s[real_start..];
let within = s_new.trim_start_matches('-');
let len_opening = s_new.len() - within.len();
let real_s = &s[real_start..];
let within = real_s.trim_start_matches('-');
let len_opening = real_s.len() - within.len();
let frontmatter_opening_end_pos = frontmatter_opening_pos + BytePos(len_opening as u32);
if has_invalid_preceding_whitespace {
@ -614,8 +614,8 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
});
}
let line_end = real_s.find('\n').unwrap_or(real_s.len());
if invalid_infostring {
let line_end = s[real_start..].find('\n').unwrap_or(s[real_start..].len());
let span = self.mk_sp(
frontmatter_opening_end_pos,
frontmatter_opening_pos + BytePos(line_end as u32),
@ -623,10 +623,18 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
self.dcx().emit_err(errors::FrontmatterInvalidInfostring { span });
}
let last_line_start = within.rfind('\n').map_or(0, |i| i + 1);
let last_line = &within[last_line_start..];
let last_line_start = real_s.rfind('\n').map_or(0, |i| i + 1);
let content = &real_s[line_end..last_line_start];
if let Some(cr_offset) = content.find('\r') {
let cr_pos = start + BytePos((real_start + line_end + cr_offset) as u32);
let span = self.mk_sp(cr_pos, cr_pos + BytePos(1 as u32));
self.dcx().emit_err(errors::BareCrFrontmatter { span });
}
let last_line = &real_s[last_line_start..];
let last_line_trimmed = last_line.trim_start_matches(is_horizontal_whitespace);
let last_line_start_pos = frontmatter_opening_end_pos + BytePos(last_line_start as u32);
let last_line_start_pos = frontmatter_opening_pos + BytePos(last_line_start as u32);
let frontmatter_span = self.mk_sp(frontmatter_opening_pos, self.pos);
self.psess.gated_spans.gate(sym::frontmatter, frontmatter_span);

View file

@ -2760,9 +2760,13 @@ impl<'a> Parser<'a> {
let (mut cond, _) =
self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL | Restrictions::ALLOW_LET, attrs)?;
CondChecker::new(self, let_chains_policy).visit_expr(&mut cond);
Ok(cond)
let mut checker = CondChecker::new(self, let_chains_policy);
checker.visit_expr(&mut cond);
Ok(if let Some(guar) = checker.found_incorrect_let_chain {
self.mk_expr_err(cond.span, guar)
} else {
cond
})
}
/// Parses a `let $pat = $expr` pseudo-expression.
@ -3484,13 +3488,19 @@ impl<'a> Parser<'a> {
let if_span = self.prev_token.span;
let mut cond = self.parse_match_guard_condition()?;
CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond);
let mut checker = CondChecker::new(self, LetChainsPolicy::AlwaysAllowed);
checker.visit_expr(&mut cond);
if has_let_expr(&cond) {
let span = if_span.to(cond.span);
self.psess.gated_spans.gate(sym::if_let_guard, span);
}
Ok(Some(cond))
Ok(Some(if let Some(guar) = checker.found_incorrect_let_chain {
self.mk_expr_err(cond.span, guar)
} else {
cond
}))
}
fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (Pat, Option<Box<Expr>>)> {
@ -3511,13 +3521,23 @@ impl<'a> Parser<'a> {
let ast::PatKind::Paren(subpat) = pat.kind else { unreachable!() };
let ast::PatKind::Guard(_, mut cond) = subpat.kind else { unreachable!() };
self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span);
CondChecker::new(self, LetChainsPolicy::AlwaysAllowed).visit_expr(&mut cond);
let mut checker = CondChecker::new(self, LetChainsPolicy::AlwaysAllowed);
checker.visit_expr(&mut cond);
let right = self.prev_token.span;
self.dcx().emit_err(errors::ParenthesesInMatchPat {
span: vec![left, right],
sugg: errors::ParenthesesInMatchPatSugg { left, right },
});
Ok((self.mk_pat(span, ast::PatKind::Wild), Some(cond)))
Ok((
self.mk_pat(span, ast::PatKind::Wild),
(if let Some(guar) = checker.found_incorrect_let_chain {
Some(self.mk_expr_err(cond.span, guar))
} else {
Some(cond)
}),
))
} else {
Ok((pat, self.parse_match_arm_guard()?))
}
@ -4208,6 +4228,7 @@ struct CondChecker<'a> {
forbid_let_reason: Option<ForbiddenLetReason>,
missing_let: Option<errors::MaybeMissingLet>,
comparison: Option<errors::MaybeComparison>,
found_incorrect_let_chain: Option<ErrorGuaranteed>,
}
impl<'a> CondChecker<'a> {
@ -4218,6 +4239,7 @@ impl<'a> CondChecker<'a> {
missing_let: None,
comparison: None,
let_chains_policy,
found_incorrect_let_chain: None,
depth: 0,
}
}
@ -4236,12 +4258,19 @@ impl MutVisitor for CondChecker<'_> {
NotSupportedOr(or_span) => {
self.parser.dcx().emit_err(errors::OrInLetChain { span: or_span })
}
_ => self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet {
span,
reason,
missing_let: self.missing_let,
comparison: self.comparison,
}),
_ => {
let guar =
self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet {
span,
reason,
missing_let: self.missing_let,
comparison: self.comparison,
});
if let Some(_) = self.missing_let {
self.found_incorrect_let_chain = Some(guar);
}
guar
}
};
*recovered = Recovered::Yes(error);
} else if self.depth > 1 {

View file

@ -758,7 +758,7 @@ impl<'input> Parser<'input> {
}
/// Parses a word starting at the current position. A word is the same as a
/// Rust identifier, except that it can't start with `_` character.
/// Rust identifier or keyword, except that it can't be a bare `_` character.
fn word(&mut self) -> &'input str {
let index = self.input_vec_index;
match self.peek() {

View file

@ -150,7 +150,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
span: attr_span,
stability: Stability { level, feature },
}
| AttributeKind::ConstStability {
| AttributeKind::RustcConstStability {
span: attr_span,
stability: PartialConstStability { level, feature, .. },
},
@ -168,7 +168,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Attribute::Parsed(AttributeKind::AllowInternalUnsafe(attr_span) | AttributeKind::AllowInternalUnstable(.., attr_span)) => {
self.check_macro_only_attr(*attr_span, span, target, attrs)
}
Attribute::Parsed(AttributeKind::AllowConstFnUnstable(_, first_span)) => {
Attribute::Parsed(AttributeKind::RustcAllowConstFnUnstable(_, first_span)) => {
self.check_rustc_allow_const_fn_unstable(hir_id, *first_span, span, target)
}
Attribute::Parsed(AttributeKind::Deprecation {span: attr_span, .. }) => {
@ -180,7 +180,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Attribute::Parsed(AttributeKind::RustcObjectLifetimeDefault) => {
self.check_object_lifetime_default(hir_id);
}
&Attribute::Parsed(AttributeKind::PubTransparent(attr_span)) => {
&Attribute::Parsed(AttributeKind::RustcPubTransparent(attr_span)) => {
self.check_rustc_pub_transparent(attr_span, span, attrs)
}
Attribute::Parsed(AttributeKind::Align { align, span: attr_span }) => {
@ -226,29 +226,21 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
Attribute::Parsed(AttributeKind::DoNotRecommend{attr_span}) => {self.check_do_not_recommend(*attr_span, hir_id, target, item)},
Attribute::Parsed(
// tidy-alphabetical-start
AttributeKind::AllowIncoherentImpl(..)
| AttributeKind::AsPtr(..)
AttributeKind::RustcAllowIncoherentImpl(..)
| AttributeKind::AutomaticallyDerived(..)
| AttributeKind::BodyStability { .. }
| AttributeKind::CfgAttrTrace
| AttributeKind::CfgTrace(..)
| AttributeKind::CfiEncoding { .. }
| AttributeKind::Coinductive(..)
| AttributeKind::Cold(..)
| AttributeKind::CollapseDebugInfo(..)
| AttributeKind::CompilerBuiltins
| AttributeKind::Confusables { .. }
| AttributeKind::ConstStabilityIndirect
| AttributeKind::Coroutine(..)
| AttributeKind::Coverage (..)
| AttributeKind::CrateName { .. }
| AttributeKind::CrateType(..)
| AttributeKind::DebuggerVisualizer(..)
| AttributeKind::DenyExplicitImpl(..)
// `#[doc]` is actually a lot more than just doc comments, so is checked below
| AttributeKind::DocComment {..}
| AttributeKind::Dummy
| AttributeKind::DynIncompatibleTrait(..)
| AttributeKind::EiiDeclaration { .. }
| AttributeKind::EiiForeignItem
| AttributeKind::ExportName { .. }
@ -262,7 +254,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::LinkSection { .. }
| AttributeKind::Linkage(..)
| AttributeKind::MacroEscape( .. )
| AttributeKind::MacroTransparency(_)
| AttributeKind::MacroUse { .. }
| AttributeKind::Marker(..)
| AttributeKind::MoveSizeLimit { .. }
@ -277,12 +268,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::NoMain
| AttributeKind::NoMangle(..)
| AttributeKind::NoStd { .. }
| AttributeKind::ObjcClass { .. }
| AttributeKind::ObjcSelector { .. }
| AttributeKind::Optimize(..)
| AttributeKind::PanicRuntime
| AttributeKind::ParenSugar(..)
| AttributeKind::PassByValue (..)
| AttributeKind::PatchableFunctionEntry { .. }
| AttributeKind::Path(..)
| AttributeKind::PatternComplexityLimit { .. }
@ -295,14 +282,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::RustcAllocator
| AttributeKind::RustcAllocatorZeroed
| AttributeKind::RustcAllocatorZeroedVariant { .. }
| AttributeKind::RustcAsPtr(..)
| AttributeKind::RustcBodyStability { .. }
| AttributeKind::RustcBuiltinMacro { .. }
| AttributeKind::RustcCoherenceIsCore(..)
| AttributeKind::RustcCoinductive(..)
| AttributeKind::RustcConfusables { .. }
| AttributeKind::RustcConstStabilityIndirect
| AttributeKind::RustcDeallocator
| AttributeKind::RustcDenyExplicitImpl(..)
| AttributeKind::RustcDummy
| AttributeKind::RustcDumpDefParents
| AttributeKind::RustcDumpItemBounds
| AttributeKind::RustcDumpPredicates
| AttributeKind::RustcDumpUserArgs
| AttributeKind::RustcDumpVtable(..)
| AttributeKind::RustcDynIncompatibleTrait(..)
| AttributeKind::RustcHasIncoherentInherentImpls
| AttributeKind::RustcLayoutScalarValidRangeEnd(..)
| AttributeKind::RustcLayoutScalarValidRangeStart(..)
@ -310,26 +305,31 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::RustcLintOptTy
| AttributeKind::RustcLintQueryInstability
| AttributeKind::RustcLintUntrackedQueryInformation
| AttributeKind::RustcMacroTransparency(_)
| AttributeKind::RustcMain
| AttributeKind::RustcNeverReturnsNullPointer
| AttributeKind::RustcNoImplicitAutorefs
| AttributeKind::RustcNounwind
| AttributeKind::RustcObjcClass { .. }
| AttributeKind::RustcObjcSelector { .. }
| AttributeKind::RustcOffloadKernel
| AttributeKind::RustcParenSugar(..)
| AttributeKind::RustcPassByValue (..)
| AttributeKind::RustcPassIndirectlyInNonRusticAbis(..)
| AttributeKind::RustcReallocator
| AttributeKind::RustcScalableVector { .. }
| AttributeKind::RustcShouldNotBeCalledOnConstItems(..)
| AttributeKind::RustcSimdMonomorphizeLaneLimit(..)
| AttributeKind::RustcSkipDuringMethodDispatch { .. }
| AttributeKind::RustcSpecializationTrait(..)
| AttributeKind::RustcStdInternalSymbol (..)
| AttributeKind::RustcUnsafeSpecializationMarker(..)
| AttributeKind::RustcVariance
| AttributeKind::RustcVarianceOfOpaques
| AttributeKind::ShouldPanic { .. }
| AttributeKind::SkipDuringMethodDispatch { .. }
| AttributeKind::SpecializationTrait(..)
| AttributeKind::StdInternalSymbol (..)
| AttributeKind::ThreadLocal
| AttributeKind::TypeConst{..}
| AttributeKind::TypeLengthLimit { .. }
| AttributeKind::UnsafeSpecializationMarker(..)
| AttributeKind::UnstableFeatureBound(..)
| AttributeKind::Used { .. }
| AttributeKind::WindowsSubsystem(..)

View file

@ -30,10 +30,10 @@ impl<'tcx> LibFeatureCollector<'tcx> {
Attribute::Parsed(AttributeKind::Stability { stability, span }) => {
(stability.feature, stability.level, *span)
}
Attribute::Parsed(AttributeKind::ConstStability { stability, span }) => {
Attribute::Parsed(AttributeKind::RustcConstStability { stability, span }) => {
(stability.feature, stability.level, *span)
}
Attribute::Parsed(AttributeKind::BodyStability { stability, span }) => {
Attribute::Parsed(AttributeKind::RustcBodyStability { stability, span }) => {
(stability.feature, stability.level, *span)
}
_ => return None,

View file

@ -197,7 +197,7 @@ fn lookup_default_body_stability(
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
// FIXME: check that this item can have body stability
find_attr!(attrs, AttributeKind::BodyStability { stability, .. } => *stability)
find_attr!(attrs, AttributeKind::RustcBodyStability { stability, .. } => *stability)
}
#[instrument(level = "debug", skip(tcx))]
@ -214,7 +214,7 @@ fn lookup_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ConstSt
{
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
let const_stability_indirect =
find_attr!(attrs, AttributeKind::ConstStabilityIndirect);
find_attr!(attrs, AttributeKind::RustcConstStabilityIndirect);
return Some(ConstStability::unmarked(const_stability_indirect, parent_stab));
}
}
@ -223,9 +223,9 @@ fn lookup_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ConstSt
}
let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(def_id));
let const_stability_indirect = find_attr!(attrs, AttributeKind::ConstStabilityIndirect);
let const_stability_indirect = find_attr!(attrs, AttributeKind::RustcConstStabilityIndirect);
let const_stab =
find_attr!(attrs, AttributeKind::ConstStability { stability, span: _ } => *stability);
find_attr!(attrs, AttributeKind::RustcConstStability { stability, span: _ } => *stability);
// After checking the immediate attributes, get rid of the span and compute implied
// const stability: inherit feature gate from regular stability.
@ -393,7 +393,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
if let Some(fn_sig) = fn_sig
&& !fn_sig.header.is_const()
&& const_stab.is_some()
&& find_attr_span!(ConstStability).is_some()
&& find_attr_span!(RustcConstStability).is_some()
{
self.tcx.dcx().emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span });
}
@ -403,7 +403,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
&& let Some(fn_sig) = fn_sig
&& const_stab.is_const_stable()
&& !stab.is_some_and(|s| s.is_stable())
&& let Some(const_span) = find_attr_span!(ConstStability)
&& let Some(const_span) = find_attr_span!(RustcConstStability)
{
self.tcx
.dcx()
@ -413,7 +413,7 @@ impl<'tcx> MissingStabilityAnnotations<'tcx> {
if let Some(stab) = &const_stab
&& stab.is_const_stable()
&& stab.const_stable_indirect
&& let Some(span) = find_attr_span!(ConstStability)
&& let Some(span) = find_attr_span!(RustcConstStability)
{
self.tcx.dcx().emit_err(errors::RustcConstStableIndirectPairing { span });
}
@ -602,7 +602,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
let stab = find_attr!(attrs, AttributeKind::Stability{stability, span} => (*stability, *span));
// FIXME(jdonszelmann): make it impossible to miss the or_else in the typesystem
let const_stab = find_attr!(attrs, AttributeKind::ConstStability{stability, ..} => *stability);
let const_stab = find_attr!(attrs, AttributeKind::RustcConstStability{stability, ..} => *stability);
let unstable_feature_stab =
find_attr!(attrs, AttributeKind::UnstableFeatureBound(i) => i)

View file

@ -497,7 +497,7 @@ impl<'tcx> EmbargoVisitor<'tcx> {
let hir_id = self.tcx.local_def_id_to_hir_id(local_def_id);
let attrs = self.tcx.hir_attrs(hir_id);
if find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x)
if find_attr!(attrs, AttributeKind::RustcMacroTransparency(x) => *x)
.unwrap_or(Transparency::fallback(md.macro_rules))
!= Transparency::Opaque
{

View file

@ -433,7 +433,7 @@ where
}
impl RustcInternal for BoundVariableKind {
type T<'tcx> = rustc_ty::BoundVariableKind;
type T<'tcx> = rustc_ty::BoundVariableKind<'tcx>;
fn internal<'tcx>(
&self,

View file

@ -271,7 +271,7 @@ impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> {
}
}
impl<'tcx> Stable<'tcx> for ty::BoundTyKind {
impl<'tcx> Stable<'tcx> for ty::BoundTyKind<'tcx> {
type T = crate::ty::BoundTyKind;
fn stable<'cx>(
@ -290,7 +290,7 @@ impl<'tcx> Stable<'tcx> for ty::BoundTyKind {
}
}
impl<'tcx> Stable<'tcx> for ty::BoundRegionKind {
impl<'tcx> Stable<'tcx> for ty::BoundRegionKind<'tcx> {
type T = crate::ty::BoundRegionKind;
fn stable<'cx>(
@ -307,12 +307,12 @@ impl<'tcx> Stable<'tcx> for ty::BoundRegionKind {
cx.tcx.item_name(*def_id).to_string(),
),
ty::BoundRegionKind::ClosureEnv => BoundRegionKind::BrEnv,
ty::BoundRegionKind::NamedAnon(_) => bug!("only used for pretty printing"),
ty::BoundRegionKind::NamedForPrinting(_) => bug!("only used for pretty printing"),
}
}
}
impl<'tcx> Stable<'tcx> for ty::BoundVariableKind {
impl<'tcx> Stable<'tcx> for ty::BoundVariableKind<'tcx> {
type T = crate::ty::BoundVariableKind;
fn stable<'cx>(
@ -546,7 +546,7 @@ impl<'tcx> Stable<'tcx> for ty::ParamTy {
}
}
impl<'tcx> Stable<'tcx> for ty::BoundTy {
impl<'tcx> Stable<'tcx> for ty::BoundTy<'tcx> {
type T = crate::ty::BoundTy;
fn stable<'cx>(
&self,

View file

@ -78,7 +78,10 @@ impl<'tcx> InternalCx<'tcx> for TyCtxt<'tcx> {
fn mk_bound_variable_kinds_from_iter<I, T>(self, iter: I) -> T::Output
where
I: Iterator<Item = T>,
T: ty::CollectAndApply<ty::BoundVariableKind, &'tcx List<ty::BoundVariableKind>>,
T: ty::CollectAndApply<
ty::BoundVariableKind<'tcx>,
&'tcx List<ty::BoundVariableKind<'tcx>>,
>,
{
TyCtxt::mk_bound_variable_kinds_from_iter(self, iter)
}

View file

@ -47,7 +47,10 @@ pub trait InternalCx<'tcx>: Copy + Clone {
fn mk_bound_variable_kinds_from_iter<I, T>(self, iter: I) -> T::Output
where
I: Iterator<Item = T>,
T: ty::CollectAndApply<ty::BoundVariableKind, &'tcx List<ty::BoundVariableKind>>;
T: ty::CollectAndApply<
ty::BoundVariableKind<'tcx>,
&'tcx List<ty::BoundVariableKind<'tcx>>,
>;
fn mk_place_elems(self, v: &[mir::PlaceElem<'tcx>]) -> &'tcx List<mir::PlaceElem<'tcx>>;

View file

@ -10,19 +10,17 @@ use rustc_data_structures::stable_hasher::HashStable;
use rustc_data_structures::sync::AtomicU64;
use rustc_middle::arena::Arena;
use rustc_middle::dep_graph::{self, DepKind, DepKindVTable, DepNodeIndex};
use rustc_middle::query::erase::{Erase, erase, restore};
use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache};
use rustc_middle::query::plumbing::{DynamicQuery, QuerySystem, QuerySystemFns};
use rustc_middle::query::plumbing::{QuerySystem, QuerySystemFns, QueryVTable};
use rustc_middle::query::{
AsLocalKey, DynamicQueries, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates,
queries,
AsLocalKey, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates, queries,
};
use rustc_middle::ty::TyCtxt;
use rustc_query_system::Value;
use rustc_query_system::dep_graph::SerializedDepNodeIndex;
use rustc_query_system::ich::StableHashingContext;
use rustc_query_system::query::{
CycleError, CycleErrorHandling, HashResult, QueryCache, QueryConfig, QueryMap, QueryMode,
CycleError, CycleErrorHandling, HashResult, QueryCache, QueryDispatcher, QueryMap, QueryMode,
QueryStackDeferred, QueryState, get_query_incr, get_query_non_incr,
};
use rustc_span::{ErrorGuaranteed, Span};
@ -37,45 +35,54 @@ pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all};
mod profiling_support;
pub use self::profiling_support::alloc_self_profile_query_strings;
struct DynamicConfig<
/// Combines a [`QueryVTable`] with some additional compile-time booleans
/// to implement [`QueryDispatcher`], for use by code in [`rustc_query_system`].
///
/// Baking these boolean flags into the type gives a modest but measurable
/// improvement to compiler perf and compiler code size; see
/// <https://github.com/rust-lang/rust/pull/151633>.
struct SemiDynamicQueryDispatcher<
'tcx,
C: QueryCache,
const ANON: bool,
const DEPTH_LIMIT: bool,
const FEEDABLE: bool,
> {
dynamic: &'tcx DynamicQuery<'tcx, C>,
vtable: &'tcx QueryVTable<'tcx, C>,
}
// Manually implement Copy/Clone, because deriving would put trait bounds on the cache type.
impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> Copy
for DynamicConfig<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
{
}
impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> Clone
for DynamicConfig<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
{
fn clone(&self) -> Self {
*self
}
}
// This is `impl QueryDispatcher for SemiDynamicQueryDispatcher`.
impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool>
QueryConfig<QueryCtxt<'tcx>> for DynamicConfig<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
QueryDispatcher for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
where
for<'a> C::Key: HashStable<StableHashingContext<'a>>,
{
type Qcx = QueryCtxt<'tcx>;
type Key = C::Key;
type Value = C::Value;
type Cache = C;
#[inline(always)]
fn name(self) -> &'static str {
self.dynamic.name
self.vtable.name
}
#[inline(always)]
fn cache_on_disk(self, tcx: TyCtxt<'tcx>, key: &Self::Key) -> bool {
(self.dynamic.cache_on_disk)(tcx, key)
(self.vtable.cache_on_disk)(tcx, key)
}
#[inline(always)]
@ -90,33 +97,30 @@ where
// This is just manually doing the subfield referencing through pointer math.
unsafe {
&*(&qcx.tcx.query_system.states as *const QueryStates<'tcx>)
.byte_add(self.dynamic.query_state)
.byte_add(self.vtable.query_state)
.cast::<QueryState<Self::Key, QueryStackDeferred<'tcx>>>()
}
}
#[inline(always)]
fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache
where
'tcx: 'a,
{
fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache {
// Safety:
// This is just manually doing the subfield referencing through pointer math.
unsafe {
&*(&qcx.tcx.query_system.caches as *const QueryCaches<'tcx>)
.byte_add(self.dynamic.query_cache)
.byte_add(self.vtable.query_cache)
.cast::<Self::Cache>()
}
}
#[inline(always)]
fn execute_query(self, tcx: TyCtxt<'tcx>, key: Self::Key) -> Self::Value {
(self.dynamic.execute_query)(tcx, key)
(self.vtable.execute_query)(tcx, key)
}
#[inline(always)]
fn compute(self, qcx: QueryCtxt<'tcx>, key: Self::Key) -> Self::Value {
(self.dynamic.compute)(qcx.tcx, key)
(self.vtable.compute)(qcx.tcx, key)
}
#[inline(always)]
@ -127,8 +131,8 @@ where
prev_index: SerializedDepNodeIndex,
index: DepNodeIndex,
) -> Option<Self::Value> {
if self.dynamic.can_load_from_disk {
(self.dynamic.try_load_from_disk)(qcx.tcx, key, prev_index, index)
if self.vtable.can_load_from_disk {
(self.vtable.try_load_from_disk)(qcx.tcx, key, prev_index, index)
} else {
None
}
@ -141,7 +145,7 @@ where
key: &Self::Key,
index: SerializedDepNodeIndex,
) -> bool {
(self.dynamic.loadable_from_disk)(qcx.tcx, key, index)
(self.vtable.loadable_from_disk)(qcx.tcx, key, index)
}
fn value_from_cycle_error(
@ -150,12 +154,12 @@ where
cycle_error: &CycleError,
guar: ErrorGuaranteed,
) -> Self::Value {
(self.dynamic.value_from_cycle_error)(tcx, cycle_error, guar)
(self.vtable.value_from_cycle_error)(tcx, cycle_error, guar)
}
#[inline(always)]
fn format_value(self) -> fn(&Self::Value) -> String {
self.dynamic.format_value
self.vtable.format_value
}
#[inline(always)]
@ -165,7 +169,7 @@ where
#[inline(always)]
fn eval_always(self) -> bool {
self.dynamic.eval_always
self.vtable.eval_always
}
#[inline(always)]
@ -180,31 +184,40 @@ where
#[inline(always)]
fn dep_kind(self) -> DepKind {
self.dynamic.dep_kind
self.vtable.dep_kind
}
#[inline(always)]
fn cycle_error_handling(self) -> CycleErrorHandling {
self.dynamic.cycle_error_handling
self.vtable.cycle_error_handling
}
#[inline(always)]
fn hash_result(self) -> HashResult<Self::Value> {
self.dynamic.hash_result
self.vtable.hash_result
}
}
/// This is implemented per query. It allows restoring query values from their erased state
/// and constructing a QueryConfig.
trait QueryConfigRestored<'tcx> {
type RestoredValue;
type Config: QueryConfig<QueryCtxt<'tcx>>;
/// Provides access to vtable-like operations for a query
/// (by creating a [`QueryDispatcher`]),
/// but also keeps track of the "unerased" value type of the query
/// (i.e. the actual result type in the query declaration).
///
/// This trait allows some per-query code to be defined in generic functions
/// with a trait bound, instead of having to be defined inline within a macro
/// expansion.
///
/// There is one macro-generated implementation of this trait for each query,
/// on the type `rustc_query_impl::query_impl::$name::QueryType`.
trait QueryDispatcherUnerased<'tcx> {
type UnerasedValue;
type Dispatcher: QueryDispatcher<Qcx = QueryCtxt<'tcx>>;
const NAME: &'static &'static str;
fn config(tcx: TyCtxt<'tcx>) -> Self::Config;
fn restore(value: <Self::Config as QueryConfig<QueryCtxt<'tcx>>>::Value)
-> Self::RestoredValue;
fn query_dispatcher(tcx: TyCtxt<'tcx>) -> Self::Dispatcher;
fn restore_val(value: <Self::Dispatcher as QueryDispatcher>::Value) -> Self::UnerasedValue;
}
pub fn query_system<'a>(
@ -217,7 +230,7 @@ pub fn query_system<'a>(
states: Default::default(),
arenas: Default::default(),
caches: Default::default(),
dynamic_queries: dynamic_queries(),
query_vtables: make_query_vtables(),
on_disk_cache,
fns: QuerySystemFns {
engine: engine(incremental),

View file

@ -27,14 +27,14 @@ use rustc_middle::ty::{self, TyCtxt};
use rustc_query_system::dep_graph::{DepNodeParams, HasDepContext};
use rustc_query_system::ich::StableHashingContext;
use rustc_query_system::query::{
QueryCache, QueryConfig, QueryContext, QueryJobId, QueryMap, QuerySideEffect,
QueryCache, QueryContext, QueryDispatcher, QueryJobId, QueryMap, QuerySideEffect,
QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra, force_query,
};
use rustc_query_system::{QueryOverflow, QueryOverflowNote};
use rustc_serialize::{Decodable, Encodable};
use rustc_span::def_id::LOCAL_CRATE;
use crate::QueryConfigRestored;
use crate::QueryDispatcherUnerased;
/// Implements [`QueryContext`] for use by [`rustc_query_system`], since that
/// crate does not have direct access to [`TyCtxt`].
@ -281,7 +281,10 @@ macro_rules! feedable {
macro_rules! hash_result {
([][$V:ty]) => {{
Some(|hcx, result| dep_graph::hash_result(hcx, &restore::<$V>(*result)))
Some(|hcx, result| {
let result = ::rustc_middle::query::erase::restore_val::<$V>(*result);
::rustc_query_system::dep_graph::hash_result(hcx, &result)
})
}};
([(no_hash) $($rest:tt)*][$V:ty]) => {{
None
@ -387,13 +390,13 @@ pub(crate) fn create_query_frame<
}
pub(crate) fn encode_query_results<'a, 'tcx, Q>(
query: Q::Config,
query: Q::Dispatcher,
qcx: QueryCtxt<'tcx>,
encoder: &mut CacheEncoder<'a, 'tcx>,
query_result_index: &mut EncodedDepNodeIndex,
) where
Q: super::QueryConfigRestored<'tcx>,
Q::RestoredValue: Encodable<CacheEncoder<'a, 'tcx>>,
Q: QueryDispatcherUnerased<'tcx>,
Q::UnerasedValue: Encodable<CacheEncoder<'a, 'tcx>>,
{
let _timer = qcx.tcx.prof.generic_activity_with_arg("encode_query_results_for", query.name());
@ -408,13 +411,13 @@ pub(crate) fn encode_query_results<'a, 'tcx, Q>(
// Encode the type check tables with the `SerializedDepNodeIndex`
// as tag.
encoder.encode_tagged(dep_node, &Q::restore(*value));
encoder.encode_tagged(dep_node, &Q::restore_val(*value));
}
});
}
pub(crate) fn query_key_hash_verify<'tcx>(
query: impl QueryConfig<QueryCtxt<'tcx>>,
query: impl QueryDispatcher<Qcx = QueryCtxt<'tcx>>,
qcx: QueryCtxt<'tcx>,
) {
let _timer = qcx.tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name());
@ -442,7 +445,7 @@ pub(crate) fn query_key_hash_verify<'tcx>(
fn try_load_from_on_disk_cache<'tcx, Q>(query: Q, tcx: TyCtxt<'tcx>, dep_node: DepNode)
where
Q: QueryConfig<QueryCtxt<'tcx>>,
Q: QueryDispatcher<Qcx = QueryCtxt<'tcx>>,
{
debug_assert!(tcx.dep_graph.is_green(&dep_node));
@ -488,7 +491,7 @@ where
fn force_from_dep_node<'tcx, Q>(query: Q, tcx: TyCtxt<'tcx>, dep_node: DepNode) -> bool
where
Q: QueryConfig<QueryCtxt<'tcx>>,
Q: QueryDispatcher<Qcx = QueryCtxt<'tcx>>,
{
// We must avoid ever having to call `force_from_dep_node()` for a
// `DepNode::codegen_unit`:
@ -521,9 +524,9 @@ pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q>(
is_eval_always: bool,
) -> DepKindVTable<'tcx>
where
Q: QueryConfigRestored<'tcx>,
Q: QueryDispatcherUnerased<'tcx>,
{
let fingerprint_style = <Q::Config as QueryConfig<QueryCtxt<'tcx>>>::Key::fingerprint_style();
let fingerprint_style = <Q::Dispatcher as QueryDispatcher>::Key::fingerprint_style();
if is_anon || !fingerprint_style.reconstructible() {
return DepKindVTable {
@ -541,10 +544,10 @@ where
is_eval_always,
fingerprint_style,
force_from_dep_node: Some(|tcx, dep_node, _| {
force_from_dep_node(Q::config(tcx), tcx, dep_node)
force_from_dep_node(Q::query_dispatcher(tcx), tcx, dep_node)
}),
try_load_from_on_disk_cache: Some(|tcx, dep_node| {
try_load_from_on_disk_cache(Q::config(tcx), tcx, dep_node)
try_load_from_on_disk_cache(Q::query_dispatcher(tcx), tcx, dep_node)
}),
name: Q::NAME,
}
@ -597,6 +600,7 @@ macro_rules! define_queries {
pub(crate) mod query_impl { $(pub(crate) mod $name {
use super::super::*;
use std::marker::PhantomData;
use ::rustc_middle::query::erase::{self, Erased};
pub(crate) mod get_query_incr {
use super::*;
@ -609,11 +613,11 @@ macro_rules! define_queries {
span: Span,
key: queries::$name::Key<'tcx>,
mode: QueryMode,
) -> Option<Erase<queries::$name::Value<'tcx>>> {
) -> Option<Erased<queries::$name::Value<'tcx>>> {
#[cfg(debug_assertions)]
let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
get_query_incr(
QueryType::config(tcx),
QueryType::query_dispatcher(tcx),
QueryCtxt::new(tcx),
span,
key,
@ -631,9 +635,9 @@ macro_rules! define_queries {
span: Span,
key: queries::$name::Key<'tcx>,
__mode: QueryMode,
) -> Option<Erase<queries::$name::Value<'tcx>>> {
) -> Option<Erased<queries::$name::Value<'tcx>>> {
Some(get_query_non_incr(
QueryType::config(tcx),
QueryType::query_dispatcher(tcx),
QueryCtxt::new(tcx),
span,
key,
@ -641,10 +645,10 @@ macro_rules! define_queries {
}
}
pub(crate) fn dynamic_query<'tcx>()
-> DynamicQuery<'tcx, queries::$name::Storage<'tcx>>
pub(crate) fn make_query_vtable<'tcx>()
-> QueryVTable<'tcx, queries::$name::Storage<'tcx>>
{
DynamicQuery {
QueryVTable {
name: stringify!($name),
eval_always: is_eval_always!([$($modifiers)*]),
dep_kind: dep_graph::dep_kinds::$name,
@ -652,7 +656,7 @@ macro_rules! define_queries {
query_state: std::mem::offset_of!(QueryStates<'tcx>, $name),
query_cache: std::mem::offset_of!(QueryCaches<'tcx>, $name),
cache_on_disk: |tcx, key| ::rustc_middle::query::cached::$name(tcx, key),
execute_query: |tcx, key| erase(tcx.$name(key)),
execute_query: |tcx, key| erase::erase_val(tcx.$name(key)),
compute: |tcx, key| {
#[cfg(debug_assertions)]
let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
@ -690,7 +694,7 @@ macro_rules! define_queries {
}),
value_from_cycle_error: |tcx, cycle, guar| {
let result: queries::$name::Value<'tcx> = Value::from_cycle_error(tcx, cycle, guar);
erase(result)
erase::erase_val(result)
},
loadable_from_disk: |_tcx, _key, _index| {
should_ever_cache_on_disk!([$($modifiers)*] {
@ -701,7 +705,7 @@ macro_rules! define_queries {
})
},
hash_result: hash_result!([$($modifiers)*][queries::$name::Value<'tcx>]),
format_value: |value| format!("{:?}", restore::<queries::$name::Value<'tcx>>(*value)),
format_value: |value| format!("{:?}", erase::restore_val::<queries::$name::Value<'tcx>>(*value)),
}
}
@ -710,9 +714,9 @@ macro_rules! define_queries {
data: PhantomData<&'tcx ()>
}
impl<'tcx> QueryConfigRestored<'tcx> for QueryType<'tcx> {
type RestoredValue = queries::$name::Value<'tcx>;
type Config = DynamicConfig<
impl<'tcx> QueryDispatcherUnerased<'tcx> for QueryType<'tcx> {
type UnerasedValue = queries::$name::Value<'tcx>;
type Dispatcher = SemiDynamicQueryDispatcher<
'tcx,
queries::$name::Storage<'tcx>,
{ is_anon!([$($modifiers)*]) },
@ -723,15 +727,15 @@ macro_rules! define_queries {
const NAME: &'static &'static str = &stringify!($name);
#[inline(always)]
fn config(tcx: TyCtxt<'tcx>) -> Self::Config {
DynamicConfig {
dynamic: &tcx.query_system.dynamic_queries.$name,
fn query_dispatcher(tcx: TyCtxt<'tcx>) -> Self::Dispatcher {
SemiDynamicQueryDispatcher {
vtable: &tcx.query_system.query_vtables.$name,
}
}
#[inline(always)]
fn restore(value: <Self::Config as QueryConfig<QueryCtxt<'tcx>>>::Value) -> Self::RestoredValue {
restore::<queries::$name::Value<'tcx>>(value)
fn restore_val(value: <Self::Dispatcher as QueryDispatcher>::Value) -> Self::UnerasedValue {
erase::restore_val::<queries::$name::Value<'tcx>>(value)
}
}
@ -782,7 +786,7 @@ macro_rules! define_queries {
query_result_index: &mut EncodedDepNodeIndex
) {
$crate::plumbing::encode_query_results::<query_impl::$name::QueryType<'tcx>>(
query_impl::$name::QueryType::config(tcx),
query_impl::$name::QueryType::query_dispatcher(tcx),
QueryCtxt::new(tcx),
encoder,
query_result_index,
@ -792,7 +796,7 @@ macro_rules! define_queries {
pub(crate) fn query_key_hash_verify<'tcx>(tcx: TyCtxt<'tcx>) {
$crate::plumbing::query_key_hash_verify(
query_impl::$name::QueryType::config(tcx),
query_impl::$name::QueryType::query_dispatcher(tcx),
QueryCtxt::new(tcx),
)
}
@ -810,10 +814,10 @@ macro_rules! define_queries {
}
}
pub fn dynamic_queries<'tcx>() -> DynamicQueries<'tcx> {
DynamicQueries {
pub fn make_query_vtables<'tcx>() -> ::rustc_middle::query::PerQueryVTables<'tcx> {
::rustc_middle::query::PerQueryVTables {
$(
$name: query_impl::$name::dynamic_query(),
$name: query_impl::$name::make_query_vtable(),
)*
}
}

View file

@ -1,5 +1,3 @@
//! Query configuration and description traits.
use std::fmt::Debug;
use std::hash::Hash;
@ -7,19 +5,35 @@ use rustc_data_structures::fingerprint::Fingerprint;
use rustc_span::ErrorGuaranteed;
use super::QueryStackFrameExtra;
use crate::dep_graph::{DepKind, DepNode, DepNodeParams, SerializedDepNodeIndex};
use crate::dep_graph::{DepKind, DepNode, DepNodeParams, HasDepContext, SerializedDepNodeIndex};
use crate::ich::StableHashingContext;
use crate::query::caches::QueryCache;
use crate::query::{CycleError, CycleErrorHandling, DepNodeIndex, QueryContext, QueryState};
pub type HashResult<V> = Option<fn(&mut StableHashingContext<'_>, &V) -> Fingerprint>;
pub trait QueryConfig<Qcx: QueryContext>: Copy {
/// Unambiguous shorthand for `<This::Qcx as HasDepContext>::DepContext`.
#[expect(type_alias_bounds)]
type DepContextOf<This: QueryDispatcher> =
<<This as QueryDispatcher>::Qcx as HasDepContext>::DepContext;
/// Trait that can be used as a vtable for a single query, providing operations
/// and metadata for that query.
///
/// Implemented by `rustc_query_impl::SemiDynamicQueryDispatcher`, which
/// mostly delegates to `rustc_middle::query::plumbing::QueryVTable`.
/// Those types are not visible from this `rustc_query_system` crate.
///
/// "Dispatcher" should be understood as a near-synonym of "vtable".
pub trait QueryDispatcher: Copy {
fn name(self) -> &'static str;
/// Query context used by this dispatcher, i.e. `rustc_query_impl::QueryCtxt`.
type Qcx: QueryContext;
// `Key` and `Value` are `Copy` instead of `Clone` to ensure copying them stays cheap,
// but it isn't necessary.
type Key: DepNodeParams<Qcx::DepContext> + Eq + Hash + Copy + Debug;
type Key: DepNodeParams<DepContextOf<Self>> + Eq + Hash + Copy + Debug;
type Value: Copy;
type Cache: QueryCache<Key = Self::Key, Value = Self::Value>;
@ -27,36 +41,40 @@ pub trait QueryConfig<Qcx: QueryContext>: Copy {
fn format_value(self) -> fn(&Self::Value) -> String;
// Don't use this method to access query results, instead use the methods on TyCtxt
fn query_state<'a>(self, tcx: Qcx) -> &'a QueryState<Self::Key, Qcx::QueryInfo>
where
Qcx: 'a;
fn query_state<'a>(
self,
tcx: Self::Qcx,
) -> &'a QueryState<Self::Key, <Self::Qcx as QueryContext>::QueryInfo>;
// Don't use this method to access query results, instead use the methods on TyCtxt
fn query_cache<'a>(self, tcx: Qcx) -> &'a Self::Cache
where
Qcx: 'a;
fn query_cache<'a>(self, tcx: Self::Qcx) -> &'a Self::Cache;
fn cache_on_disk(self, tcx: Qcx::DepContext, key: &Self::Key) -> bool;
fn cache_on_disk(self, tcx: DepContextOf<Self>, key: &Self::Key) -> bool;
// Don't use this method to compute query results, instead use the methods on TyCtxt
fn execute_query(self, tcx: Qcx::DepContext, k: Self::Key) -> Self::Value;
fn execute_query(self, tcx: DepContextOf<Self>, k: Self::Key) -> Self::Value;
fn compute(self, tcx: Qcx, key: Self::Key) -> Self::Value;
fn compute(self, tcx: Self::Qcx, key: Self::Key) -> Self::Value;
fn try_load_from_disk(
self,
tcx: Qcx,
tcx: Self::Qcx,
key: &Self::Key,
prev_index: SerializedDepNodeIndex,
index: DepNodeIndex,
) -> Option<Self::Value>;
fn loadable_from_disk(self, qcx: Qcx, key: &Self::Key, idx: SerializedDepNodeIndex) -> bool;
fn loadable_from_disk(
self,
qcx: Self::Qcx,
key: &Self::Key,
idx: SerializedDepNodeIndex,
) -> bool;
/// Synthesize an error value to let compilation continue after a cycle.
fn value_from_cycle_error(
self,
tcx: Qcx::DepContext,
tcx: DepContextOf<Self>,
cycle_error: &CycleError<QueryStackFrameExtra>,
guar: ErrorGuaranteed,
) -> Self::Value;
@ -71,7 +89,7 @@ pub trait QueryConfig<Qcx: QueryContext>: Copy {
fn hash_result(self) -> HashResult<Self::Value>;
// Just here for convenience and checking that the key matches the kind, don't override this.
fn construct_dep_node(self, tcx: Qcx::DepContext, key: &Self::Key) -> DepNode {
fn construct_dep_node(self, tcx: DepContextOf<Self>, key: &Self::Key) -> DepNode {
DepNode::construct(tcx, self.dep_kind(), key)
}
}

View file

@ -13,7 +13,7 @@ use rustc_span::Span;
use rustc_span::def_id::DefId;
pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache};
pub use self::config::{HashResult, QueryConfig};
pub use self::dispatcher::{HashResult, QueryDispatcher};
pub use self::job::{
QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap, break_query_cycles, print_query_stack,
report_cycle,
@ -22,7 +22,7 @@ pub use self::plumbing::*;
use crate::dep_graph::{DepKind, DepNodeIndex, HasDepContext, SerializedDepNodeIndex};
mod caches;
mod config;
mod dispatcher;
mod job;
mod plumbing;

View file

@ -18,8 +18,10 @@ use rustc_errors::{Diag, FatalError, StashKey};
use rustc_span::{DUMMY_SP, Span};
use tracing::instrument;
use super::{QueryConfig, QueryStackFrameExtra};
use crate::dep_graph::{DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams};
use super::{QueryDispatcher, QueryStackFrameExtra};
use crate::dep_graph::{
DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams, HasDepContext,
};
use crate::ich::StableHashingContext;
use crate::query::caches::QueryCache;
use crate::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryLatch, report_cycle};
@ -124,24 +126,22 @@ where
#[cold]
#[inline(never)]
fn mk_cycle<Q, Qcx>(query: Q, qcx: Qcx, cycle_error: CycleError) -> Q::Value
fn mk_cycle<Q>(query: Q, qcx: Q::Qcx, cycle_error: CycleError) -> Q::Value
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
let error = report_cycle(qcx.dep_context().sess(), &cycle_error);
handle_cycle_error(query, qcx, &cycle_error, error)
}
fn handle_cycle_error<Q, Qcx>(
fn handle_cycle_error<Q>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
cycle_error: &CycleError,
error: Diag<'_>,
) -> Q::Value
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
match query.cycle_error_handling() {
CycleErrorHandling::Error => {
@ -272,15 +272,14 @@ where
#[cold]
#[inline(never)]
fn cycle_error<Q, Qcx>(
fn cycle_error<Q>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
try_execute: QueryJobId,
span: Span,
) -> (Q::Value, Option<DepNodeIndex>)
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
// Ensure there was no errors collecting all active jobs.
// We need the complete map to ensure we find a cycle to break.
@ -291,17 +290,16 @@ where
}
#[inline(always)]
fn wait_for_query<Q, Qcx>(
fn wait_for_query<Q>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
span: Span,
key: Q::Key,
latch: QueryLatch<Qcx::QueryInfo>,
latch: QueryLatch<<Q::Qcx as QueryContext>::QueryInfo>,
current: Option<QueryJobId>,
) -> (Q::Value, Option<DepNodeIndex>)
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
// For parallel queries, we'll block and wait until the query running
// in another thread has completed. Record how long we wait in the
@ -341,16 +339,15 @@ where
}
#[inline(never)]
fn try_execute_query<Q, Qcx, const INCR: bool>(
fn try_execute_query<Q, const INCR: bool>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
span: Span,
key: Q::Key,
dep_node: Option<DepNode>,
) -> (Q::Value, Option<DepNodeIndex>)
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
let state = query.query_state(qcx);
let key_hash = sharded::make_hash(&key);
@ -382,7 +379,7 @@ where
// Drop the lock before we start executing the query
drop(state_lock);
execute_job::<_, _, INCR>(query, qcx, state, key, key_hash, id, dep_node)
execute_job::<Q, INCR>(query, qcx, state, key, key_hash, id, dep_node)
}
Entry::Occupied(mut entry) => {
match &mut entry.get_mut().1 {
@ -411,18 +408,17 @@ where
}
#[inline(always)]
fn execute_job<Q, Qcx, const INCR: bool>(
fn execute_job<Q, const INCR: bool>(
query: Q,
qcx: Qcx,
state: &QueryState<Q::Key, Qcx::QueryInfo>,
qcx: Q::Qcx,
state: &QueryState<Q::Key, <Q::Qcx as QueryContext>::QueryInfo>,
key: Q::Key,
key_hash: u64,
id: QueryJobId,
dep_node: Option<DepNode>,
) -> (Q::Value, Option<DepNodeIndex>)
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
// Use `JobOwner` so the query will be poisoned if executing it panics.
let job_owner = JobOwner { state, key };
@ -484,15 +480,14 @@ where
// Fast path for when incr. comp. is off.
#[inline(always)]
fn execute_job_non_incr<Q, Qcx>(
fn execute_job_non_incr<Q>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
key: Q::Key,
job_id: QueryJobId,
) -> (Q::Value, DepNodeIndex)
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled());
@ -521,17 +516,16 @@ where
}
#[inline(always)]
fn execute_job_incr<Q, Qcx>(
fn execute_job_incr<Q>(
query: Q,
qcx: Qcx,
dep_graph_data: &DepGraphData<Qcx::Deps>,
qcx: Q::Qcx,
dep_graph_data: &DepGraphData<<Q::Qcx as HasDepContext>::Deps>,
key: Q::Key,
mut dep_node_opt: Option<DepNode>,
job_id: QueryJobId,
) -> (Q::Value, DepNodeIndex)
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
if !query.anon() && !query.eval_always() {
// `to_dep_node` is expensive for some `DepKind`s.
@ -577,16 +571,15 @@ where
}
#[inline(always)]
fn try_load_from_disk_and_cache_in_memory<Q, Qcx>(
fn try_load_from_disk_and_cache_in_memory<Q>(
query: Q,
dep_graph_data: &DepGraphData<Qcx::Deps>,
qcx: Qcx,
dep_graph_data: &DepGraphData<<Q::Qcx as HasDepContext>::Deps>,
qcx: Q::Qcx,
key: &Q::Key,
dep_node: &DepNode,
) -> Option<(Q::Value, DepNodeIndex)>
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
// Note this function can be called concurrently from the same query
// We must ensure that this is handled correctly.
@ -764,15 +757,14 @@ fn incremental_verify_ich_failed<Tcx>(
///
/// Note: The optimization is only available during incr. comp.
#[inline(never)]
fn ensure_must_run<Q, Qcx>(
fn ensure_must_run<Q>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
key: &Q::Key,
check_cache: bool,
) -> (bool, Option<DepNode>)
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
if query.eval_always() {
return (true, None);
@ -817,27 +809,25 @@ pub enum QueryMode {
}
#[inline(always)]
pub fn get_query_non_incr<Q, Qcx>(query: Q, qcx: Qcx, span: Span, key: Q::Key) -> Q::Value
pub fn get_query_non_incr<Q>(query: Q, qcx: Q::Qcx, span: Span, key: Q::Key) -> Q::Value
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled());
ensure_sufficient_stack(|| try_execute_query::<Q, Qcx, false>(query, qcx, span, key, None).0)
ensure_sufficient_stack(|| try_execute_query::<Q, false>(query, qcx, span, key, None).0)
}
#[inline(always)]
pub fn get_query_incr<Q, Qcx>(
pub fn get_query_incr<Q>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
span: Span,
key: Q::Key,
mode: QueryMode,
) -> Option<Q::Value>
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
debug_assert!(qcx.dep_context().dep_graph().is_fully_enabled());
@ -851,19 +841,17 @@ where
None
};
let (result, dep_node_index) = ensure_sufficient_stack(|| {
try_execute_query::<_, _, true>(query, qcx, span, key, dep_node)
});
let (result, dep_node_index) =
ensure_sufficient_stack(|| try_execute_query::<Q, true>(query, qcx, span, key, dep_node));
if let Some(dep_node_index) = dep_node_index {
qcx.dep_context().dep_graph().read_index(dep_node_index)
}
Some(result)
}
pub fn force_query<Q, Qcx>(query: Q, qcx: Qcx, key: Q::Key, dep_node: DepNode)
pub fn force_query<Q>(query: Q, qcx: Q::Qcx, key: Q::Key, dep_node: DepNode)
where
Q: QueryConfig<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
// We may be concurrently trying both execute and force a query.
// Ensure that only one of them runs the query.
@ -875,6 +863,6 @@ where
debug_assert!(!query.anon());
ensure_sufficient_stack(|| {
try_execute_query::<_, _, true>(query, qcx, DUMMY_SP, key, Some(dep_node))
try_execute_query::<Q, true>(query, qcx, DUMMY_SP, key, Some(dep_node))
});
}

View file

@ -26,7 +26,7 @@ use rustc_middle::metadata::{ModChild, Reexport};
use rustc_middle::ty::{Feed, Visibility};
use rustc_middle::{bug, span_bug};
use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind};
use rustc_span::{Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym};
use rustc_span::{Ident, Span, Symbol, kw, sym};
use thin_vec::ThinVec;
use tracing::debug;
@ -36,9 +36,9 @@ use crate::imports::{ImportData, ImportKind};
use crate::macros::{MacroRulesDecl, MacroRulesScope, MacroRulesScopeRef};
use crate::ref_mut::CmCell;
use crate::{
BindingKey, Decl, DeclData, DeclKind, ExternPreludeEntry, Finalize, MacroData, Module,
ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, ResolutionError, Resolver, Segment,
Used, VisResolutionError, errors,
BindingKey, Decl, DeclData, DeclKind, ExternPreludeEntry, Finalize, IdentKey, MacroData,
Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, ResolutionError, Resolver,
Segment, Used, VisResolutionError, errors,
};
type Res = def::Res<NodeId>;
@ -48,20 +48,23 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
/// and report an error in case of a collision.
pub(crate) fn plant_decl_into_local_module(
&mut self,
ident: Macros20NormalizedIdent,
ident: IdentKey,
orig_ident_span: Span,
ns: Namespace,
decl: Decl<'ra>,
) {
if let Err(old_decl) = self.try_plant_decl_into_local_module(ident, ns, decl, false) {
self.report_conflict(ident.0, ns, old_decl, decl);
if let Err(old_decl) =
self.try_plant_decl_into_local_module(ident, orig_ident_span, ns, decl, false)
{
self.report_conflict(ident, ns, old_decl, decl);
}
}
/// Create a name definitinon from the given components, and put it into the local module.
/// Create a name definition from the given components, and put it into the local module.
fn define_local(
&mut self,
parent: Module<'ra>,
ident: Ident,
orig_ident: Ident,
ns: Namespace,
res: Res,
vis: Visibility,
@ -69,15 +72,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
expn_id: LocalExpnId,
) {
let decl = self.arenas.new_def_decl(res, vis.to_def_id(), span, expn_id, Some(parent));
let ident = Macros20NormalizedIdent::new(ident);
self.plant_decl_into_local_module(ident, ns, decl);
let ident = IdentKey::new(orig_ident);
self.plant_decl_into_local_module(ident, orig_ident.span, ns, decl);
}
/// Create a name definitinon from the given components, and put it into the extern module.
/// Create a name definition from the given components, and put it into the extern module.
fn define_extern(
&self,
parent: Module<'ra>,
ident: Macros20NormalizedIdent,
ident: IdentKey,
orig_ident_span: Span,
ns: Namespace,
child_index: usize,
res: Res,
@ -102,7 +106,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let key =
BindingKey::new_disambiguated(ident, ns, || (child_index + 1).try_into().unwrap()); // 0 indicates no underscore
if self
.resolution_or_default(parent, key)
.resolution_or_default(parent, key, orig_ident_span)
.borrow_mut_unchecked()
.non_glob_decl
.replace(decl)
@ -279,8 +283,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
.unwrap_or_else(|| res.def_id()),
)
};
let ModChild { ident, res, vis, ref reexport_chain } = *child;
let ident = Macros20NormalizedIdent::new(ident);
let ModChild { ident: orig_ident, res, vis, ref reexport_chain } = *child;
let ident = IdentKey::new(orig_ident);
let span = child_span(self, reexport_chain, res);
let res = res.expect_non_local();
let expansion = parent_scope.expansion;
@ -293,7 +297,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// Record primary definitions.
let define_extern = |ns| {
self.define_extern(parent, ident, ns, child_index, res, vis, span, expansion, ambig)
self.define_extern(
parent,
ident,
orig_ident.span,
ns,
child_index,
res,
vis,
span,
expansion,
ambig,
)
};
match res {
Res::Def(
@ -533,8 +548,8 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
if target.name != kw::Underscore {
self.r.per_ns(|this, ns| {
if !type_ns_only || ns == TypeNS {
let key = BindingKey::new(Macros20NormalizedIdent::new(target), ns);
this.resolution_or_default(current_module, key)
let key = BindingKey::new(IdentKey::new(target), ns);
this.resolution_or_default(current_module, key, target.span)
.borrow_mut(this)
.single_imports
.insert(import);
@ -974,7 +989,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
&mut self,
orig_name: Option<Symbol>,
item: &Item,
ident: Ident,
orig_ident: Ident,
local_def_id: LocalDefId,
vis: Visibility,
) {
@ -983,7 +998,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let parent = parent_scope.module;
let expansion = parent_scope.expansion;
let (used, module, decl) = if orig_name.is_none() && ident.name == kw::SelfLower {
let (used, module, decl) = if orig_name.is_none() && orig_ident.name == kw::SelfLower {
self.r.dcx().emit_err(errors::ExternCrateSelfRequiresRenaming { span: sp });
return;
} else if orig_name == Some(kw::SelfLower) {
@ -1008,7 +1023,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
})
.unwrap_or((true, None, self.r.dummy_decl));
let import = self.r.arenas.alloc_import(ImportData {
kind: ImportKind::ExternCrate { source: orig_name, target: ident, id: item.id },
kind: ImportKind::ExternCrate { source: orig_name, target: orig_ident, id: item.id },
root_id: item.id,
parent_scope,
imported_module: CmCell::new(module),
@ -1026,7 +1041,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
}
self.r.potentially_unused_imports.push(import);
let import_decl = self.r.new_import_decl(decl, import);
let ident = Macros20NormalizedIdent::new(ident);
let ident = IdentKey::new(orig_ident);
if ident.name != kw::Underscore && parent == self.r.graph_root {
// FIXME: this error is technically unnecessary now when extern prelude is split into
// two scopes, remove it with lang team approval.
@ -1045,20 +1060,20 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
Entry::Occupied(mut occupied) => {
let entry = occupied.get_mut();
if entry.item_decl.is_some() {
let msg = format!("extern crate `{ident}` already in extern prelude");
let msg = format!("extern crate `{orig_ident}` already in extern prelude");
self.r.tcx.dcx().span_delayed_bug(item.span, msg);
} else {
entry.item_decl = Some((import_decl, orig_name.is_some()));
entry.item_decl = Some((import_decl, orig_ident.span, orig_name.is_some()));
}
entry
}
Entry::Vacant(vacant) => vacant.insert(ExternPreludeEntry {
item_decl: Some((import_decl, true)),
item_decl: Some((import_decl, orig_ident.span, true)),
flag_decl: None,
}),
};
}
self.r.plant_decl_into_local_module(ident, TypeNS, import_decl);
self.r.plant_decl_into_local_module(ident, orig_ident.span, TypeNS, import_decl);
}
/// Constructs the reduced graph for one foreign item.
@ -1159,7 +1174,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
if let Some(span) = import_all {
let import = macro_use_import(self, span, false);
self.r.potentially_unused_imports.push(import);
module.for_each_child_mut(self, |this, ident, ns, binding| {
module.for_each_child_mut(self, |this, ident, _, ns, binding| {
if ns == MacroNS {
let import =
if this.r.is_accessible_from(binding.vis(), this.parent_scope.module) {
@ -1270,7 +1285,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
let expansion = parent_scope.expansion;
let feed = self.r.feed(item.id);
let def_id = feed.key();
let (res, ident, span, macro_rules) = match &item.kind {
let (res, orig_ident, span, macro_rules) = match &item.kind {
ItemKind::MacroDef(ident, def) => {
(self.res(def_id), *ident, item.span, def.macro_rules)
}
@ -1293,8 +1308,8 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
self.r.local_macro_def_scopes.insert(def_id, parent_scope.module);
if macro_rules {
let ident = Macros20NormalizedIdent::new(ident);
self.r.macro_names.insert(ident.0);
let ident = IdentKey::new(orig_ident);
self.r.macro_names.insert(ident);
let is_macro_export = ast::attr::contains_name(&item.attrs, sym::macro_export);
let vis = if is_macro_export {
Visibility::Public
@ -1326,10 +1341,10 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
});
self.r.import_use_map.insert(import, Used::Other);
let import_decl = self.r.new_import_decl(decl, import);
self.r.plant_decl_into_local_module(ident, MacroNS, import_decl);
self.r.plant_decl_into_local_module(ident, orig_ident.span, MacroNS, import_decl);
} else {
self.r.check_reserved_macro_name(ident.0, res);
self.insert_unused_macro(ident.0, def_id, item.id);
self.r.check_reserved_macro_name(ident.name, orig_ident.span, res);
self.insert_unused_macro(orig_ident, def_id, item.id);
}
self.r.feed_visibility(feed, vis);
let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Def(
@ -1337,6 +1352,7 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
parent_macro_rules_scope: parent_scope.macro_rules,
decl,
ident,
orig_ident_span: orig_ident.span,
}),
));
self.r.macro_rules_scopes.insert(def_id, scope);
@ -1352,9 +1368,9 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
_ => self.resolve_visibility(&item.vis),
};
if !vis.is_public() {
self.insert_unused_macro(ident, def_id, item.id);
self.insert_unused_macro(orig_ident, def_id, item.id);
}
self.r.define_local(module, ident, MacroNS, res, vis, span, expansion);
self.r.define_local(module, orig_ident, MacroNS, res, vis, span, expansion);
self.r.feed_visibility(feed, vis);
self.parent_scope.macro_rules
}
@ -1496,7 +1512,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
{
// Don't add underscore names, they cannot be looked up anyway.
let impl_def_id = self.r.tcx.local_parent(local_def_id);
let key = BindingKey::new(Macros20NormalizedIdent::new(ident), ns);
let key = BindingKey::new(IdentKey::new(ident), ns);
self.r.impl_binding_keys.entry(impl_def_id).or_default().insert(key);
}

View file

@ -33,10 +33,10 @@ use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::{
MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES, UNUSED_IMPORTS, UNUSED_QUALIFICATIONS,
};
use rustc_span::{DUMMY_SP, Ident, Macros20NormalizedIdent, Span, kw};
use rustc_span::{DUMMY_SP, Ident, Span, kw};
use crate::imports::{Import, ImportKind};
use crate::{DeclKind, LateDecl, Resolver, module_to_string};
use crate::{DeclKind, IdentKey, LateDecl, Resolver, module_to_string};
struct UnusedImport {
use_tree: ast::UseTree,
@ -203,7 +203,7 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> {
if self
.r
.extern_prelude
.get(&Macros20NormalizedIdent::new(extern_crate.ident))
.get(&IdentKey::new(extern_crate.ident))
.is_none_or(|entry| entry.introduced_by_item())
{
continue;

View file

@ -32,9 +32,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::edition::Edition;
use rustc_span::hygiene::MacroKind;
use rustc_span::source_map::{SourceMap, Spanned};
use rustc_span::{
BytePos, DUMMY_SP, Ident, Macros20NormalizedIdent, Span, Symbol, SyntaxContext, kw, sym,
};
use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, SyntaxContext, kw, sym};
use thin_vec::{ThinVec, thin_vec};
use tracing::{debug, instrument};
@ -47,10 +45,10 @@ use crate::imports::{Import, ImportKind};
use crate::late::{DiagMetadata, PatternSource, Rib};
use crate::{
AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingError, BindingKey, Decl, DeclKind,
Finalize, ForwardGenericParamBanReason, HasGenericParams, LateDecl, MacroRulesScope, Module,
ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PrivacyError, ResolutionError,
Resolver, Scope, ScopeSet, Segment, UseError, Used, VisResolutionError, errors as errs,
path_names_to_string,
Finalize, ForwardGenericParamBanReason, HasGenericParams, IdentKey, LateDecl, MacroRulesScope,
Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PrivacyError,
ResolutionError, Resolver, Scope, ScopeSet, Segment, UseError, Used, VisResolutionError,
errors as errs, path_names_to_string,
};
type Res = def::Res<ast::NodeId>;
@ -81,24 +79,14 @@ pub(crate) struct TypoSuggestion {
}
impl TypoSuggestion {
pub(crate) fn typo_from_ident(ident: Ident, res: Res) -> TypoSuggestion {
Self {
candidate: ident.name,
span: Some(ident.span),
res,
target: SuggestionTarget::SimilarlyNamed,
}
pub(crate) fn new(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion {
Self { candidate, span: Some(span), res, target: SuggestionTarget::SimilarlyNamed }
}
pub(crate) fn typo_from_name(candidate: Symbol, res: Res) -> TypoSuggestion {
Self { candidate, span: None, res, target: SuggestionTarget::SimilarlyNamed }
}
pub(crate) fn single_item_from_ident(ident: Ident, res: Res) -> TypoSuggestion {
Self {
candidate: ident.name,
span: Some(ident.span),
res,
target: SuggestionTarget::SingleItem,
}
pub(crate) fn single_item(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion {
Self { candidate, span: Some(span), res, target: SuggestionTarget::SingleItem }
}
}
@ -212,7 +200,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
pub(crate) fn report_conflict(
&mut self,
ident: Ident,
ident: IdentKey,
ns: Namespace,
old_binding: Decl<'ra>,
new_binding: Decl<'ra>,
@ -324,10 +312,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// Check if the target of the use for both bindings is the same.
let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id();
let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy();
let from_item = self
.extern_prelude
.get(&Macros20NormalizedIdent::new(ident))
.is_none_or(|entry| entry.introduced_by_item());
let from_item =
self.extern_prelude.get(&ident).is_none_or(|entry| entry.introduced_by_item());
// Only suggest removing an import if both bindings are to the same def, if both spans
// aren't dummy spans. Further, if both bindings are imports, then the ident must have
// been introduced by an item.
@ -531,10 +517,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
filter_fn: &impl Fn(Res) -> bool,
ctxt: Option<SyntaxContext>,
) {
module.for_each_child(self, |_this, ident, _ns, binding| {
module.for_each_child(self, |_this, ident, orig_ident_span, _ns, binding| {
let res = binding.res();
if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == ident.span.ctxt()) {
names.push(TypoSuggestion::typo_from_ident(ident.0, res));
if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == *ident.ctxt) {
names.push(TypoSuggestion::new(ident.name, orig_ident_span, res));
}
});
}
@ -1187,11 +1173,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
if filter_fn(res) {
suggestions.extend(
this.helper_attrs
.get(&expn_id)
.into_iter()
.flatten()
.map(|(ident, _)| TypoSuggestion::typo_from_ident(ident.0, res)),
this.helper_attrs.get(&expn_id).into_iter().flatten().map(
|&(ident, orig_ident_span, _)| {
TypoSuggestion::new(ident.name, orig_ident_span, res)
},
),
);
}
}
@ -1202,8 +1188,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
if let MacroRulesScope::Def(macro_rules_def) = macro_rules_scope.get() {
let res = macro_rules_def.decl.res();
if filter_fn(res) {
suggestions
.push(TypoSuggestion::typo_from_ident(macro_rules_def.ident.0, res))
suggestions.push(TypoSuggestion::new(
macro_rules_def.ident.name,
macro_rules_def.orig_ident_span,
res,
))
}
}
}
@ -1233,9 +1222,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
Scope::ExternPreludeItems => {
// Add idents from both item and flag scopes.
suggestions.extend(this.extern_prelude.keys().filter_map(|ident| {
suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, entry)| {
let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id());
filter_fn(res).then_some(TypoSuggestion::typo_from_ident(ident.0, res))
filter_fn(res).then_some(TypoSuggestion::new(ident.name, entry.span(), res))
}));
}
Scope::ExternPreludeFlags => {}
@ -1244,7 +1233,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
suggestions.extend(
this.registered_tools
.iter()
.map(|ident| TypoSuggestion::typo_from_ident(*ident, res)),
.map(|ident| TypoSuggestion::new(ident.name, ident.span, res)),
);
}
Scope::StdLibPrelude => {
@ -1329,7 +1318,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
{
let in_module_is_extern = !in_module.def_id().is_local();
in_module.for_each_child(self, |this, ident, ns, name_binding| {
in_module.for_each_child(self, |this, ident, orig_ident_span, ns, name_binding| {
// Avoid non-importable candidates.
if name_binding.is_assoc_item()
&& !this.tcx.features().import_trait_associated_functions()
@ -1382,7 +1371,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
if ident.name == lookup_ident.name
&& ns == namespace
&& in_module != parent_scope.module
&& !ident.span.normalize_to_macros_2_0().from_expansion()
&& ident.ctxt.is_root()
&& filter_fn(res)
{
// create the path
@ -1395,7 +1384,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
};
segms.append(&mut path_segments.clone());
segms.push(ast::PathSegment::from_ident(ident.0));
segms.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span)));
let path = Path { span: name_binding.span, segments: segms, tokens: None };
if child_accessible
@ -1468,7 +1457,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
if let Some(def_id) = name_binding.res().module_like_def_id() {
// form the path
let mut path_segments = path_segments.clone();
path_segments.push(ast::PathSegment::from_ident(ident.0));
path_segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span)));
let alias_import = if let DeclKind::Import { import, .. } = name_binding.kind
&& let ImportKind::ExternCrate { source: Some(_), .. } = import.kind
@ -1557,8 +1546,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
);
if lookup_ident.span.at_least_rust_2018() {
for &ident in self.extern_prelude.keys() {
if ident.span.from_expansion() {
for (ident, entry) in &self.extern_prelude {
if entry.span().from_expansion() {
// Idents are adjusted to the root context before being
// resolved in the extern prelude, so reporting this to the
// user is no help. This skips the injected
@ -1582,7 +1571,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
self.resolutions(parent_scope.module).borrow().iter().any(
|(key, name_resolution)| {
if key.ns == TypeNS
&& key.ident == ident
&& key.ident == *ident
&& let Some(decl) = name_resolution.borrow().best_decl()
{
match decl.res() {
@ -1601,7 +1590,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
if needs_disambiguation {
crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP));
}
crate_path.push(ast::PathSegment::from_ident(ident.0));
crate_path.push(ast::PathSegment::from_ident(ident.orig(entry.span())));
suggestions.extend(self.lookup_import_candidates_from_module(
lookup_ident,
@ -1777,7 +1766,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
return;
}
if self.macro_names.contains(&ident.normalize_to_macros_2_0()) {
if self.macro_names.contains(&IdentKey::new(ident)) {
err.subdiagnostic(AddedMacroUse);
return;
}
@ -2007,8 +1996,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
matches!(scope2, Scope::ExternPreludeFlags)
&& self
.extern_prelude
.get(&Macros20NormalizedIdent::new(ident))
.is_some_and(|entry| entry.item_decl.map(|(b, _)| b) == Some(b1))
.get(&IdentKey::new(ident))
.is_some_and(|entry| entry.item_decl.map(|(b, ..)| b) == Some(b1))
};
let (b1, b2, scope1, scope2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() {
// We have to print the span-less alternative first, otherwise formatting looks bad.
@ -2914,7 +2903,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
return None;
}
let binding_key = BindingKey::new(Macros20NormalizedIdent::new(ident), MacroNS);
let binding_key = BindingKey::new(IdentKey::new(ident), MacroNS);
let binding = self.resolution(crate_module, binding_key)?.binding()?;
let Res::Def(DefKind::Macro(kinds), _) = binding.res() else {
return None;

View file

@ -96,13 +96,10 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> {
// is the maximum value among visibilities of declarations corresponding to that def id.
for (decl, eff_vis) in visitor.import_effective_visibilities.iter() {
let DeclKind::Import { import, .. } = decl.kind else { unreachable!() };
if !decl.is_ambiguity_recursive() {
if let Some(node_id) = import.id() {
r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx)
}
} else if decl.ambiguity.get().is_some()
&& eff_vis.is_public_at_level(Level::Reexported)
{
if let Some(node_id) = import.id() {
r.effective_visibilities.update_eff_vis(r.local_def_id(node_id), eff_vis, r.tcx)
}
if decl.ambiguity.get().is_some() && eff_vis.is_public_at_level(Level::Reexported) {
exported_ambiguities.insert(*decl);
}
}
@ -123,31 +120,13 @@ impl<'a, 'ra, 'tcx> EffectiveVisibilitiesVisitor<'a, 'ra, 'tcx> {
// Set the given effective visibility level to `Level::Direct` and
// sets the rest of the `use` chain to `Level::Reexported` until
// we hit the actual exported item.
//
// If the binding is ambiguous, put the root ambiguity binding and all reexports
// leading to it into the table. They are used by the `ambiguous_glob_reexports`
// lint. For all bindings added to the table this way `is_ambiguity` returns true.
let is_ambiguity =
|decl: Decl<'ra>, warn: bool| decl.ambiguity.get().is_some() && !warn;
let mut parent_id = ParentId::Def(module_id);
let mut warn_ambiguity = decl.warn_ambiguity.get();
while let DeclKind::Import { source_decl, .. } = decl.kind {
self.update_import(decl, parent_id);
if is_ambiguity(decl, warn_ambiguity) {
// Stop at the root ambiguity, further bindings in the chain should not
// be reexported because the root ambiguity blocks any access to them.
// (Those further bindings are most likely not ambiguities themselves.)
break;
}
parent_id = ParentId::Import(decl);
decl = source_decl;
warn_ambiguity |= source_decl.warn_ambiguity.get();
}
if !is_ambiguity(decl, warn_ambiguity)
&& let Some(def_id) = decl.res().opt_def_id().and_then(|id| id.as_local())
{
if let Some(def_id) = decl.res().opt_def_id().and_then(|id| id.as_local()) {
self.update_def(def_id, decl.vis().expect_local(), parent_id);
}
}

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